- Mysterious SetDParam calls with magic parameter index.
- "{SKIP}{SKIP}{..." chains. Parameters of two strings can't overlap when used in one window.
- Parameters can't be assigned once for ever to strings.
- Need to call SetDParam for every parameter in OnPaint.
- Tips and "query window" title can't have parameters etc.
- Weak memory management, processor wasting (every visible string is built every paint call).
The point is to make class for string.
Class that allows to assign parameters to strings and manages string memory: reference counting, build string only when there is a need to, map all strings that do not change (i.e. labels like "OK") to have only one buffer for one string.
There will be no need to modify a lot of code to make it work. Every GUI window can stay in its old form until it's upgraded (well it doesn't need to be upgraded at all). There is a few places that will need to be changed: GetString calls, string drawing functions, "query string" framework and Window class little modifications.
If you like it I would make a pre-implementation to show other, numerous and smaller ides that I didn't include here. Further work may be common.
It could be something like this:
Possible usage (when I write "reference" I mean reference in general concept, it may be C++ reference or pointer as well):
- static strings (parameterless), parametrized strings
Code: Select all
static const Widget _some_window_widgets[] = { /* type, ..., data (changed type to WidgetData), tooltips (changed type to String) */ { WWT_CLOSEBOX, ..., String::Static(STR_BLACK_CROSS), String::Static(STR_TOOLTIP_CLOSE_WINDOW)}, { WWT_CAPTION, ..., String::Static(STR_SOME_WINDOW_CAPTION), String::Null}, { WWT_LABEL, ..., STR_NULL, String::Static(STR_TOOLTIP_PLAYER_NAME)}, //WIDGET_NAME, { WWT_TEXT, ..., STR_NULL, String::Static(STR_TOOLTIP_OPPONENTS)}, //WIDGET_OPPONENTS { WWT_TEXT, ..., STR_NULL, String::Static(STR_TOOLTIP_STATS)}, //WIDGET_STATS //... class SomeWindow : public Window { int statA; int statB; char statC[255]; SomeWindow(const char *player_name, byte opponents, ...) : Window(...) { this->widget[WIDGET_NAME].data = ParamStr( STR_PLAYER_NAME, //"Name: {STRING}" ConstStrParam(player_name)); /*const string parameter, * memory pointed by player_name do not changes while string exists */ this->widget[WIDGET_OPPONENTS].data = ParamStr( STR_OPPONENTS_COUNT, //"Opponents: {NUM}" IntParam(opponents); //integer parameter (any integer type) this->widget[WIDGET_STATS].data = ParamStr( STR_PLAYER_STATS, //"A: {COMMA}, B: {NUM}, C: {STRING}" RefParam(&statA), RefParam(&statB), //reference (pointer) to integer StringParam(statC); //var string } virtual OnPaint() { this->DrawWidgets(); //nothig more needed ! } //...
- parametrized strings, unlimited parameters
Code: Select all
static const Widget _some_window_widgets[] = { { WWT_TXTBTN, ..., ParamStr(STR_JUST_INT, IntParam(1)), String::Null}, // button with caption "1" { WWT_TXTBTN, ..., ParamStr(STR_JUST_INT, IntParam(2)), String::Null}, // button with caption "2" { WWT_TXTBTN, ..., ParamStr(STR_JUST_INT, IntParam(3)), String::Null}, // button with caption "3" /* constructor can take up to 3 parameters */ { WWT_TXTBTN, ..., ParamStr(STR_THREE_INTS, IntParam(1), IntParam(2), IntParam(3)), String::Null}, /* if we want more, we can use << operator */ { WWT_TXTBTN, ..., ParamStr(STR_FIVE_INTS) << IntParam(1) << IntParam(2) << IntParam(3) << IntParam(4) << IntParam(5), String::Null}, //...
- positions of parameters, behaviour like values of enums
Code: Select all
class SomeWindow : public Window { SomeWindow(...) : Window(...) { ParamStr str = ParamStr(STR_SOME_TEXT); //"{NUM} {SKIP}{SKIP}{SKIP}{SKIP} {NUM}{NUM}{NUM} {SKIP}{SKIP}{SKIP} {NUM}{NUM}" /* when parameter is the first and his position is ommited, he gest position 0 */ str << IntParam(n1); /* we can explicitly specify parameter position - position 4 */ str << IntParam(n2, 4); /* when position is ommited, parameter gets position behind previous parameter (in this case 5) */ str << IntParam(n2); /* next position - 6 */ str << IntParam(n3); /* position 10 */ str << IntParam(n4, 10); /* position 11 */ str << IntParam(n5); this->widgets[SOME_WIDGET].data = str; //...
- change parameters at runtime
Code: Select all
class SomeWindow : public Window { ParamStr strA; //we must remember string whose parameters we want change ParamStr strB; int varA1, varA2; int varB1, varB2; SomeWindow(...) : Window(...) { /* 'widget[WIDGET_TEXT_A].data' and 'strA' are not separated copies of string. * Internally they are pointing to the same string and same set of parameters */ this->widget[WIDGET_TEXT_A].data = strA = ParamStr(STR_TEXT_A, IntParam(1), RefParam(&varA1)); this->widget[WIDGET_TEXT_B].data = strB = ParamStr(STR_TEXT_B, ConstStrParam("one"), RefParam(&varB1)); } void SomeMethod() { strA[0] = IntParam(2); // 1 changes to 2 strA[1] = RefParam(&varA2); // varA1 reference (pointer) changes to varA2 reference strB[0] = ConstStrParam("two"); // "one" changes to "two" strB[1] = RefParam(&varB2); // varB1 reference (pointer) changes to varB2 reference } //...
- Giving parameters to title of ShowQuery/ShowQueryString window was imposible
Code: Select all
// STR_TYPE_NEW_NAME_FOR_COMPANY - "Type new name for {STRING} company" ShowQueryString(//now parameters has a type of String, not StringID ParamString(STR_JUST_RAW_STRING, ConstStrParam(old_company_name)), ParamString(STR_TYPE_NEW_NAME_FOR_COMPANY, ConstStrParam(old_company_name)), ... );
- backward compatibility, parameters overlapping
Code: Select all
/* We can use strings in traditional way. * Parameters of traditional strings can't overlap. When one string uses parameter number 1 * (i.e. "blah blah {SKIP}{NUM} blah blah") no other string can use it. It produces long * "{SKIP}{SKIP}{SK...." chaines. * Parameters of traditional strings still can't overlap with parameters of new ParamString strings * (well not always, if we are sure that traditional strings will be evaluated before ParamString * strings their parameters may overlap), but if we use only ParamString strings their parameters * may overlap freely ! */ static const Widget _some_window_widgets[] = { /* type, ..., data, tooltips */ { WWT_MATRINX, ..., 0x501, String::Null}, // still can use 'data' field as int16 { WWT_TEXT, ..., STR_SOME_TEXT, String::Null}, // in this case String class is not used for 'data' field { WWT_TEXT, ..., STR_SOME_OTHER_TEXT, STR_NULL}, // using String::Null and STR_NULL (casted to String) is the same //.. class SomeWindow : public Window { virtual void OnPaint() { this->widget[SOME_WIDGET].data = STR_SOME_TEXT; // still can use traditional mechanics SetDParam(1, some_var1); // parameters must be set like in traditional system SetDParam(2, some_var2); SetDParam(3, some_var3); this->DrawWidgets(); } void SomeMethod() { /* traditional form may be used */ SetDParam(0, value); ShowQueryString(STR_JUST_INT, ...); //STR_JUST_INT is implicitly casted to String } //...