Skip to content

Commit d1d6a97

Browse files
Zeframsapier
Zefram
authored and
sapier
committedNov 30, 2014
Scale form elements consistently
The ratios between the sizes of form elements, including text, is now fixed, aside from variations caused by rounding. This makes form layout almost fully predictable, and particularly independent of player's screen size. The proportions of non-text elements are the traditional proportions. For compatibility, the way in which element positions and sizes are specified remains unchanged, in all its baroqueness, with one exception. The exception is that the position of a label[] element is now defined in terms of the vertically center of the first line of the label, rather than the bottom of the first line of the label. This change allows a label to be precisely aligned with button text or an edit box, which are positioned in a centering manner. Label positioning remains consistent with the previous system, just more precisely defined. Make multi-line label[] elements work properly. Previously the code set a bounding rectangle assuming that there would be only a single line, and as a result a multi-line label would be cut somewhere in the middle of the second line. Now multi-line labels not only work, but have guaranteed line spacing relative to inventory slots, to aid alignment. Incidentally fix tabheader[] elements which were being constrained to the wrong width. Given an unusually large form, in variable-size mode, the form rendering system now chooses a scale that will fit the entire form on the screen, if that doesn't make elements too small. Fixed-size forms, including the main menu, are have their sizes fixed in inch terms. The fixed size for fixed-size forms and the preferred and minimum sizes for variable-size forms all scale according to the gui_scaling parameter.
1 parent dceb9f7 commit d1d6a97

File tree

3 files changed

+287
-209
lines changed

3 files changed

+287
-209
lines changed
 

‎src/constants.h

-3
Original file line numberDiff line numberDiff line change
@@ -100,9 +100,6 @@ with this program; if not, write to the Free Software Foundation, Inc.,
100100
#define LEGACY_SCALING (2./3.)
101101
#define TTF_DEFAULT_FONT_SIZE (13.0 / LEGACY_SCALING)
102102
#define DEFAULT_FONT_SIZE (14)
103-
#define DEFAULT_IMGSIZE (48.0)
104-
#define DEFAULT_XSPACING ((15.0 + (1.0 / 3.0)))
105-
#define DEFAULT_YSPACING (9.0)
106103

107104
#endif
108105

‎src/guiFormSpecMenu.cpp

+283-205
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
5151
#include "settings.h"
5252
#include "client.h"
5353
#include "util/string.h" // for parseColorString()
54+
#include "fontengine.h"
5455

5556
#define MY_CHECKPOS(a,b) \
5657
if (v_pos.size() != 2) { \
@@ -68,6 +69,44 @@ with this program; if not, write to the Free Software Foundation, Inc.,
6869
/*
6970
GUIFormSpecMenu
7071
*/
72+
static unsigned int font_line_height(gui::IGUIFont *font)
73+
{
74+
return font->getDimension(L"Ay").Height + font->getKerningHeight();
75+
}
76+
77+
static gui::IGUIFont *select_font_by_line_height(double target_line_height)
78+
{
79+
// We don't get to directly select a font according to its
80+
// baseline-to-baseline height. Rather, we select by em size.
81+
// The ratio between these varies between fonts. The font
82+
// engine also takes its size parameter not specified in pixels,
83+
// as we want, but scaled by display density and gui_scaling,
84+
// so invert that scaling here. Use a binary search among
85+
// requested sizes to find the right font. Our starting bounds
86+
// are an em height of 1 (being careful not to request size 0,
87+
// which crashes the freetype system) and an em height of the
88+
// target baseline-to-baseline height.
89+
unsigned int loreq = ceil(1 / porting::getDisplayDensity()
90+
/ g_settings->getFloat("gui_scaling"));
91+
unsigned int hireq = ceil(target_line_height
92+
/ porting::getDisplayDensity()
93+
/ g_settings->getFloat("gui_scaling"));
94+
unsigned int lohgt = font_line_height(glb_fontengine->getFont(loreq));
95+
unsigned int hihgt = font_line_height(glb_fontengine->getFont(hireq));
96+
while(hireq - loreq > 1 && lohgt != hihgt) {
97+
unsigned int nureq = (loreq + hireq) >> 1;
98+
unsigned int nuhgt = font_line_height(glb_fontengine->getFont(nureq));
99+
if(nuhgt < target_line_height) {
100+
loreq = nureq;
101+
lohgt = nuhgt;
102+
} else {
103+
hireq = nureq;
104+
hihgt = nuhgt;
105+
}
106+
}
107+
return glb_fontengine->getFont(target_line_height - lohgt < hihgt - target_line_height ? loreq : hireq);
108+
}
109+
71110
GUIFormSpecMenu::GUIFormSpecMenu(irr::IrrlichtDevice* dev,
72111
gui::IGUIElement* parent, s32 id, IMenuManager *menumgr,
73112
InventoryManager *invmgr, IGameDef *gamedef,
@@ -89,7 +128,8 @@ GUIFormSpecMenu::GUIFormSpecMenu(irr::IrrlichtDevice* dev,
89128
m_lock(false),
90129
m_form_src(fsrc),
91130
m_text_dst(tdst),
92-
m_formspec_version(0)
131+
m_formspec_version(0),
132+
m_font(NULL)
93133
#ifdef __ANDROID__
94134
,m_JavaDialogFieldName(L"")
95135
#endif
@@ -266,13 +306,11 @@ void GUIFormSpecMenu::parseSize(parserData* data,std::string element)
266306
if (((parts.size() == 2) || parts.size() == 3) ||
267307
((parts.size() > 3) && (m_formspec_version > FORMSPEC_API_VERSION)))
268308
{
269-
v2f invsize;
270-
271309
if (parts[1].find(';') != std::string::npos)
272310
parts[1] = parts[1].substr(0,parts[1].find(';'));
273311

274-
invsize.X = stof(parts[0]);
275-
invsize.Y = stof(parts[1]);
312+
data->invsize.X = MYMAX(0, stof(parts[0]));
313+
data->invsize.Y = MYMAX(0, stof(parts[1]));
276314

277315
lockSize(false);
278316
if (parts.size() == 3) {
@@ -281,70 +319,7 @@ void GUIFormSpecMenu::parseSize(parserData* data,std::string element)
281319
}
282320
}
283321

284-
double cur_scaling = porting::getDisplayDensity() *
285-
g_settings->getFloat("gui_scaling");
286-
287-
if (m_lock) {
288-
v2u32 current_screensize = m_device->getVideoDriver()->getScreenSize();
289-
v2u32 delta = current_screensize - m_lockscreensize;
290-
291-
if (current_screensize.Y > m_lockscreensize.Y)
292-
delta.Y /= 2;
293-
else
294-
delta.Y = 0;
295-
296-
if (current_screensize.X > m_lockscreensize.X)
297-
delta.X /= 2;
298-
else
299-
delta.X = 0;
300-
301-
offset = v2s32(delta.X,delta.Y);
302-
303-
data->screensize = m_lockscreensize;
304-
305-
// fixed scaling for fixed size gui elements */
306-
cur_scaling = LEGACY_SCALING;
307-
}
308-
else {
309-
offset = v2s32(0,0);
310-
}
311-
312-
/* adjust image size to dpi */
313-
int y_partition = 15;
314-
imgsize = v2s32(data->screensize.Y/y_partition, data->screensize.Y/y_partition);
315-
int min_imgsize = DEFAULT_IMGSIZE * cur_scaling;
316-
while ((min_imgsize > imgsize.Y) && (y_partition > 1)) {
317-
y_partition--;
318-
imgsize = v2s32(data->screensize.Y/y_partition, data->screensize.Y/y_partition);
319-
}
320-
assert(y_partition > 0);
321-
322-
/* adjust spacing to dpi */
323-
spacing = v2s32(imgsize.X+(DEFAULT_XSPACING * cur_scaling),
324-
imgsize.Y+(DEFAULT_YSPACING * cur_scaling));
325-
326-
padding = v2s32(data->screensize.Y/imgsize.Y, data->screensize.Y/imgsize.Y);
327-
328-
/* adjust padding to dpi */
329-
padding = v2s32(
330-
(padding.X/(2.0/3.0)) * cur_scaling,
331-
(padding.X/(2.0/3.0)) * cur_scaling
332-
);
333-
data->size = v2s32(
334-
padding.X*2+spacing.X*(invsize.X-1.0)+imgsize.X,
335-
padding.Y*2+spacing.Y*(invsize.Y-1.0)+imgsize.Y + m_btn_height - 5
336-
);
337-
data->rect = core::rect<s32>(
338-
data->screensize.X/2 - data->size.X/2 + offset.X,
339-
data->screensize.Y/2 - data->size.Y/2 + offset.Y,
340-
data->screensize.X/2 + data->size.X/2 + offset.X,
341-
data->screensize.Y/2 + data->size.Y/2 + offset.Y
342-
);
343-
344-
DesiredRect = data->rect;
345-
recalculateAbsolutePosition(false);
346-
data->basepos = getBasePos();
347-
data->bp_set = 2;
322+
data->explicit_size = true;
348323
return;
349324
}
350325
errorstream<< "Invalid size element (" << parts.size() << "): '" << element << "'" << std::endl;
@@ -397,7 +372,7 @@ void GUIFormSpecMenu::parseList(parserData* data,std::string element)
397372
return;
398373
}
399374

400-
if(data->bp_set != 2)
375+
if(!data->explicit_size)
401376
errorstream<<"WARNING: invalid use of list without a size[] element"<<std::endl;
402377
m_inventorylists.push_back(ListDrawSpec(loc, listname, pos, geom, start_i));
403378
return;
@@ -433,14 +408,9 @@ void GUIFormSpecMenu::parseCheckbox(parserData* data,std::string element)
433408

434409
std::wstring wlabel = narrow_to_wide(label.c_str());
435410

436-
gui::IGUIFont *font = NULL;
437-
gui::IGUISkin* skin = Environment->getSkin();
438-
if (skin)
439-
font = skin->getFont();
440-
441411
core::rect<s32> rect = core::rect<s32>(
442412
pos.X, pos.Y + ((imgsize.Y/2) - m_btn_height),
443-
pos.X + font->getDimension(wlabel.c_str()).Width + 25, // text size + size of checkbox
413+
pos.X + m_font->getDimension(wlabel.c_str()).Width + 25, // text size + size of checkbox
444414
pos.Y + ((imgsize.Y/2) + m_btn_height));
445415

446416
FieldSpec spec(
@@ -518,35 +488,6 @@ void GUIFormSpecMenu::parseScrollBar(parserData* data, std::string element)
518488
e->setSmallStep(10);
519489
e->setLargeStep(100);
520490

521-
if (!m_lock) {
522-
core::rect<s32> relative_rect = e->getRelativePosition();
523-
524-
if (!is_horizontal) {
525-
s32 original_width = relative_rect.getWidth();
526-
s32 width = (original_width/(2.0/3.0))
527-
* porting::getDisplayDensity()
528-
* g_settings->getFloat("gui_scaling");
529-
e->setRelativePosition(core::rect<s32>(
530-
relative_rect.UpperLeftCorner.X,
531-
relative_rect.UpperLeftCorner.Y,
532-
relative_rect.LowerRightCorner.X + (width - original_width),
533-
relative_rect.LowerRightCorner.Y
534-
));
535-
}
536-
else {
537-
s32 original_height = relative_rect.getHeight();
538-
s32 height = (original_height/(2.0/3.0))
539-
* porting::getDisplayDensity()
540-
* g_settings->getFloat("gui_scaling");
541-
e->setRelativePosition(core::rect<s32>(
542-
relative_rect.UpperLeftCorner.X,
543-
relative_rect.UpperLeftCorner.Y,
544-
relative_rect.LowerRightCorner.X,
545-
relative_rect.LowerRightCorner.Y + (height - original_height)
546-
));
547-
}
548-
}
549-
550491
m_scrollbars.push_back(std::pair<FieldSpec,gui::IGUIScrollBar*>(spec,e));
551492
m_fields.push_back(spec);
552493
return;
@@ -576,7 +517,7 @@ void GUIFormSpecMenu::parseImage(parserData* data,std::string element)
576517
geom.X = stof(v_geom[0]) * (float)imgsize.X;
577518
geom.Y = stof(v_geom[1]) * (float)imgsize.Y;
578519

579-
if(data->bp_set != 2)
520+
if(!data->explicit_size)
580521
errorstream<<"WARNING: invalid use of image without a size[] element"<<std::endl;
581522
m_images.push_back(ImageDrawSpec(name, pos, geom));
582523
return;
@@ -592,7 +533,7 @@ void GUIFormSpecMenu::parseImage(parserData* data,std::string element)
592533
pos.X += stof(v_pos[0]) * (float) spacing.X;
593534
pos.Y += stof(v_pos[1]) * (float) spacing.Y;
594535

595-
if(data->bp_set != 2)
536+
if(!data->explicit_size)
596537
errorstream<<"WARNING: invalid use of image without a size[] element"<<std::endl;
597538
m_images.push_back(ImageDrawSpec(name, pos));
598539
return;
@@ -622,7 +563,7 @@ void GUIFormSpecMenu::parseItemImage(parserData* data,std::string element)
622563
geom.X = stof(v_geom[0]) * (float)imgsize.X;
623564
geom.Y = stof(v_geom[1]) * (float)imgsize.Y;
624565

625-
if(data->bp_set != 2)
566+
if(!data->explicit_size)
626567
errorstream<<"WARNING: invalid use of item_image without a size[] element"<<std::endl;
627568
m_itemimages.push_back(ImageDrawSpec(name, pos, geom));
628569
return;
@@ -658,7 +599,7 @@ void GUIFormSpecMenu::parseButton(parserData* data,std::string element,
658599
core::rect<s32>(pos.X, pos.Y - m_btn_height,
659600
pos.X + geom.X, pos.Y + m_btn_height);
660601

661-
if(data->bp_set != 2)
602+
if(!data->explicit_size)
662603
errorstream<<"WARNING: invalid use of button without a size[] element"<<std::endl;
663604

664605
label = unescape_string(label);
@@ -717,7 +658,7 @@ void GUIFormSpecMenu::parseBackground(parserData* data,std::string element)
717658
}
718659
}
719660

720-
if(data->bp_set != 2)
661+
if(!data->explicit_size)
721662
errorstream<<"WARNING: invalid use of background without a size[] element"<<std::endl;
722663
m_backgrounds.push_back(ImageDrawSpec(name, pos, geom));
723664
return;
@@ -1008,8 +949,9 @@ void GUIFormSpecMenu::parsePwdField(parserData* data,std::string element)
1008949

1009950
if (label.length() >= 1)
1010951
{
1011-
rect.UpperLeftCorner.Y -= m_btn_height;
1012-
rect.LowerRightCorner.Y = rect.UpperLeftCorner.Y + m_btn_height;
952+
int font_height = font_line_height(m_font);
953+
rect.UpperLeftCorner.Y -= font_height;
954+
rect.LowerRightCorner.Y = rect.UpperLeftCorner.Y + font_height;
1013955
Environment->addStaticText(spec.flabel.c_str(), rect, false, true, this, 0);
1014956
}
1015957

@@ -1038,20 +980,7 @@ void GUIFormSpecMenu::parseSimpleField(parserData* data,
1038980

1039981
core::rect<s32> rect;
1040982

1041-
if(!data->bp_set)
1042-
{
1043-
rect = core::rect<s32>(
1044-
data->screensize.X/2 - 580/2,
1045-
data->screensize.Y/2 - 300/2,
1046-
data->screensize.X/2 + 580/2,
1047-
data->screensize.Y/2 + 300/2
1048-
);
1049-
DesiredRect = rect;
1050-
recalculateAbsolutePosition(false);
1051-
data->basepos = getBasePos();
1052-
data->bp_set = 1;
1053-
}
1054-
else if(data->bp_set == 2)
983+
if(data->explicit_size)
1055984
errorstream<<"WARNING: invalid use of unpositioned \"field\" in inventory"<<std::endl;
1056985

1057986
v2s32 pos = padding + AbsoluteRect.UpperLeftCorner;
@@ -1103,8 +1032,9 @@ void GUIFormSpecMenu::parseSimpleField(parserData* data,
11031032

11041033
if (label.length() >= 1)
11051034
{
1106-
rect.UpperLeftCorner.Y -= m_btn_height;
1107-
rect.LowerRightCorner.Y = rect.UpperLeftCorner.Y + m_btn_height;
1035+
int font_height = font_line_height(m_font);
1036+
rect.UpperLeftCorner.Y -= font_height;
1037+
rect.LowerRightCorner.Y = rect.UpperLeftCorner.Y + font_height;
11081038
Environment->addStaticText(spec.flabel.c_str(), rect, false, true, this, 0);
11091039
}
11101040
}
@@ -1147,7 +1077,7 @@ void GUIFormSpecMenu::parseTextArea(parserData* data,
11471077

11481078
core::rect<s32> rect = core::rect<s32>(pos.X, pos.Y, pos.X+geom.X, pos.Y+geom.Y);
11491079

1150-
if(data->bp_set != 2)
1080+
if(!data->explicit_size)
11511081
errorstream<<"WARNING: invalid use of positioned "<<type<<" without a size[] element"<<std::endl;
11521082

11531083
if(m_form_src)
@@ -1199,8 +1129,9 @@ void GUIFormSpecMenu::parseTextArea(parserData* data,
11991129

12001130
if (label.length() >= 1)
12011131
{
1202-
rect.UpperLeftCorner.Y -= m_btn_height;
1203-
rect.LowerRightCorner.Y = rect.UpperLeftCorner.Y + m_btn_height;
1132+
int font_height = font_line_height(m_font);
1133+
rect.UpperLeftCorner.Y -= font_height;
1134+
rect.LowerRightCorner.Y = rect.UpperLeftCorner.Y + font_height;
12041135
Environment->addStaticText(spec.flabel.c_str(), rect, false, true, this, 0);
12051136
}
12061137
}
@@ -1240,33 +1171,46 @@ void GUIFormSpecMenu::parseLabel(parserData* data,std::string element)
12401171

12411172
v2s32 pos = padding;
12421173
pos.X += stof(v_pos[0]) * (float)spacing.X;
1243-
pos.Y += stof(v_pos[1]) * (float)spacing.Y;
1174+
pos.Y += (stof(v_pos[1]) + 7.0/30.0) * (float)spacing.Y;
12441175

1245-
if(data->bp_set != 2)
1176+
if(!data->explicit_size)
12461177
errorstream<<"WARNING: invalid use of label without a size[] element"<<std::endl;
12471178

1248-
text = unescape_string(text);
1249-
1250-
std::wstring wlabel = narrow_to_wide(text.c_str());
1251-
1252-
gui::IGUIFont *font = NULL;
1253-
gui::IGUISkin* skin = Environment->getSkin();
1254-
if (skin)
1255-
font = skin->getFont();
1179+
int font_height = font_line_height(m_font);
12561180

1257-
core::rect<s32> rect = core::rect<s32>(
1258-
pos.X, pos.Y+((imgsize.Y/2) - m_btn_height),
1259-
pos.X + font->getDimension(wlabel.c_str()).Width,
1260-
pos.Y+((imgsize.Y/2) + m_btn_height));
1181+
text = unescape_string(text);
1182+
std::vector<std::string> lines = split(text, '\n');
1183+
1184+
for (unsigned int i = 0; i != lines.size(); i++) {
1185+
// Lines are spaced at the nominal distance of
1186+
// 2/5 inventory slot, even if the font doesn't
1187+
// quite match that. This provides consistent
1188+
// form layout, at the expense of sometimes
1189+
// having sub-optimal spacing for the font.
1190+
// We multiply by 2 and then divide by 5, rather
1191+
// than multiply by 0.4, to get exact results
1192+
// in the integer cases: 0.4 is not exactly
1193+
// representable in binary floating point.
1194+
s32 posy = pos.Y + ((float)i) * spacing.Y * 2.0 / 5.0;
1195+
std::wstring wlabel = narrow_to_wide(lines[i].c_str());
1196+
core::rect<s32> rect = core::rect<s32>(
1197+
pos.X, posy - font_height,
1198+
pos.X + m_font->getDimension(wlabel.c_str()).Width,
1199+
posy + font_height);
1200+
FieldSpec spec(
1201+
L"",
1202+
wlabel,
1203+
L"",
1204+
258+m_fields.size()
1205+
);
1206+
gui::IGUIStaticText *e =
1207+
Environment->addStaticText(spec.flabel.c_str(),
1208+
rect, false, false, this, spec.fid);
1209+
e->setTextAlignment(gui::EGUIA_UPPERLEFT,
1210+
gui::EGUIA_CENTER);
1211+
m_fields.push_back(spec);
1212+
}
12611213

1262-
FieldSpec spec(
1263-
L"",
1264-
wlabel,
1265-
L"",
1266-
258+m_fields.size()
1267-
);
1268-
Environment->addStaticText(spec.flabel.c_str(), rect, false, false, this, spec.fid);
1269-
m_fields.push_back(spec);
12701214
return;
12711215
}
12721216
errorstream<< "Invalid label element(" << parts.size() << "): '" << element << "'" << std::endl;
@@ -1288,21 +1232,15 @@ void GUIFormSpecMenu::parseVertLabel(parserData* data,std::string element)
12881232
pos.X += stof(v_pos[0]) * (float)spacing.X;
12891233
pos.Y += stof(v_pos[1]) * (float)spacing.Y;
12901234

1291-
gui::IGUIFont *font = NULL;
1292-
gui::IGUISkin* skin = Environment->getSkin();
1293-
if (skin)
1294-
font = skin->getFont();
1295-
12961235
core::rect<s32> rect = core::rect<s32>(
12971236
pos.X, pos.Y+((imgsize.Y/2)- m_btn_height),
12981237
pos.X+15, pos.Y +
1299-
(font->getKerningHeight() +
1300-
font->getDimension(text.c_str()).Height)
1238+
font_line_height(m_font)
13011239
* (text.length()+1)
13021240
+((imgsize.Y/2)- m_btn_height));
13031241
//actually text.length() would be correct but adding +1 avoids to break all mods
13041242

1305-
if(data->bp_set != 2)
1243+
if(!data->explicit_size)
13061244
errorstream<<"WARNING: invalid use of label without a size[] element"<<std::endl;
13071245

13081246
std::wstring label = L"";
@@ -1368,7 +1306,7 @@ void GUIFormSpecMenu::parseImageButton(parserData* data,std::string element,
13681306

13691307
core::rect<s32> rect = core::rect<s32>(pos.X, pos.Y, pos.X+geom.X, pos.Y+geom.Y);
13701308

1371-
if(data->bp_set != 2)
1309+
if(!data->explicit_size)
13721310
errorstream<<"WARNING: invalid use of image_button without a size[] element"<<std::endl;
13731311

13741312
image_name = unescape_string(image_name);
@@ -1452,7 +1390,7 @@ void GUIFormSpecMenu::parseTabHeader(parserData* data,std::string element)
14521390
pos.X += stof(v_pos[0]) * (float)spacing.X;
14531391
pos.Y += stof(v_pos[1]) * (float)spacing.Y - m_btn_height * 2;
14541392
v2s32 geom;
1455-
geom.X = data->screensize.Y;
1393+
geom.X = DesiredRect.getWidth();
14561394
geom.Y = m_btn_height*2;
14571395

14581396
core::rect<s32> rect = core::rect<s32>(pos.X, pos.Y, pos.X+geom.X,
@@ -1519,7 +1457,7 @@ void GUIFormSpecMenu::parseItemImageButton(parserData* data,std::string element)
15191457

15201458
core::rect<s32> rect = core::rect<s32>(pos.X, pos.Y, pos.X+geom.X, pos.Y+geom.Y);
15211459

1522-
if(data->bp_set != 2)
1460+
if(!data->explicit_size)
15231461
errorstream<<"WARNING: invalid use of item_image_button without a size[] element"<<std::endl;
15241462

15251463
IItemDefManager *idef = m_gamedef->idef();
@@ -1683,6 +1621,30 @@ bool GUIFormSpecMenu::parseVersionDirect(std::string data)
16831621
return false;
16841622
}
16851623

1624+
bool GUIFormSpecMenu::parseSizeDirect(parserData* data, std::string element)
1625+
{
1626+
if (element == "")
1627+
return false;
1628+
1629+
std::vector<std::string> parts = split(element,'[');
1630+
1631+
if (parts.size() < 2)
1632+
return false;
1633+
1634+
std::string type = trim(parts[0]);
1635+
std::string description = trim(parts[1]);
1636+
1637+
if (type != "size" && type != "invsize")
1638+
return false;
1639+
1640+
if (type == "invsize")
1641+
log_deprecated("Deprecated formspec element \"invsize\" is used");
1642+
1643+
parseSize(data, description);
1644+
1645+
return true;
1646+
}
1647+
16861648
void GUIFormSpecMenu::parseElement(parserData* data, std::string element)
16871649
{
16881650
//some prechecks
@@ -1708,17 +1670,6 @@ void GUIFormSpecMenu::parseElement(parserData* data, std::string element)
17081670
std::string type = trim(parts[0]);
17091671
std::string description = trim(parts[1]);
17101672

1711-
if (type == "size") {
1712-
parseSize(data,description);
1713-
return;
1714-
}
1715-
1716-
if (type == "invsize") {
1717-
log_deprecated("Deprecated formspec element \"invsize\" is used");
1718-
parseSize(data,description);
1719-
return;
1720-
}
1721-
17221673
if (type == "list") {
17231674
parseList(data,description);
17241675
return;
@@ -1849,14 +1800,6 @@ void GUIFormSpecMenu::regenerateGui(v2u32 screensize)
18491800
return;
18501801
}
18511802

1852-
gui::IGUIFont *font = NULL;
1853-
gui::IGUISkin* skin = Environment->getSkin();
1854-
if (skin)
1855-
font = skin->getFont();
1856-
1857-
m_btn_height = font->getDimension(L"Some unimportant test String").Height;
1858-
assert(m_btn_height > 0);
1859-
18601803
parserData mydata;
18611804

18621805
//preserve tables
@@ -1895,12 +1838,6 @@ void GUIFormSpecMenu::regenerateGui(v2u32 screensize)
18951838
// Base position of contents of form
18961839
mydata.basepos = getBasePos();
18971840

1898-
// State of basepos, 0 = not set, 1= set by formspec, 2 = set by size[] element
1899-
// Used to adjust form size automatically if needed
1900-
// A proceed button is added if there is no size[] element
1901-
mydata.bp_set = 0;
1902-
1903-
19041841
/* Convert m_init_draw_spec to m_inventorylists */
19051842

19061843
m_inventorylists.clear();
@@ -1954,13 +1891,132 @@ void GUIFormSpecMenu::regenerateGui(v2u32 screensize)
19541891
}
19551892
}
19561893

1894+
/* we need size first in order to calculate image scale */
1895+
mydata.explicit_size = false;
1896+
for (; i< elements.size(); i++) {
1897+
if (!parseSizeDirect(&mydata, elements[i])) {
1898+
break;
1899+
}
1900+
}
1901+
1902+
if (mydata.explicit_size) {
1903+
// compute scaling for specified form size
1904+
if (m_lock) {
1905+
v2u32 current_screensize = m_device->getVideoDriver()->getScreenSize();
1906+
v2u32 delta = current_screensize - m_lockscreensize;
1907+
1908+
if (current_screensize.Y > m_lockscreensize.Y)
1909+
delta.Y /= 2;
1910+
else
1911+
delta.Y = 0;
1912+
1913+
if (current_screensize.X > m_lockscreensize.X)
1914+
delta.X /= 2;
1915+
else
1916+
delta.X = 0;
1917+
1918+
offset = v2s32(delta.X,delta.Y);
1919+
1920+
mydata.screensize = m_lockscreensize;
1921+
} else {
1922+
offset = v2s32(0,0);
1923+
}
1924+
1925+
double gui_scaling = g_settings->getFloat("gui_scaling");
1926+
double screen_dpi = porting::getDisplayDensity() * 96;
1927+
1928+
double use_imgsize;
1929+
if (m_lock) {
1930+
// In fixed-size mode, inventory image size
1931+
// is 0.53 inch multiplied by the gui_scaling
1932+
// config parameter. This magic size is chosen
1933+
// to make the main menu (15.5 inventory images
1934+
// wide, including border) just fit into the
1935+
// default window (800 pixels wide) at 96 DPI
1936+
// and default scaling (1.00).
1937+
use_imgsize = 0.53 * screen_dpi * gui_scaling;
1938+
} else {
1939+
// In variable-size mode, we prefer to make the
1940+
// inventory image size 1/15 of screen height,
1941+
// multiplied by the gui_scaling config parameter.
1942+
// If the preferred size won't fit the whole
1943+
// form on the screen, either horizontally or
1944+
// vertically, then we scale it down to fit.
1945+
// (The magic numbers in the computation of what
1946+
// fits arise from the scaling factors in the
1947+
// following stanza, including the form border,
1948+
// help text space, and 0.1 inventory slot spare.)
1949+
// However, a minimum size is also set, that
1950+
// the image size can't be less than 0.3 inch
1951+
// multiplied by gui_scaling, even if this means
1952+
// the form doesn't fit the screen.
1953+
double prefer_imgsize = mydata.screensize.Y / 15 *
1954+
gui_scaling;
1955+
double fitx_imgsize = mydata.screensize.X /
1956+
((5.0/4.0) * (0.5 + mydata.invsize.X));
1957+
double fity_imgsize = mydata.screensize.Y /
1958+
((15.0/13.0) * (0.85 * mydata.invsize.Y));
1959+
double screen_dpi = porting::getDisplayDensity() * 96;
1960+
double min_imgsize = 0.3 * screen_dpi * gui_scaling;
1961+
use_imgsize = MYMAX(min_imgsize, MYMIN(prefer_imgsize,
1962+
MYMIN(fitx_imgsize, fity_imgsize)));
1963+
}
1964+
1965+
// Everything else is scaled in proportion to the
1966+
// inventory image size. The inventory slot spacing
1967+
// is 5/4 image size horizontally and 15/13 image size
1968+
// vertically. The padding around the form (incorporating
1969+
// the border of the outer inventory slots) is 3/8
1970+
// image size. Font height (baseline to baseline)
1971+
// is 2/5 vertical inventory slot spacing, and button
1972+
// half-height is 7/8 of font height.
1973+
imgsize = v2s32(use_imgsize, use_imgsize);
1974+
spacing = v2s32(use_imgsize*5.0/4, use_imgsize*15.0/13);
1975+
padding = v2s32(use_imgsize*3.0/8, use_imgsize*3.0/8);
1976+
double target_font_height = use_imgsize*15.0/13 * 0.4;
1977+
m_btn_height = use_imgsize*15.0/13 * 0.35;
1978+
1979+
m_font = select_font_by_line_height(target_font_height);
1980+
1981+
mydata.size = v2s32(
1982+
padding.X*2+spacing.X*(mydata.invsize.X-1.0)+imgsize.X,
1983+
padding.Y*2+spacing.Y*(mydata.invsize.Y-1.0)+imgsize.Y + m_btn_height*2.0/3.0
1984+
);
1985+
DesiredRect = mydata.rect = core::rect<s32>(
1986+
mydata.screensize.X/2 - mydata.size.X/2 + offset.X,
1987+
mydata.screensize.Y/2 - mydata.size.Y/2 + offset.Y,
1988+
mydata.screensize.X/2 + mydata.size.X/2 + offset.X,
1989+
mydata.screensize.Y/2 + mydata.size.Y/2 + offset.Y
1990+
);
1991+
} else {
1992+
// Non-size[] form must consist only of text fields and
1993+
// implicit "Proceed" button. Use default font, and
1994+
// temporary form size which will be recalculated below.
1995+
m_font = glb_fontengine->getFont();
1996+
m_btn_height = font_line_height(m_font) * 0.875;
1997+
DesiredRect = core::rect<s32>(
1998+
mydata.screensize.X/2 - 580/2,
1999+
mydata.screensize.Y/2 - 300/2,
2000+
mydata.screensize.X/2 + 580/2,
2001+
mydata.screensize.Y/2 + 300/2
2002+
);
2003+
}
2004+
recalculateAbsolutePosition(false);
2005+
mydata.basepos = getBasePos();
2006+
m_tooltip_element->setOverrideFont(m_font);
2007+
2008+
gui::IGUISkin* skin = Environment->getSkin();
2009+
assert(skin != NULL);
2010+
gui::IGUIFont *old_font = skin->getFont();
2011+
skin->setFont(m_font);
2012+
19572013
for (; i< elements.size(); i++) {
19582014
parseElement(&mydata, elements[i]);
19592015
}
19602016

1961-
// If there's fields, add a Proceed button
1962-
if (m_fields.size() && mydata.bp_set != 2) {
1963-
// if the size wasn't set by an invsize[] or size[] adjust it now to fit all the fields
2017+
// If there are fields without explicit size[], add a "Proceed"
2018+
// button and adjust size to fit all the fields.
2019+
if (m_fields.size() && !mydata.explicit_size) {
19642020
mydata.rect = core::rect<s32>(
19652021
mydata.screensize.X/2 - 580/2,
19662022
mydata.screensize.Y/2 - 300/2,
@@ -1992,6 +2048,8 @@ void GUIFormSpecMenu::regenerateGui(v2u32 screensize)
19922048
|| !isMyChild(focused_element)
19932049
|| focused_element->getType() == gui::EGUIET_TAB_CONTROL)
19942050
setInitialFocus();
2051+
2052+
skin->setFont(old_font);
19952053
}
19962054

19972055
#ifdef __ANDROID__
@@ -2068,11 +2126,6 @@ void GUIFormSpecMenu::drawList(const ListDrawSpec &s, int phase)
20682126
{
20692127
video::IVideoDriver* driver = Environment->getVideoDriver();
20702128

2071-
gui::IGUIFont *font = NULL;
2072-
gui::IGUISkin* skin = Environment->getSkin();
2073-
if (skin)
2074-
font = skin->getFont();
2075-
20762129
Inventory *inv = m_invmgr->getInventory(s.inventoryloc);
20772130
if(!inv){
20782131
infostream<<"GUIFormSpecMenu::drawList(): WARNING: "
@@ -2149,7 +2202,7 @@ void GUIFormSpecMenu::drawList(const ListDrawSpec &s, int phase)
21492202
}
21502203
if(!item.empty())
21512204
{
2152-
drawItemStack(driver, font, item,
2205+
drawItemStack(driver, m_font, item,
21532206
rect, &AbsoluteClippingRect, m_gamedef);
21542207
}
21552208

@@ -2183,12 +2236,6 @@ void GUIFormSpecMenu::drawSelectedItem()
21832236

21842237
video::IVideoDriver* driver = Environment->getVideoDriver();
21852238

2186-
// Get font
2187-
gui::IGUIFont *font = NULL;
2188-
gui::IGUISkin* skin = Environment->getSkin();
2189-
if (skin)
2190-
font = skin->getFont();
2191-
21922239
Inventory *inv = m_invmgr->getInventory(m_selected_item->inventoryloc);
21932240
assert(inv);
21942241
InventoryList *list = inv->getList(m_selected_item->listname);
@@ -2198,7 +2245,7 @@ void GUIFormSpecMenu::drawSelectedItem()
21982245

21992246
core::rect<s32> imgrect(0,0,imgsize.X,imgsize.Y);
22002247
core::rect<s32> rect = imgrect + (m_pointer - imgrect.getCenter());
2201-
drawItemStack(driver, font, stack, rect, NULL, m_gamedef);
2248+
drawItemStack(driver, m_font, stack, rect, NULL, m_gamedef);
22022249
}
22032250

22042251
void GUIFormSpecMenu::drawMenu()
@@ -2211,6 +2258,11 @@ void GUIFormSpecMenu::drawMenu()
22112258
}
22122259
}
22132260

2261+
gui::IGUISkin* skin = Environment->getSkin();
2262+
assert(skin != NULL);
2263+
gui::IGUIFont *old_font = skin->getFont();
2264+
skin->setFont(m_font);
2265+
22142266
updateSelectedItem();
22152267

22162268
video::IVideoDriver* driver = Environment->getVideoDriver();
@@ -2409,6 +2461,8 @@ void GUIFormSpecMenu::drawMenu()
24092461
Draw dragged item stack
24102462
*/
24112463
drawSelectedItem();
2464+
2465+
skin->setFont(old_font);
24122466
}
24132467

24142468
void GUIFormSpecMenu::updateSelectedItem()
@@ -2665,6 +2719,30 @@ static bool isChild(gui::IGUIElement * tocheck, gui::IGUIElement * parent)
26652719

26662720
bool GUIFormSpecMenu::preprocessEvent(const SEvent& event)
26672721
{
2722+
// The IGUITabControl renders visually using the skin's selected
2723+
// font, which we override for the duration of form drawing,
2724+
// but computes tab hotspots based on how it would have rendered
2725+
// using the font that is selected at the time of button release.
2726+
// To make these two consistent, temporarily override the skin's
2727+
// font while the IGUITabControl is processing the event.
2728+
if (event.EventType == EET_MOUSE_INPUT_EVENT &&
2729+
event.MouseInput.Event == EMIE_LMOUSE_LEFT_UP) {
2730+
s32 x = event.MouseInput.X;
2731+
s32 y = event.MouseInput.Y;
2732+
gui::IGUIElement *hovered =
2733+
Environment->getRootGUIElement()->getElementFromPoint(
2734+
core::position2d<s32>(x, y));
2735+
if (hovered->getType() == gui::EGUIET_TAB_CONTROL) {
2736+
gui::IGUISkin* skin = Environment->getSkin();
2737+
assert(skin != NULL);
2738+
gui::IGUIFont *old_font = skin->getFont();
2739+
skin->setFont(m_font);
2740+
bool retval = hovered->OnEvent(event);
2741+
skin->setFont(old_font);
2742+
return retval;
2743+
}
2744+
}
2745+
26682746
// Fix Esc/Return key being eaten by checkboxen and tables
26692747
if(event.EventType==EET_KEY_INPUT_EVENT) {
26702748
KeyPress kp(event.KeyInput);

‎src/guiFormSpecMenu.h

+4-1
Original file line numberDiff line numberDiff line change
@@ -348,10 +348,11 @@ class GUIFormSpecMenu : public GUIModalMenu
348348
unsigned int m_formspec_version;
349349

350350
typedef struct {
351+
bool explicit_size;
352+
v2f invsize;
351353
v2s32 size;
352354
core::rect<s32> rect;
353355
v2s32 basepos;
354-
int bp_set;
355356
v2u32 screensize;
356357
std::wstring focused_fieldname;
357358
GUITable::TableOptions table_options;
@@ -398,6 +399,7 @@ class GUIFormSpecMenu : public GUIModalMenu
398399
void parseListColors(parserData* data,std::string element);
399400
void parseTooltip(parserData* data,std::string element);
400401
bool parseVersionDirect(std::string data);
402+
bool parseSizeDirect(parserData* data, std::string element);
401403
void parseScrollBar(parserData* data, std::string element);
402404

403405
/**
@@ -415,6 +417,7 @@ class GUIFormSpecMenu : public GUIModalMenu
415417
clickpos m_doubleclickdetect[2];
416418

417419
int m_btn_height;
420+
gui::IGUIFont *m_font;
418421

419422
std::wstring getLabelByID(s32 id);
420423
std::wstring getNameByID(s32 id);

0 commit comments

Comments
 (0)
Please sign in to comment.