Merge lp:~widelands-dev/widelands/fix-dropdowns into lp:widelands

Proposed by GunChleoc
Status: Merged
Merged at revision: 9160
Proposed branch: lp:~widelands-dev/widelands/fix-dropdowns
Merge into: lp:widelands
Diff against target: 2577 lines (+782/-513)
44 files modified
data/templates/default/init.lua (+20/-0)
src/editor/CMakeLists.txt (+2/-0)
src/editor/ui_menus/main_menu_new_map.cc (+5/-34)
src/editor/ui_menus/main_menu_new_map.h (+3/-4)
src/editor/ui_menus/main_menu_random_map.cc (+15/-57)
src/editor/ui_menus/main_menu_random_map.h (+2/-2)
src/editor/ui_menus/map_size_box.cc (+76/-0)
src/editor/ui_menus/map_size_box.h (+57/-0)
src/editor/ui_menus/player_menu.cc (+10/-10)
src/editor/ui_menus/tool_resize_options_menu.cc (+21/-58)
src/editor/ui_menus/tool_resize_options_menu.h (+3/-4)
src/graphic/style_manager.cc (+2/-1)
src/graphic/styles/font_style.h (+1/-0)
src/graphic/styles/table_style.h (+6/-2)
src/graphic/text/rt_parse.cc (+4/-2)
src/graphic/text_layout.cc (+7/-0)
src/graphic/text_layout.h (+2/-0)
src/scripting/lua_ui.cc (+135/-32)
src/scripting/lua_ui.h (+38/-1)
src/ui_basic/button.cc (+1/-3)
src/ui_basic/button.h (+0/-3)
src/ui_basic/checkbox.cc (+2/-2)
src/ui_basic/checkbox.h (+2/-2)
src/ui_basic/dropdown.cc (+93/-59)
src/ui_basic/dropdown.h (+42/-22)
src/ui_basic/icongrid.cc (+1/-1)
src/ui_basic/icongrid.h (+1/-1)
src/ui_basic/listselect.cc (+98/-74)
src/ui_basic/listselect.h (+30/-40)
src/ui_basic/panel.cc (+10/-3)
src/ui_basic/panel.h (+3/-2)
src/ui_basic/radiobutton.cc (+1/-1)
src/ui_basic/radiobutton.h (+1/-1)
src/ui_basic/slider.h (+0/-1)
src/ui_basic/unique_window.cc (+7/-1)
src/ui_fsmenu/launch_game.cc (+3/-3)
src/ui_fsmenu/launch_spg.cc (+2/-0)
src/ui_fsmenu/options.cc (+12/-10)
src/wui/economy_options_window.cc (+1/-1)
src/wui/fieldaction.cc (+1/-1)
src/wui/game_client_disconnected.cc (+3/-2)
src/wui/game_message_menu.cc (+17/-25)
src/wui/multiplayersetupgroup.cc (+12/-8)
src/wui/seafaring_statistics_menu.cc (+30/-40)
To merge this branch: bzr merge lp:~widelands-dev/widelands/fix-dropdowns
Reviewer Review Type Date Requested Status
Klaus Halfmann testplay Needs Fixing
Review via email: mp+368223@code.launchpad.net

Commit message

Dropdown fixes and improvements
- Fix positioning of dropdown lists on fullscreen switches
- Define dropdown list height by number of items
- Add dropdowns to Lua interface
- Panel::free_children() now frees all its children
- Create styles for hotkeys and reduce number of render calls in listselect
- Get rid of map width/height code duplication in the editor
- Add support for using dropdowns as toolbar menus with hotkeys
- Fix toggling of minimized UniqueWindows

Description of the change

I pulled out some code from

https://code.launchpad.net/~widelands-dev/widelands/toolbar-dropdown-menus

to make it more reviewable, because that branch has become too big.

The new dropdown functionality in the Lua interface and for toolbar menus is not being used in this branch, but it can be tested with the other branch.

To post a comment you must log in.
Revision history for this message
bunnybot (widelandsofficial) wrote :

Continuous integration builds have changed state:

Travis build 5113. State: failed. Details: https://travis-ci.org/widelands/widelands/builds/540070598.
Appveyor build 4895. State: failed. Details: https://ci.appveyor.com/project/widelands-dev/widelands/build/_widelands_dev_widelands_fix_dropdowns-4895.

Revision history for this message
bunnybot (widelandsofficial) wrote :

Continuous integration builds have changed state:

Travis build 5121. State: errored. Details: https://travis-ci.org/widelands/widelands/builds/540143701.
Appveyor build 4903. State: failed. Details: https://ci.appveyor.com/project/widelands-dev/widelands/build/_widelands_dev_widelands_fix_dropdowns-4903.

Revision history for this message
bunnybot (widelandsofficial) wrote :

Continuous integration builds have changed state:

Travis build 5163. State: errored. Details: https://travis-ci.org/widelands/widelands/builds/542120713.
Appveyor build 4945. State: success. Details: https://ci.appveyor.com/project/widelands-dev/widelands/build/_widelands_dev_widelands_fix_dropdowns-4945.

Revision history for this message
Klaus Halfmann (klaus-halfmann) wrote :

Most travis builds green, two fail as of test_inputqueues.lua

Compiled. To test:
* in MapEditor
** Widht/Height dropdowns (new Map)
** Widht/Height dropdowns (resize Map)
** Number of Players dropdown
* Filter in news overview
* Which examples of minimzed UniqueWindows can you give me?

* How can I find the hotkeys and test them?
* Improved SyntaxErrorImpl - no idea how to test this :-)
  Can I break som RichText in an easy way?

* Sure we want to log every (Lua Button) keypress?

Nice refactoring of boring code :-)

Will no test the Lua part -> next Branch.

Revision history for this message
Klaus Halfmann (klaus-halfmann) wrote :

Found a unrealted, minor Bug: when leaving the "Set Player Positions" Window,
the Map still shows only Big buidling Plots. Not sure if we _must_ fix this.
Will go away with the next map commands.

When trying to open an Online Game I get:
InternetGaming: Connecting to the metaserver.
[NetClient]: Trying to connect to 2a03:4000:32:524:48cb:feff:feae:b0c6:7395 ... failed.
Could not resolve network name: resolve: Host not found (authoritative)

> dig widelands.org AAAA
widelands.org. 2431 IN AAAA 2a03:4000:32:524:48cb:feff:feae:b0c6

> dig widelands.org A
widelands.org. 3312 IN A 85.235.66.69

./widelands --metaserver=2a03:4000:32:524:48cb:feff:feae:b0c6
> OK

./widelands --metaserver=85.235.66.69
> OK

How did this branch break the Internet (IPv6) connectivitiy?
Can anybody confirm.

I will continue testing with the hardcoded IPv6 Adress

Revision history for this message
GunChleoc (gunchleoc) wrote :

> Found a unrealted, minor Bug: when leaving the "Set Player Positions" Window,
> the Map still shows only Big buidling Plots. Not sure if we _must_ fix this.
> Will go away with the next map commands.

That's independent from this branch, so I have created a new bug report: https://bugs.launchpad.net/widelands/+bug/1834001

> When trying to open an Online Game I get:

This is probably related to the server move this weekend. I don't think that this branch broke it, but I'll merge trunk to make sure that the networking code is up to date.

Revision history for this message
GunChleoc (gunchleoc) wrote :

> * How can I find the hotkeys and test them?

Best use https://code.launchpad.net/~widelands-dev/widelands/toolbar-dropdown-menus/+merge/368230 for that where you can see them in action.

> * Improved SyntaxErrorImpl - no idea how to test this :-)
> Can I break som RichText in an easy way?

You could edit one of the basic layouting functions in data/scripting/richtext.lua (for example, add an additional "<p>" to the p() function without closing the tag) and then access the encyclopedia.

> * Sure we want to log every (Lua Button) keypress?

It makes debugging scenarios easier.

> Nice refactoring of boring code :-)

And that bug in UI::Panel is really nasty, it cost me many hours of pulling my hair when I originally implemented the dropdowns.

Revision history for this message
Klaus Halfmann (klaus-halfmann) wrote :

Played a bit and tested the MapEditor. So far all fine.

The new WorkAreaIndicator (when selecting a building) is a pain, at least on my OSX-Computer.
It is so slow, that selecting the correct building becomes quit difficult.
Do we have an optin to disable this? Do we have a bug for this?

This branch per se is fine, except for the Networking Issue.

Will wait for other opinions for now.

Revision history for this message
GunChleoc (gunchleoc) wrote :

Can you test the networking issue with trunk? I don't think that it's related to this branch.

We also just merged an improvement for the work areas, so they will hopefully be a bit faster now.

Revision history for this message
bunnybot (widelandsofficial) wrote :

Continuous integration builds have changed state:

Travis build 5233. State: errored. Details: https://travis-ci.org/widelands/widelands/builds/549672031.
Appveyor build 5012. State: failed. Details: https://ci.appveyor.com/project/widelands-dev/widelands/build/_widelands_dev_widelands_fix_dropdowns-5012.

Revision history for this message
bunnybot (widelandsofficial) wrote :

Continuous integration builds have changed state:

Travis build 5237. State: passed. Details: https://travis-ci.org/widelands/widelands/builds/549847068.
Appveyor build 5016. State: success. Details: https://ci.appveyor.com/project/widelands-dev/widelands/build/_widelands_dev_widelands_fix_dropdowns-5016.

Revision history for this message
Klaus Halfmann (klaus-halfmann) wrote :

Compiling again, got:

src/wui/constructionsitewindow.cc:33:19: warning: unused variable 'pic_max_fill_indicator'
static const char pic_max_fill_indicator[] = "images/wui/buildings/max_fill_indicator.png";
                  ^
src/wui/constructionsitewindow.cc:34:19: warning: unused variable 'pic_priority_low'
static const char pic_priority_low[] = "images/wui/buildings/low_priority_button.png";
                  ^
src/wui/constructionsitewindow.cc:35:19: warning: unused variable 'pic_priority_normal'
static const char pic_priority_normal[] = "images/wui/buildings/normal_priority_button.png";
                  ^
src/wui/constructionsitewindow.cc:36:19: warning: unused variable 'pic_priority_high'
static const char pic_priority_high[] = "images/wui/buildings/high_priority_button.png";

Not sure where got that from?

After merging with trunk I dont see the network Issue any longer, well.

Did we just break savegame ccompatibility (yes with the elk_moose)
OK, will start a new game for testing

Revision history for this message
Klaus Halfmann (klaus-halfmann) wrote :

No I got a crash :( kaputtnik is right, we must play some network games on trunk, RSN.

FATAL ERROR - game crashed. Attempting emergency save.
...
InternetGaming: logout(SERVER_CRASHED)
[NetClient] Closing network socket connected to 2a03:4000:32:524:48cb:feff:feae:b0c6:7395.
Warning: Economy still has requests left on destruction
Warning: Economy still has flags left on destruction
Warning: Economy still has warehouses left on destruction
WareList: 125 items of 30 left.
WareList: 2 items of 33 left.
...
WareList: 1 items of 102 left.
Warning: Economy still has requests left on destruction
Warning: Economy still has flags left on destruction
Warning: Economy still has warehouses left on destruction
WareList: 127 items of 0 left.
...
WareList: 12 items of 102 left.
ObjectManager: ouch! remaining objects
lastserial: 2499
[NetRelayConnection] Closing network socket connected to 2a03:4000:32:524:48cb:feff:feae:b0c6:7397.
InternetGaming: logout(SERVER_CRASHED)
libc++abi.dylib: terminating with uncaught exception of type WException: [../src/logic/playercommand.cc:1070] Unreachable code was reached.
Abort trap: 6
ObjectManager: ouch! remaining objects
lastserial: 2499
[NetRelayConnection] Closing network socket connected to 2a03:4000:32:524:48cb:feff:feae:b0c6:7397.
InternetGaming: logout(SERVER_CRASHED)

I dont think this is related to tis particular branch, though.

Will try this on trunk again, later

review: Needs Fixing (testplay)
Revision history for this message
GunChleoc (gunchleoc) wrote :

Can you please create a bug report when finding errors caused by trunk, rather than putting them in a code review? You now have marked this branch as "needs fixing"; but it's trunk that needs fixing ;)

I have created a new bug report for this:

https://bugs.launchpad.net/widelands/+bug/1834151

Since you have found no further error in this branch, I'll go ahead and merge.

@bunnybot merge

> Compiling again, got:
>
> src/wui/constructionsitewindow.cc:33:19: warning: unused variable
> 'pic_max_fill_indicator'
> static const char pic_max_fill_indicator[] =
> "images/wui/buildings/max_fill_indicator.png";
> ^

These are already in trunk. It's time for another "fix compiler warnings" branch.
> After merging with trunk I dont see the network Issue any longer, well.

Excellent

> Did we just break savegame ccompatibility (yes with the elk_moose)

No, that branch has compatibility code. You won't be able to load new maps on older WIdelands versions though.

Revision history for this message
GunChleoc (gunchleoc) wrote :

Tracked down the line for the crash, it is caused by the new WIP constructionsite options. So, definitely not related to this branch.

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
=== modified file 'data/templates/default/init.lua'
--- data/templates/default/init.lua 2019-05-25 09:40:09 +0000
+++ data/templates/default/init.lua 2019-06-24 16:19:42 +0000
@@ -422,6 +422,13 @@
422 size = fs_font_size,422 size = fs_font_size,
423 bold = true,423 bold = true,
424 shadow = true424 shadow = true
425 },
426 hotkey = {
427 color = {180, 180, 180},
428 face = fs_font_face,
429 size = fs_font_size,
430 bold = true,
431 shadow = true
425 }432 }
426 },433 },
427 wui = {434 wui = {
@@ -438,6 +445,13 @@
438 size = fs_font_size,445 size = fs_font_size,
439 bold = true,446 bold = true,
440 shadow = true447 shadow = true
448 },
449 hotkey = {
450 color = {180, 180, 180},
451 face = fs_font_face,
452 size = fs_font_size,
453 bold = true,
454 shadow = true
441 }455 }
442 },456 },
443 },457 },
@@ -615,6 +629,12 @@
615 size = 14,629 size = 14,
616 bold = false,630 bold = false,
617 },631 },
632 tooltip_hotkey = {
633 color = {180, 180, 180},
634 face = fs_font_face,
635 size = 14,
636 bold = false,
637 },
618 tooltip_header = {638 tooltip_header = {
619 color = fs_font_color,639 color = fs_font_color,
620 face = fs_font_face,640 face = fs_font_face,
621641
=== modified file 'src/editor/CMakeLists.txt'
--- src/editor/CMakeLists.txt 2019-05-12 07:45:59 +0000
+++ src/editor/CMakeLists.txt 2019-06-24 16:19:42 +0000
@@ -65,6 +65,8 @@
65 ui_menus/main_menu_save_map.h65 ui_menus/main_menu_save_map.h
66 ui_menus/main_menu_save_map_make_directory.cc66 ui_menus/main_menu_save_map_make_directory.cc
67 ui_menus/main_menu_save_map_make_directory.h67 ui_menus/main_menu_save_map_make_directory.h
68 ui_menus/map_size_box.cc
69 ui_menus/map_size_box.h
68 ui_menus/player_menu.cc70 ui_menus/player_menu.cc
69 ui_menus/player_menu.h71 ui_menus/player_menu.h
70 ui_menus/tool_change_height_options_menu.cc72 ui_menus/tool_change_height_options_menu.cc
7173
=== modified file 'src/editor/ui_menus/main_menu_new_map.cc'
--- src/editor/ui_menus/main_menu_new_map.cc 2019-04-26 05:52:49 +0000
+++ src/editor/ui_menus/main_menu_new_map.cc 2019-06-24 16:19:42 +0000
@@ -46,24 +46,7 @@
46 margin_(4),46 margin_(4),
47 box_width_(get_inner_w() - 2 * margin_),47 box_width_(get_inner_w() - 2 * margin_),
48 box_(this, margin_, margin_, UI::Box::Vertical, 0, 0, margin_),48 box_(this, margin_, margin_, UI::Box::Vertical, 0, 0, margin_),
49 width_(&box_,49 map_size_box_(box_, "new_map_menu", 4, parent.egbase().map().get_width(), parent.egbase().map().get_height()),
50 0,
51 0,
52 box_width_,
53 box_width_ / 3,
54 24,
55 _("Width"),
56 UI::DropdownType::kTextual,
57 UI::PanelStyle::kWui),
58 height_(&box_,
59 0,
60 0,
61 box_width_,
62 box_width_ / 3,
63 24,
64 _("Height"),
65 UI::DropdownType::kTextual,
66 UI::PanelStyle::kWui),
67 list_(&box_, 0, 0, box_width_, 330, UI::PanelStyle::kWui),50 list_(&box_, 0, 0, box_width_, 330, UI::PanelStyle::kWui),
68 // Buttons51 // Buttons
69 button_box_(&box_, 0, 0, UI::Box::Horizontal, 0, 0, margin_),52 button_box_(&box_, 0, 0, UI::Box::Horizontal, 0, 0, margin_),
@@ -84,18 +67,8 @@
84 UI::ButtonStyle::kWuiSecondary,67 UI::ButtonStyle::kWuiSecondary,
85 _("Cancel")) {68 _("Cancel")) {
8669
87 for (const int32_t& i : Widelands::kMapDimensions) {
88 width_.add(std::to_string(i), i);
89 height_.add(std::to_string(i), i);
90 }
91 width_.select(parent.egbase().map().get_width());
92 height_.select(parent.egbase().map().get_height());
93 width_.set_max_items(12);
94 height_.set_max_items(12);
95
96 box_.set_size(100, 20); // Prevent assert failures70 box_.set_size(100, 20); // Prevent assert failures
97 box_.add(&width_);71 box_.add(&map_size_box_, UI::Box::Resizing::kExpandBoth);
98 box_.add(&height_);
99 box_.add_space(margin_);72 box_.add_space(margin_);
100 UI::Textarea* terrain_label = new UI::Textarea(&box_, _("Terrain:"));73 UI::Textarea* terrain_label = new UI::Textarea(&box_, _("Terrain:"));
101 box_.add(terrain_label);74 box_.add(terrain_label);
@@ -113,9 +86,7 @@
113 }86 }
114 box_.add(&button_box_);87 box_.add(&button_box_);
11588
116 box_.set_size(box_width_, width_.get_h() + height_.get_h() + terrain_label->get_h() +89 set_center_panel(&box_);
117 list_.get_h() + button_box_.get_h() + 9 * margin_);
118 set_size(get_w(), box_.get_h() + 2 * margin_ + get_h() - get_inner_h());
119 fill_list();90 fill_list();
120 center_to_parent();91 center_to_parent();
121}92}
@@ -132,8 +103,8 @@
132103
133 map->create_empty_map(104 map->create_empty_map(
134 egbase.world(),105 egbase.world(),
135 width_.get_selected() > 0 ? width_.get_selected() : Widelands::kMapDimensions[0],106 map_size_box_.selected_width(),
136 height_.get_selected() > 0 ? height_.get_selected() : Widelands::kMapDimensions[0],107 map_size_box_.selected_height(),
137 list_.get_selected(), _("No Name"),108 list_.get_selected(), _("No Name"),
138 g_options.pull_section("global").get_string("realname", pgettext("author_name", "Unknown")));109 g_options.pull_section("global").get_string("realname", pgettext("author_name", "Unknown")));
139110
140111
=== modified file 'src/editor/ui_menus/main_menu_new_map.h'
--- src/editor/ui_menus/main_menu_new_map.h 2019-04-26 05:52:49 +0000
+++ src/editor/ui_menus/main_menu_new_map.h 2019-06-24 16:19:42 +0000
@@ -20,10 +20,10 @@
20#ifndef WL_EDITOR_UI_MENUS_MAIN_MENU_NEW_MAP_H20#ifndef WL_EDITOR_UI_MENUS_MAIN_MENU_NEW_MAP_H
21#define WL_EDITOR_UI_MENUS_MAIN_MENU_NEW_MAP_H21#define WL_EDITOR_UI_MENUS_MAIN_MENU_NEW_MAP_H
2222
23#include "logic/widelands.h"23#include "editor/ui_menus/map_size_box.h"
24#include "logic/map_objects/description_maintainer.h"
24#include "ui_basic/box.h"25#include "ui_basic/box.h"
25#include "ui_basic/button.h"26#include "ui_basic/button.h"
26#include "ui_basic/dropdown.h"
27#include "ui_basic/listselect.h"27#include "ui_basic/listselect.h"
28#include "ui_basic/window.h"28#include "ui_basic/window.h"
2929
@@ -46,8 +46,7 @@
46 int32_t margin_;46 int32_t margin_;
47 int32_t box_width_;47 int32_t box_width_;
48 UI::Box box_;48 UI::Box box_;
49 UI::Dropdown<int32_t> width_;49 MapSizeBox map_size_box_;
50 UI::Dropdown<int32_t> height_;
5150
52 // Terrains list51 // Terrains list
53 UI::Listselect<Widelands::DescriptionIndex> list_;52 UI::Listselect<Widelands::DescriptionIndex> list_;
5453
=== modified file 'src/editor/ui_menus/main_menu_random_map.cc'
--- src/editor/ui_menus/main_menu_random_map.cc 2019-05-26 17:21:15 +0000
+++ src/editor/ui_menus/main_menu_random_map.cc 2019-06-24 16:19:42 +0000
@@ -55,24 +55,7 @@
55 label_height_(text_height(UI::FontStyle::kLabel) + 2),55 label_height_(text_height(UI::FontStyle::kLabel) + 2),
56 box_(this, margin_, margin_, UI::Box::Vertical, 0, 0, margin_),56 box_(this, margin_, margin_, UI::Box::Vertical, 0, 0, margin_),
57 // Size57 // Size
58 width_(&box_,58 map_size_box_(box_, "random_map_menu", 4, parent.egbase().map().get_width(), parent.egbase().map().get_height()),
59 0,
60 0,
61 box_width_,
62 box_width_ / 3,
63 24,
64 _("Width"),
65 UI::DropdownType::kTextual,
66 UI::PanelStyle::kWui),
67 height_(&box_,
68 0,
69 0,
70 box_width_,
71 box_width_ / 3,
72 24,
73 _("Height"),
74 UI::DropdownType::kTextual,
75 UI::PanelStyle::kWui),
76 max_players_(2),59 max_players_(2),
77 players_(&box_,60 players_(&box_,
78 0,61 0,
@@ -212,28 +195,13 @@
212 UI::ButtonStyle::kWuiSecondary,195 UI::ButtonStyle::kWuiSecondary,
213 _("Cancel")) {196 _("Cancel")) {
214 int32_t box_height = 0;197 int32_t box_height = 0;
198 box_.set_size(100, 20); // Prevent assert failures
215199
216 // ---------- Width + Height ----------200 // ---------- Width + Height ----------
217201
218 for (const int32_t& i : Widelands::kMapDimensions) {202 map_size_box_.set_selection_function([this] { button_clicked(ButtonId::kMapSize); });
219 width_.add(std::to_string(i), i);203 box_.add(&map_size_box_, UI::Box::Resizing::kExpandBoth);
220 height_.add(std::to_string(i), i);204 box_height += margin_ + map_size_box_.get_h();
221 }
222 width_.select(parent.egbase().map().get_width());
223 height_.select(parent.egbase().map().get_height());
224 width_.set_max_items(12);
225 height_.set_max_items(12);
226
227 width_.selected.connect(
228 boost::bind(&MainMenuNewRandomMap::button_clicked, this, ButtonId::kMapSize));
229 height_.selected.connect(
230 boost::bind(&MainMenuNewRandomMap::button_clicked, this, ButtonId::kMapSize));
231
232 box_.set_size(100, 20); // Prevent assert failures
233 box_.add(&width_);
234 box_.add(&height_);
235 box_height += margin_ + width_.get_h();
236 box_height += margin_ + height_.get_h();
237205
238 // ---------- Players -----------206 // ---------- Players -----------
239207
@@ -396,19 +364,6 @@
396 */364 */
397void MainMenuNewRandomMap::button_clicked(MainMenuNewRandomMap::ButtonId n) {365void MainMenuNewRandomMap::button_clicked(MainMenuNewRandomMap::ButtonId n) {
398 switch (n) {366 switch (n) {
399 case ButtonId::kPlayers: // intended fall-through
400 case ButtonId::kMapSize:
401 // Restrict maximum players according to map size, but allow at least 2 players.
402 max_players_ = std::min(
403 static_cast<size_t>(kMaxMapgenPlayers), (find_dimension_index(width_.get_selected()) +
404 find_dimension_index(height_.get_selected())) /
405 2 +
406 2);
407 players_.set_interval(1, max_players_);
408 if (players_.get_value() > max_players_) {
409 players_.set_value(max_players_);
410 }
411 break;
412 case ButtonId::kWater:367 case ButtonId::kWater:
413 waterval_ = water_.get_value();368 waterval_ = water_.get_value();
414 normalize_landmass(n);369 normalize_landmass(n);
@@ -433,17 +388,20 @@
433 break;388 break;
434 case ButtonId::kIslandMode:389 case ButtonId::kIslandMode:
435 break;390 break;
391 case ButtonId::kPlayers: // intended fall-through
392 case ButtonId::kMapSize:
436 case ButtonId::kNone:393 case ButtonId::kNone:
437 // Make sure that all conditions are met394 // Restrict maximum players according to map size, but allow at least 2 players.
438 max_players_ = std::min(395 max_players_ = std::min(
439 static_cast<size_t>(kMaxMapgenPlayers), (find_dimension_index(width_.get_selected()) +396 static_cast<size_t>(kMaxMapgenPlayers), (find_dimension_index(map_size_box_.selected_width()) +
440 find_dimension_index(height_.get_selected())) /397 find_dimension_index(map_size_box_.selected_height())) /
441 2 +398 2 +
442 2);399 2);
443 players_.set_interval(1, max_players_);400 players_.set_interval(1, max_players_);
444 if (players_.get_value() > max_players_) {401 if (players_.get_value() > max_players_) {
445 players_.set_value(max_players_);402 players_.set_value(max_players_);
446 }403 }
404 // Make sure that landmass is consistent
447 normalize_landmass(n);405 normalize_landmass(n);
448 }406 }
449 nr_edit_box_changed(); // Update ID String407 nr_edit_box_changed(); // Update ID String
@@ -598,8 +556,8 @@
598 sstrm << map_info.mapNumber;556 sstrm << map_info.mapNumber;
599 map_number_edit_.set_text(sstrm.str());557 map_number_edit_.set_text(sstrm.str());
600558
601 width_.select(map_info.w);559 map_size_box_.select_width(map_info.w);
602 height_.select(map_info.h);560 map_size_box_.select_height(map_info.h);
603561
604 players_.set_interval(1, map_info.numPlayers); // hack to make sure we can set the value562 players_.set_interval(1, map_info.numPlayers); // hack to make sure we can set the value
605 players_.set_value(map_info.numPlayers);563 players_.set_value(map_info.numPlayers);
@@ -652,8 +610,8 @@
652}610}
653611
654void MainMenuNewRandomMap::set_map_info(Widelands::UniqueRandomMapInfo& map_info) const {612void MainMenuNewRandomMap::set_map_info(Widelands::UniqueRandomMapInfo& map_info) const {
655 map_info.w = width_.get_selected() > 0 ? width_.get_selected() : Widelands::kMapDimensions[0];613 map_info.w = map_size_box_.selected_width();
656 map_info.h = height_.get_selected() > 0 ? height_.get_selected() : Widelands::kMapDimensions[0];614 map_info.h = map_size_box_.selected_height();
657 map_info.waterRatio = static_cast<double>(waterval_) / 100.0;615 map_info.waterRatio = static_cast<double>(waterval_) / 100.0;
658 map_info.landRatio = static_cast<double>(landval_) / 100.0;616 map_info.landRatio = static_cast<double>(landval_) / 100.0;
659 map_info.wastelandRatio = static_cast<double>(wastelandval_) / 100.0;617 map_info.wastelandRatio = static_cast<double>(wastelandval_) / 100.0;
660618
=== modified file 'src/editor/ui_menus/main_menu_random_map.h'
--- src/editor/ui_menus/main_menu_random_map.h 2019-04-10 10:42:22 +0000
+++ src/editor/ui_menus/main_menu_random_map.h 2019-06-24 16:19:42 +0000
@@ -23,6 +23,7 @@
23#include <vector>23#include <vector>
2424
25#include "base/macros.h"25#include "base/macros.h"
26#include "editor/ui_menus/map_size_box.h"
26#include "ui_basic/box.h"27#include "ui_basic/box.h"
27#include "ui_basic/checkbox.h"28#include "ui_basic/checkbox.h"
28#include "ui_basic/dropdown.h"29#include "ui_basic/dropdown.h"
@@ -89,8 +90,7 @@
89 UI::Box box_;90 UI::Box box_;
9091
91 // Size92 // Size
92 UI::Dropdown<int32_t> width_;93 MapSizeBox map_size_box_;
93 UI::Dropdown<int32_t> height_;
9494
95 uint8_t max_players_;95 uint8_t max_players_;
96 UI::SpinBox players_;96 UI::SpinBox players_;
9797
=== added file 'src/editor/ui_menus/map_size_box.cc'
--- src/editor/ui_menus/map_size_box.cc 1970-01-01 00:00:00 +0000
+++ src/editor/ui_menus/map_size_box.cc 2019-06-24 16:19:42 +0000
@@ -0,0 +1,76 @@
1/*
2 * Copyright (C) 2019 by the Widelands Development Team
3 *
4 * This program is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU General Public License
6 * as published by the Free Software Foundation; either version 2
7 * of the License, or (at your option) any later version.
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write to the Free Software
16 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17 *
18 */
19
20#include "editor/ui_menus/map_size_box.h"
21
22#include "base/i18n.h"
23#include "logic/map.h"
24
25MapSizeBox::MapSizeBox(UI::Box& parent, const std::string& name, int spacing, int map_width, int map_height)
26 : UI::Box(&parent, 0, 0, UI::Box::Horizontal, 0, 0, spacing),
27 width_(this,
28 name + "_map_width",
29 0,
30 0,
31 160,
32 12,
33 24,
34 _("Width"),
35 UI::DropdownType::kTextual,
36 UI::PanelStyle::kWui,
37 UI::ButtonStyle::kWuiSecondary),
38 height_(this,
39 name + "_map_height",
40 0,
41 0,
42 160,
43 12,
44 24,
45 _("Height"),
46 UI::DropdownType::kTextual,
47 UI::PanelStyle::kWui,
48 UI::ButtonStyle::kWuiSecondary) {
49 for (const int32_t& i : Widelands::kMapDimensions) {
50 width_.add(std::to_string(i), i);
51 height_.add(std::to_string(i), i);
52 }
53 width_.select(map_width);
54 height_.select(map_height);
55 add(&width_, UI::Box::Resizing::kFillSpace);
56 add(&height_, UI::Box::Resizing::kFillSpace);
57}
58
59void MapSizeBox::set_selection_function(const std::function<void()> func) {
60 width_.selected.connect(func);
61 height_.selected.connect(func);
62}
63
64uint32_t MapSizeBox::selected_width() const {
65 return width_.get_selected();
66}
67uint32_t MapSizeBox::selected_height() const {
68 return height_.get_selected();
69}
70
71void MapSizeBox::select_width(int new_width) {
72 width_.select(new_width);
73}
74void MapSizeBox::select_height(int new_height) {
75 height_.select(new_height);
76}
077
=== added file 'src/editor/ui_menus/map_size_box.h'
--- src/editor/ui_menus/map_size_box.h 1970-01-01 00:00:00 +0000
+++ src/editor/ui_menus/map_size_box.h 2019-06-24 16:19:42 +0000
@@ -0,0 +1,57 @@
1/*
2 * Copyright (C) 2019 by the Widelands Development Team
3 *
4 * This program is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU General Public License
6 * as published by the Free Software Foundation; either version 2
7 * of the License, or (at your option) any later version.
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write to the Free Software
16 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17 *
18 */
19
20#ifndef WL_EDITOR_UI_MENUS_MAP_SIZE_BOX_H
21#define WL_EDITOR_UI_MENUS_MAP_SIZE_BOX_H
22
23#include "ui_basic/box.h"
24#include "ui_basic/dropdown.h"
25
26/**
27 * A box containing 2 dropdowns to select map width and height with horizontal layout.
28 * Selections are taken from Widelands::kMapDimensions.
29 */
30struct MapSizeBox : public UI::Box {
31
32 /**
33 * @param parent The parent panel
34 * @param name A string to prefix for the dropdown names, so that they can be idenfitied uniquely
35 * @param spacing The horizontal space between the 2 dropdowns
36 * @param map_width Width to preselect
37 * @param map_height Height to preselect
38 */
39 MapSizeBox(UI::Box& parent, const std::string& name, int spacing, int map_width, int map_height);
40
41 /// This function will be triggered when a new width or height is selected from the dropdowns
42 void set_selection_function(const std::function<void()> func);
43 /// The currently selected width
44 uint32_t selected_width() const;
45 /// The currently selected height
46 uint32_t selected_height() const;
47 /// Set the selected width
48 void select_width(int new_width);
49 /// Set the selected height
50 void select_height(int new_height);
51
52private:
53 UI::Dropdown<uint32_t> width_;
54 UI::Dropdown<uint32_t> height_;
55};
56
57#endif // end of include guard: WL_EDITOR_UI_MENUS_MAP_SIZE_BOX_H
058
=== modified file 'src/editor/ui_menus/player_menu.cc'
--- src/editor/ui_menus/player_menu.cc 2019-05-12 07:45:59 +0000
+++ src/editor/ui_menus/player_menu.cc 2019-06-24 16:19:42 +0000
@@ -38,8 +38,9 @@
3838
39namespace {39namespace {
40constexpr int kMargin = 4;40constexpr int kMargin = 4;
41// Make room for 8 players
41// If this ever gets changed, don't forget to change the strings in the warning box as well.42// If this ever gets changed, don't forget to change the strings in the warning box as well.
42constexpr Widelands::PlayerNumber max_recommended_players = 8;43constexpr Widelands::PlayerNumber kMaxRecommendedPlayers = 8;
43} // namespace44} // namespace
4445
45class EditorPlayerMenuWarningBox : public UI::Window {46class EditorPlayerMenuWarningBox : public UI::Window {
@@ -122,14 +123,15 @@
122 : UI::UniqueWindow(&parent, "players_menu", &registry, 100, 100, _("Player Options")),123 : UI::UniqueWindow(&parent, "players_menu", &registry, 100, 100, _("Player Options")),
123 box_(this, kMargin, kMargin, UI::Box::Vertical),124 box_(this, kMargin, kMargin, UI::Box::Vertical),
124 no_of_players_(&box_,125 no_of_players_(&box_,
126 "dropdown_map_players",
125 0,127 0,
126 0,128 0,
127 50,129 50,
128 100,130 kMaxRecommendedPlayers,
129 24,131 24,
130 _("Number of players"),132 _("Number of players"),
131 UI::DropdownType::kTextual,133 UI::DropdownType::kTextual,
132 UI::PanelStyle::kWui) {134 UI::PanelStyle::kWui, UI::ButtonStyle::kWuiSecondary) {
133 box_.set_size(100, 100); // Prevent assert failures135 box_.set_size(100, 100); // Prevent assert failures
134 box_.add(&no_of_players_, UI::Box::Resizing::kFullSize);136 box_.add(&no_of_players_, UI::Box::Resizing::kFullSize);
135 box_.add_space(2 * kMargin);137 box_.add_space(2 * kMargin);
@@ -153,7 +155,7 @@
153 iterate_player_numbers(p, kMaxPlayers) {155 iterate_player_numbers(p, kMaxPlayers) {
154 const bool map_has_player = p <= nr_players;156 const bool map_has_player = p <= nr_players;
155157
156 no_of_players_.add(boost::lexical_cast<std::string>(static_cast<unsigned int>(p)), p);158 no_of_players_.add(boost::lexical_cast<std::string>(static_cast<unsigned int>(p)), p, nullptr, p == nr_players);
157 no_of_players_.selected.connect(159 no_of_players_.selected.connect(
158 boost::bind(&EditorPlayerMenu::no_of_players_clicked, boost::ref(*this)));160 boost::bind(&EditorPlayerMenu::no_of_players_clicked, boost::ref(*this)));
159161
@@ -168,8 +170,8 @@
168170
169 // Tribe171 // Tribe
170 UI::Dropdown<std::string>* plr_tribe =172 UI::Dropdown<std::string>* plr_tribe =
171 new UI::Dropdown<std::string>(row, 0, 0, 50, 400, plr_name->get_h(), _("Tribe"),173 new UI::Dropdown<std::string>(row, (boost::format("dropdown_tribe%d") % static_cast<unsigned int>(p)).str(), 0, 0, 50, 16, plr_name->get_h(), _("Tribe"),
172 UI::DropdownType::kPictorial, UI::PanelStyle::kWui);174 UI::DropdownType::kPictorial, UI::PanelStyle::kWui, UI::ButtonStyle::kWuiSecondary);
173 {175 {
174 i18n::Textdomain td("tribes");176 i18n::Textdomain td("tribes");
175 for (const Widelands::TribeBasicInfo& tribeinfo : Widelands::get_all_tribeinfos()) {177 for (const Widelands::TribeBasicInfo& tribeinfo : Widelands::get_all_tribeinfos()) {
@@ -219,8 +221,6 @@
219 std::unique_ptr<PlayerEditRow>(new PlayerEditRow(row, plr_name, plr_position, plr_tribe)));221 std::unique_ptr<PlayerEditRow>(new PlayerEditRow(row, plr_name, plr_position, plr_tribe)));
220 }222 }
221223
222 // Make room for 8 players
223 no_of_players_.set_max_items(max_recommended_players);
224 no_of_players_.select(nr_players);224 no_of_players_.select(nr_players);
225225
226 // Init button states226 // Init button states
@@ -249,13 +249,13 @@
249 }249 }
250250
251 // Display a warning if there are too many players251 // Display a warning if there are too many players
252 if (nr_players > max_recommended_players) {252 if (nr_players > kMaxRecommendedPlayers) {
253 if (g_options.pull_section("global").get_bool(253 if (g_options.pull_section("global").get_bool(
254 "editor_player_menu_warn_too_many_players", true)) {254 "editor_player_menu_warn_too_many_players", true)) {
255 EditorPlayerMenuWarningBox warning(get_parent());255 EditorPlayerMenuWarningBox warning(get_parent());
256 if (warning.run<UI::Panel::Returncodes>() == UI::Panel::Returncodes::kBack) {256 if (warning.run<UI::Panel::Returncodes>() == UI::Panel::Returncodes::kBack) {
257 // Abort setting of players257 // Abort setting of players
258 no_of_players_.select(std::min(old_nr_players, max_recommended_players));258 no_of_players_.select(std::min(old_nr_players, kMaxRecommendedPlayers));
259 }259 }
260 }260 }
261 }261 }
262262
=== modified file 'src/editor/ui_menus/tool_resize_options_menu.cc'
--- src/editor/ui_menus/tool_resize_options_menu.cc 2019-04-24 07:09:29 +0000
+++ src/editor/ui_menus/tool_resize_options_menu.cc 2019-06-24 16:19:42 +0000
@@ -39,69 +39,32 @@
39 : EditorToolOptionsMenu(parent, registry, 260, 200, _("Resize")),39 : EditorToolOptionsMenu(parent, registry, 260, 200, _("Resize")),
40 resize_tool_(resize_tool),40 resize_tool_(resize_tool),
41 box_(this, hmargin(), vmargin(), UI::Box::Vertical, 0, 0, vspacing()),41 box_(this, hmargin(), vmargin(), UI::Box::Vertical, 0, 0, vspacing()),
42 new_width_(&box_,42 map_size_box_(box_, "tool_resize_map", 4, parent.egbase().map().get_width(), parent.egbase().map().get_height()),
43 0,
44 0,
45 get_inner_w() - 2 * hmargin(),
46 200,
47 24,
48 _("New width"),
49 UI::DropdownType::kTextual,
50 UI::PanelStyle::kWui),
51 new_height_(&box_,
52 0,
53 0,
54 get_inner_w() - 2 * hmargin(),
55 200,
56 24,
57 _("New height"),
58 UI::DropdownType::kTextual,
59 UI::PanelStyle::kWui),
60 text_area_(43 text_area_(
61 &box_,44 &box_,
62 0,45 0,
63 0,46 0,
64 get_inner_w() - 2 * hmargin(),47 get_inner_w() - 2 * hmargin(),
65 48,48 48,
66 UI::PanelStyle::kWui,49 UI::PanelStyle::kWui,
67 _("Select the new map size, then click the map to split it at the desired location."),50 _("Select the new map size, then click the map to split it at the desired location."),
68 UI::Align::kCenter,51 UI::Align::kCenter,
69 UI::MultilineTextarea::ScrollMode::kNoScrolling) {52 UI::MultilineTextarea::ScrollMode::kNoScrolling) {
7053
71 for (const int32_t& i : Widelands::kMapDimensions) {54 map_size_box_.set_selection_function([this] { update_dimensions(); });
72 new_width_.add(std::to_string(i), i);55
73 new_height_.add(std::to_string(i), i);56 box_.add(&map_size_box_, UI::Box::Resizing::kExpandBoth);
74 }57 box_.add(&text_area_, UI::Box::Resizing::kFullSize);
75 new_width_.select(parent.egbase().map().get_width());58
76 new_height_.select(parent.egbase().map().get_height());59 set_center_panel(&box_);
77 new_width_.set_max_items(8);
78 new_height_.set_max_items(8);
79
80 new_width_.selected.connect(
81 boost::bind(&EditorToolResizeOptionsMenu::update_width, boost::ref(*this)));
82 new_height_.selected.connect(
83 boost::bind(&EditorToolResizeOptionsMenu::update_height, boost::ref(*this)));
84
85 box_.add(&text_area_);
86 box_.set_size(100, 20); // Prevent assert failures
87 box_.add(&new_width_, UI::Box::Resizing::kFullSize);
88 box_.add(&new_height_, UI::Box::Resizing::kFullSize);
89
90 box_.set_size(get_inner_w() - 2 * hmargin(),
91 new_width_.get_h() + new_height_.get_h() + text_area_.get_h() + 2 * vspacing());
92 set_inner_size(get_inner_w(), box_.get_h() + 1 * vmargin());
93}60}
9461
95void EditorToolResizeOptionsMenu::update_width() {62void EditorToolResizeOptionsMenu::update_dimensions() {
96 int32_t w = new_width_.get_selected();63 const int32_t w = map_size_box_.selected_width();
64 const int32_t h = map_size_box_.selected_height();
97 assert(w > 0);65 assert(w > 0);
66 assert(h > 0);
98 resize_tool_.set_width(w);67 resize_tool_.set_width(w);
99 select_correct_tool();
100}
101
102void EditorToolResizeOptionsMenu::update_height() {
103 int32_t h = new_height_.get_selected();
104 assert(h > 0);
105 resize_tool_.set_height(h);68 resize_tool_.set_height(h);
106 select_correct_tool();69 select_correct_tool();
107}70}
10871
=== modified file 'src/editor/ui_menus/tool_resize_options_menu.h'
--- src/editor/ui_menus/tool_resize_options_menu.h 2019-04-24 07:09:29 +0000
+++ src/editor/ui_menus/tool_resize_options_menu.h 2019-06-24 16:19:42 +0000
@@ -20,6 +20,7 @@
20#ifndef WL_EDITOR_UI_MENUS_TOOL_RESIZE_OPTIONS_MENU_H20#ifndef WL_EDITOR_UI_MENUS_TOOL_RESIZE_OPTIONS_MENU_H
21#define WL_EDITOR_UI_MENUS_TOOL_RESIZE_OPTIONS_MENU_H21#define WL_EDITOR_UI_MENUS_TOOL_RESIZE_OPTIONS_MENU_H
2222
23#include "editor/ui_menus/map_size_box.h"
23#include "editor/ui_menus/tool_options_menu.h"24#include "editor/ui_menus/tool_options_menu.h"
24#include "ui_basic/box.h"25#include "ui_basic/box.h"
25#include "ui_basic/dropdown.h"26#include "ui_basic/dropdown.h"
@@ -33,13 +34,11 @@
3334
34private:35private:
35 EditorInteractive& eia();36 EditorInteractive& eia();
36 void update_width();37 void update_dimensions();
37 void update_height();
3838
39 EditorResizeTool& resize_tool_;39 EditorResizeTool& resize_tool_;
40 UI::Box box_;40 UI::Box box_;
41 UI::Dropdown<int32_t> new_width_;41 MapSizeBox map_size_box_;
42 UI::Dropdown<int32_t> new_height_;
43 UI::MultilineTextarea text_area_;42 UI::MultilineTextarea text_area_;
44};43};
4544
4645
=== modified file 'src/graphic/style_manager.cc'
--- src/graphic/style_manager.cc 2019-05-26 17:21:15 +0000
+++ src/graphic/style_manager.cc 2019-06-24 16:19:42 +0000
@@ -214,6 +214,7 @@
214 add_font_style(UI::FontStyle::kDisabled, *element_table, "disabled");214 add_font_style(UI::FontStyle::kDisabled, *element_table, "disabled");
215 add_font_style(UI::FontStyle::kLabel, *element_table, "label");215 add_font_style(UI::FontStyle::kLabel, *element_table, "label");
216 add_font_style(UI::FontStyle::kTooltipHeader, *element_table, "tooltip_header");216 add_font_style(UI::FontStyle::kTooltipHeader, *element_table, "tooltip_header");
217 add_font_style(UI::FontStyle::kTooltipHotkey, *element_table, "tooltip_hotkey");
217 add_font_style(UI::FontStyle::kTooltip, *element_table, "tooltip");218 add_font_style(UI::FontStyle::kTooltip, *element_table, "tooltip");
218 add_font_style(UI::FontStyle::kWarning, *element_table, "warning");219 add_font_style(UI::FontStyle::kWarning, *element_table, "warning");
219 add_font_style(220 add_font_style(
@@ -339,7 +340,7 @@
339void StyleManager::add_table_style(UI::PanelStyle style, const LuaTable& table) {340void StyleManager::add_table_style(UI::PanelStyle style, const LuaTable& table) {
340 table_styles_.insert(std::make_pair(341 table_styles_.insert(std::make_pair(
341 style, std::unique_ptr<const UI::TableStyleInfo>(new UI::TableStyleInfo(342 style, std::unique_ptr<const UI::TableStyleInfo>(new UI::TableStyleInfo(
342 read_font_style(table, "enabled"), read_font_style(table, "disabled")))));343 read_font_style(table, "enabled"), read_font_style(table, "disabled"), read_font_style(table, "hotkey")))));
343}344}
344345
345void StyleManager::set_statistics_plot_style(const LuaTable& table) {346void StyleManager::set_statistics_plot_style(const LuaTable& table) {
346347
=== modified file 'src/graphic/styles/font_style.h'
--- src/graphic/styles/font_style.h 2019-05-26 17:21:15 +0000
+++ src/graphic/styles/font_style.h 2019-06-24 16:19:42 +0000
@@ -44,6 +44,7 @@
44 kDisabled,44 kDisabled,
45 kLabel,45 kLabel,
46 kTooltipHeader,46 kTooltipHeader,
47 kTooltipHotkey,
47 kTooltip,48 kTooltip,
48 kWarning,49 kWarning,
49 kWuiAttackBoxSliderLabel,50 kWuiAttackBoxSliderLabel,
5051
=== modified file 'src/graphic/styles/table_style.h'
--- src/graphic/styles/table_style.h 2019-05-26 17:21:15 +0000
+++ src/graphic/styles/table_style.h 2019-06-24 16:19:42 +0000
@@ -27,8 +27,8 @@
27namespace UI {27namespace UI {
2828
29struct TableStyleInfo {29struct TableStyleInfo {
30 explicit TableStyleInfo(UI::FontStyleInfo* init_enabled, UI::FontStyleInfo* init_disabled)30 explicit TableStyleInfo(UI::FontStyleInfo* init_enabled, UI::FontStyleInfo* init_disabled, UI::FontStyleInfo* init_hotkey)
31 : enabled_(init_enabled), disabled_(init_disabled) {31 : enabled_(init_enabled), disabled_(init_disabled), hotkey_(init_hotkey) {
32 }32 }
3333
34 const UI::FontStyleInfo& enabled() const {34 const UI::FontStyleInfo& enabled() const {
@@ -37,10 +37,14 @@
37 const UI::FontStyleInfo& disabled() const {37 const UI::FontStyleInfo& disabled() const {
38 return *disabled_.get();38 return *disabled_.get();
39 }39 }
40 const UI::FontStyleInfo& hotkey() const {
41 return *hotkey_.get();
42 }
4043
41private:44private:
42 std::unique_ptr<const UI::FontStyleInfo> enabled_;45 std::unique_ptr<const UI::FontStyleInfo> enabled_;
43 std::unique_ptr<const UI::FontStyleInfo> disabled_;46 std::unique_ptr<const UI::FontStyleInfo> disabled_;
47 std::unique_ptr<const UI::FontStyleInfo> hotkey_;
44};48};
4549
46} // namespace UI50} // namespace UI
4751
=== modified file 'src/graphic/text/rt_parse.cc'
--- src/graphic/text/rt_parse.cc 2019-02-28 11:46:22 +0000
+++ src/graphic/text/rt_parse.cc 2019-06-24 16:19:42 +0000
@@ -119,8 +119,10 @@
119119
120void Tag::parse_attribute(TextStream& ts, std::unordered_set<std::string>& allowed_attrs) {120void Tag::parse_attribute(TextStream& ts, std::unordered_set<std::string>& allowed_attrs) {
121 std::string aname = ts.till_any("=");121 std::string aname = ts.till_any("=");
122 if (!allowed_attrs.count(aname))122 if (!allowed_attrs.count(aname)) {
123 throw SyntaxErrorImpl(ts.line(), ts.col(), "an allowed attribute", aname, ts.peek(100));123 const std::string error_info = (boost::format("an allowed attribute for '%s' tag") % name_).str();
124 throw SyntaxErrorImpl(ts.line(), ts.col(), error_info, aname, ts.peek(100));
125 }
124126
125 ts.skip(1);127 ts.skip(1);
126128
127129
=== modified file 'src/graphic/text_layout.cc'
--- src/graphic/text_layout.cc 2019-05-26 17:21:15 +0000
+++ src/graphic/text_layout.cc 2019-06-24 16:19:42 +0000
@@ -243,3 +243,10 @@
243 }243 }
244 NEVER_HERE();244 NEVER_HERE();
245}245}
246
247std::string as_tooltip_text_with_hotkey(const std::string& text, const std::string& hotkey) {
248 static boost::format f("<rt><p>%s %s</p></rt>");
249 f % g_gr->styles().font_style(UI::FontStyle::kTooltip).as_font_tag(text);
250 f % g_gr->styles().font_style(UI::FontStyle::kTooltipHotkey).as_font_tag("(" + hotkey + ")");
251 return f.str();
252}
246253
=== modified file 'src/graphic/text_layout.h'
--- src/graphic/text_layout.h 2019-05-26 17:21:15 +0000
+++ src/graphic/text_layout.h 2019-06-24 16:19:42 +0000
@@ -107,4 +107,6 @@
107/// Paragraph in menu info texts107/// Paragraph in menu info texts
108std::string as_content(const std::string& txt, UI::PanelStyle style);108std::string as_content(const std::string& txt, UI::PanelStyle style);
109109
110std::string as_tooltip_text_with_hotkey(const std::string& text, const std::string& hotkey);
111
110#endif // end of include guard: WL_GRAPHIC_TEXT_LAYOUT_H112#endif // end of include guard: WL_GRAPHIC_TEXT_LAYOUT_H
111113
=== modified file 'src/scripting/lua_ui.cc'
--- src/scripting/lua_ui.cc 2019-02-23 11:00:49 +0000
+++ src/scripting/lua_ui.cc 2019-06-24 16:19:42 +0000
@@ -73,7 +73,7 @@
73*/73*/
74const char LuaPanel::className[] = "Panel";74const char LuaPanel::className[] = "Panel";
75const PropertyType<LuaPanel> LuaPanel::Properties[] = {75const PropertyType<LuaPanel> LuaPanel::Properties[] = {
76 PROP_RO(LuaPanel, buttons), PROP_RO(LuaPanel, tabs), PROP_RO(LuaPanel, windows),76 PROP_RO(LuaPanel, buttons), PROP_RO(LuaPanel, dropdowns), PROP_RO(LuaPanel, tabs), PROP_RO(LuaPanel, windows),
77 PROP_RW(LuaPanel, position_x), PROP_RW(LuaPanel, position_y), PROP_RW(LuaPanel, width),77 PROP_RW(LuaPanel, position_x), PROP_RW(LuaPanel, position_y), PROP_RW(LuaPanel, width),
78 PROP_RW(LuaPanel, height), {nullptr, nullptr, nullptr},78 PROP_RW(LuaPanel, height), {nullptr, nullptr, nullptr},
79};79};
@@ -82,6 +82,26 @@
82 {nullptr, nullptr},82 {nullptr, nullptr},
83};83};
8484
85// Look for all descendant panels of class P and add the corresponding Lua version to the currently active Lua table. Class P needs to be a NamedPanel.
86template <class P, class LuaP>
87static void put_all_visible_panels_into_table(lua_State* L, UI::Panel* g) {
88 if (g == nullptr) {
89 return;
90 }
91
92 for (UI::Panel* child = g->get_first_child(); child; child = child->get_next_sibling()) {
93 put_all_visible_panels_into_table<P, LuaP>(L, child);
94
95 if (upcast(P, specific_panel, child)) {
96 if (specific_panel->is_visible()) {
97 lua_pushstring(L, specific_panel->get_name());
98 to_lua<LuaP>(L, new LuaP(specific_panel));
99 lua_rawset(L, -3);
100 }
101 }
102 }
103}
104
85/*105/*
86 * Properties106 * Properties
87 */107 */
@@ -97,26 +117,26 @@
97117
98 (RO) An :class:`array` of all visible buttons inside this Panel.118 (RO) An :class:`array` of all visible buttons inside this Panel.
99*/119*/
100static void put_all_visible_buttons_into_table(lua_State* L, UI::Panel* g) {
101 if (!g)
102 return;
103
104 for (UI::Panel* f = g->get_first_child(); f; f = f->get_next_sibling()) {
105 put_all_visible_buttons_into_table(L, f);
106
107 if (upcast(UI::Button, b, f))
108 if (b->is_visible()) {
109 lua_pushstring(L, b->get_name());
110 to_lua<LuaButton>(L, new LuaButton(b));
111 lua_rawset(L, -3);
112 }
113 }
114}
115int LuaPanel::get_buttons(lua_State* L) {120int LuaPanel::get_buttons(lua_State* L) {
116 assert(panel_);121 assert(panel_);
117122
118 lua_newtable(L);123 lua_newtable(L);
119 put_all_visible_buttons_into_table(L, panel_);124 put_all_visible_panels_into_table<UI::Button, LuaButton>(L, panel_);
125
126 return 1;
127}
128
129
130/* RST
131 .. attribute:: dropdowns
132
133 (RO) An :class:`array` of all visible dropdowns inside this Panel.
134*/
135int LuaPanel::get_dropdowns(lua_State* L) {
136 assert(panel_);
137
138 lua_newtable(L);
139 put_all_visible_panels_into_table<UI::BaseDropdown, LuaDropdown>(L, panel_);
120140
121 return 1;141 return 1;
122}142}
@@ -156,25 +176,11 @@
156 (RO) A :class:`array` of all currently open windows that are176 (RO) A :class:`array` of all currently open windows that are
157 children of this Panel.177 children of this Panel.
158*/178*/
159static void put_all_visible_windows_into_table(lua_State* L, UI::Panel* g) {
160 if (!g)
161 return;
162
163 for (UI::Panel* f = g->get_first_child(); f; f = f->get_next_sibling()) {
164 put_all_visible_windows_into_table(L, f);
165
166 if (upcast(UI::Window, win, f)) {
167 lua_pushstring(L, win->get_name());
168 to_lua<LuaWindow>(L, new LuaWindow(win));
169 lua_rawset(L, -3);
170 }
171 }
172}
173int LuaPanel::get_windows(lua_State* L) {179int LuaPanel::get_windows(lua_State* L) {
174 assert(panel_);180 assert(panel_);
175181
176 lua_newtable(L);182 lua_newtable(L);
177 put_all_visible_windows_into_table(L, panel_);183 put_all_visible_panels_into_table<UI::Window, LuaWindow>(L, panel_);
178184
179 return 1;185 return 1;
180}186}
@@ -317,6 +323,7 @@
317 event in tutorials323 event in tutorials
318*/324*/
319int LuaButton::press(lua_State* /* L */) {325int LuaButton::press(lua_State* /* L */) {
326 log("Pressing button '%s'\n", get()->get_name().c_str());
320 get()->handle_mousein(true);327 get()->handle_mousein(true);
321 get()->handle_mousepress(SDL_BUTTON_LEFT, 1, 1);328 get()->handle_mousepress(SDL_BUTTON_LEFT, 1, 1);
322 return 0;329 return 0;
@@ -328,12 +335,102 @@
328 it.335 it.
329*/336*/
330int LuaButton::click(lua_State* /* L */) {337int LuaButton::click(lua_State* /* L */) {
338 log("Clicking button '%s'\n", get()->get_name().c_str());
331 get()->handle_mousein(true);339 get()->handle_mousein(true);
332 get()->handle_mousepress(SDL_BUTTON_LEFT, 1, 1);340 get()->handle_mousepress(SDL_BUTTON_LEFT, 1, 1);
333 get()->handle_mouserelease(SDL_BUTTON_LEFT, 1, 1);341 get()->handle_mouserelease(SDL_BUTTON_LEFT, 1, 1);
334 return 0;342 return 0;
335}343}
336344
345
346/*
347 * C Functions
348 */
349
350/* RST
351Dropdown
352--------
353
354.. class:: Dropdown
355
356 Child of: :class:`Panel`
357
358 This represents a dropdown menu.
359*/
360const char LuaDropdown::className[] = "Dropdown";
361const MethodType<LuaDropdown> LuaDropdown::Methods[] = {
362 METHOD(LuaDropdown, open),
363 METHOD(LuaDropdown, highlight_item),
364 METHOD(LuaDropdown, select),
365 {nullptr, nullptr},
366};
367const PropertyType<LuaDropdown> LuaDropdown::Properties[] = {
368 PROP_RO(LuaDropdown, name),
369 {nullptr, nullptr, nullptr},
370};
371
372/*
373 * Properties
374 */
375
376// Documented in parent Class
377int LuaDropdown::get_name(lua_State* L) {
378 lua_pushstring(L, get()->get_name());
379 return 1;
380}
381
382/*
383 * Lua Functions
384 */
385/* RST
386 .. method:: open
387
388 Open this dropdown menu.
389*/
390int LuaDropdown::open(lua_State* /* L */) {
391 log("Opening dropdown '%s'\n", get()->get_name().c_str());
392 get()->set_list_visibility(true);
393 return 0;
394}
395
396/* RST
397 .. method:: highlight_item(index)
398
399 :arg index: the index of the item to highlight, starting from ``1``
400 :type index: :class:`integer`
401
402 Highlights an item in this dropdown without triggering a selection.
403*/
404int LuaDropdown::highlight_item(lua_State* L) {
405 unsigned int desired_item = luaL_checkuint32(L, -1);
406 if (desired_item < 1 || desired_item > get()->size()) {
407 report_error(L, "Attempted to highlight item %d on dropdown '%s'. Avaliable range for this dropdown is 1-%d.", desired_item, get()->get_name().c_str(), get()->size());
408 }
409 log("Highlighting item %d in dropdown '%s'\n", desired_item, get()->get_name().c_str());
410 // Open the dropdown
411 get()->set_list_visibility(true);
412 // Press arrow down until the desired item is highlighted
413 SDL_Keysym code;
414 code.sym = SDLK_DOWN;
415 for (size_t i = 1; i < desired_item; ++i) {
416 get()->handle_key(true, code);
417 }
418 return 0;
419}
420
421/* RST
422 .. method:: select()
423
424 Selects the currently highlighted item in this dropdown.
425*/
426int LuaDropdown::select(lua_State* /* L */) {
427 log("Selecting current item in dropdown '%s'\n", get()->get_name().c_str());
428 SDL_Keysym code;
429 code.sym = SDLK_RETURN;
430 get()->handle_key(true, code);
431 return 0;
432}
433
337/*434/*
338 * C Functions435 * C Functions
339 */436 */
@@ -388,6 +485,7 @@
388 Click this tab making it the active one.485 Click this tab making it the active one.
389*/486*/
390int LuaTab::click(lua_State* /* L */) {487int LuaTab::click(lua_State* /* L */) {
488 log("Clicking tab '%s'\n", get()->get_name().c_str());
391 get()->activate();489 get()->activate();
392 return 0;490 return 0;
393}491}
@@ -437,6 +535,7 @@
437 not use it any longer.535 not use it any longer.
438*/536*/
439int LuaWindow::close(lua_State* /* L */) {537int LuaWindow::close(lua_State* /* L */) {
538 log("Closing window '%s'\n", get()->get_name().c_str());
440 delete panel_;539 delete panel_;
441 panel_ = nullptr;540 panel_ = nullptr;
442 return 0;541 return 0;
@@ -789,6 +888,10 @@
789 add_parent<LuaButton, LuaPanel>(L);888 add_parent<LuaButton, LuaPanel>(L);
790 lua_pop(L, 1); // Pop the meta table889 lua_pop(L, 1); // Pop the meta table
791890
891 register_class<LuaDropdown>(L, "ui", true);
892 add_parent<LuaDropdown, LuaPanel>(L);
893 lua_pop(L, 1); // Pop the meta table
894
792 register_class<LuaTab>(L, "ui", true);895 register_class<LuaTab>(L, "ui", true);
793 add_parent<LuaTab, LuaPanel>(L);896 add_parent<LuaTab, LuaPanel>(L);
794 lua_pop(L, 1); // Pop the meta table897 lua_pop(L, 1); // Pop the meta table
795898
=== modified file 'src/scripting/lua_ui.h'
--- src/scripting/lua_ui.h 2019-02-23 11:00:49 +0000
+++ src/scripting/lua_ui.h 2019-06-24 16:19:42 +0000
@@ -23,6 +23,7 @@
23#include "scripting/lua.h"23#include "scripting/lua.h"
24#include "scripting/luna.h"24#include "scripting/luna.h"
25#include "ui_basic/button.h"25#include "ui_basic/button.h"
26#include "ui_basic/dropdown.h"
26#include "ui_basic/tabpanel.h"27#include "ui_basic/tabpanel.h"
27#include "ui_basic/window.h"28#include "ui_basic/window.h"
28#include "wui/interactive_base.h"29#include "wui/interactive_base.h"
@@ -57,7 +58,7 @@
57 }58 }
5859
59 void __persist(lua_State* L) override {60 void __persist(lua_State* L) override {
60 report_error(L, "Trying to persist a User Interface Panel which is no supported!");61 report_error(L, "Trying to persist a User Interface Panel which is not supported!");
61 }62 }
62 void __unpersist(lua_State* L) override {63 void __unpersist(lua_State* L) override {
63 report_error(L, "Trying to unpersist a User Interface Panel which is "64 report_error(L, "Trying to unpersist a User Interface Panel which is "
@@ -68,6 +69,7 @@
68 * Properties69 * Properties
69 */70 */
70 int get_buttons(lua_State* L);71 int get_buttons(lua_State* L);
72 int get_dropdowns(lua_State* L);
71 int get_tabs(lua_State* L);73 int get_tabs(lua_State* L);
72 int get_windows(lua_State* L);74 int get_windows(lua_State* L);
73 int get_width(lua_State* L);75 int get_width(lua_State* L);
@@ -121,6 +123,41 @@
121 }123 }
122};124};
123125
126
127class LuaDropdown : public LuaPanel {
128public:
129 LUNA_CLASS_HEAD(LuaDropdown);
130
131 LuaDropdown() : LuaPanel() {
132 }
133 explicit LuaDropdown(UI::Panel* p) : LuaPanel(p) {
134 }
135 explicit LuaDropdown(lua_State* L) : LuaPanel(L) {
136 }
137 ~LuaDropdown() override {
138 }
139
140 /*
141 * Properties
142 */
143 int get_name(lua_State* L);
144
145 /*
146 * Lua Methods
147 */
148 int open(lua_State* L);
149 int highlight_item(lua_State* L);
150 int select(lua_State* L);
151
152 /*
153 * C Methods
154 */
155 UI::BaseDropdown* get() {
156 return static_cast<UI::BaseDropdown*>(panel_);
157 }
158};
159
160
124class LuaTab : public LuaPanel {161class LuaTab : public LuaPanel {
125public:162public:
126 LUNA_CLASS_HEAD(LuaTab);163 LUNA_CLASS_HEAD(LuaTab);
127164
=== modified file 'src/ui_basic/button.cc'
--- src/ui_basic/button.cc 2019-05-26 17:21:15 +0000
+++ src/ui_basic/button.cc 2019-06-24 16:19:42 +0000
@@ -216,7 +216,7 @@
216 }216 }
217 }217 }
218218
219 } else if (title_.length()) {219 } else if (!title_.empty()) {
220 // Otherwise draw title string centered220 // Otherwise draw title string centered
221 std::shared_ptr<const UI::RenderedText> rendered_text = autofit_text(221 std::shared_ptr<const UI::RenderedText> rendered_text = autofit_text(
222 richtext_escape(title_), style_to_use.font(), get_inner_w() - 2 * kButtonImageMargin);222 richtext_escape(title_), style_to_use.font(), get_inner_w() - 2 * kButtonImageMargin);
@@ -285,7 +285,6 @@
285 time_nextact_ = time;285 time_nextact_ = time;
286 play_click();286 play_click();
287 sigclicked();287 sigclicked();
288 clicked();
289 // The button may not exist at this point (for example if the button288 // The button may not exist at this point (for example if the button
290 // closed the dialog that it is part of). So member variables may no289 // closed the dialog that it is part of). So member variables may no
291 // longer be accessed.290 // longer be accessed.
@@ -339,7 +338,6 @@
339 if (highlighted_ && enabled_) {338 if (highlighted_ && enabled_) {
340 play_click();339 play_click();
341 sigclicked();340 sigclicked();
342 clicked();
343 // The button may not exist at this point (for example if the button341 // The button may not exist at this point (for example if the button
344 // closed the dialog that it is part of). So member variables may no342 // closed the dialog that it is part of). So member variables may no
345 // longer be accessed.343 // longer be accessed.
346344
=== modified file 'src/ui_basic/button.h'
--- src/ui_basic/button.h 2019-04-17 16:52:55 +0000
+++ src/ui_basic/button.h 2019-06-24 16:19:42 +0000
@@ -156,9 +156,6 @@
156 boost::signals2::signal<void()> sigmouseout;156 boost::signals2::signal<void()> sigmouseout;
157157
158protected:158protected:
159 virtual void clicked() {
160 } /// Override this to react on the click.
161
162 bool highlighted_; // mouse is over the button159 bool highlighted_; // mouse is over the button
163 bool pressed_; // mouse is clicked over the button160 bool pressed_; // mouse is clicked over the button
164 bool enabled_;161 bool enabled_;
165162
=== modified file 'src/ui_basic/checkbox.cc'
--- src/ui_basic/checkbox.cc 2019-05-26 17:21:15 +0000
+++ src/ui_basic/checkbox.cc 2019-06-24 16:19:42 +0000
@@ -177,7 +177,7 @@
177 */177 */
178bool Statebox::handle_mousepress(const uint8_t btn, int32_t, int32_t) {178bool Statebox::handle_mousepress(const uint8_t btn, int32_t, int32_t) {
179 if (btn == SDL_BUTTON_LEFT && (flags_ & Is_Enabled)) {179 if (btn == SDL_BUTTON_LEFT && (flags_ & Is_Enabled)) {
180 clicked();180 button_clicked();
181 return true;181 return true;
182 }182 }
183 return false;183 return false;
@@ -190,7 +190,7 @@
190/**190/**
191 * Toggle the checkbox state191 * Toggle the checkbox state
192 */192 */
193void Checkbox::clicked() {193void Checkbox::button_clicked() {
194 clickedto(!get_state());194 clickedto(!get_state());
195 set_state(!get_state());195 set_state(!get_state());
196 play_click();196 play_click();
197197
=== modified file 'src/ui_basic/checkbox.h'
--- src/ui_basic/checkbox.h 2019-02-23 11:00:49 +0000
+++ src/ui_basic/checkbox.h 2019-06-24 16:19:42 +0000
@@ -74,7 +74,7 @@
7474
75private:75private:
76 void layout() override;76 void layout() override;
77 virtual void clicked() = 0;77 virtual void button_clicked() = 0;
7878
79 enum Flags {79 enum Flags {
80 Is_Highlighted = 0x01,80 Is_Highlighted = 0x01,
@@ -131,7 +131,7 @@
131 }131 }
132132
133private:133private:
134 void clicked() override;134 void button_clicked() override;
135};135};
136} // namespace UI136} // namespace UI
137137
138138
=== modified file 'src/ui_basic/dropdown.cc'
--- src/ui_basic/dropdown.cc 2019-05-26 17:21:15 +0000
+++ src/ui_basic/dropdown.cc 2019-06-24 16:19:42 +0000
@@ -27,7 +27,6 @@
27#include "base/macros.h"27#include "base/macros.h"
28#include "graphic/align.h"28#include "graphic/align.h"
29#include "graphic/font_handler.h"29#include "graphic/font_handler.h"
30#include "graphic/graphic.h"
31#include "graphic/rendertarget.h"30#include "graphic/rendertarget.h"
32#include "graphic/text_layout.h"31#include "graphic/text_layout.h"
33#include "ui_basic/mouse_constants.h"32#include "ui_basic/mouse_constants.h"
@@ -53,30 +52,30 @@
5352
54int BaseDropdown::next_id_ = 0;53int BaseDropdown::next_id_ = 0;
5554
56BaseDropdown::BaseDropdown(UI::Panel* parent,55BaseDropdown::BaseDropdown(UI::Panel* parent, const std::string& name,
57 int32_t x,56 int32_t x,
58 int32_t y,57 int32_t y,
59 uint32_t w,58 uint32_t w,
60 uint32_t h,59 uint32_t max_list_items,
61 int button_dimension,60 int button_dimension,
62 const std::string& label,61 const std::string& label,
63 const DropdownType type,62 const DropdownType type,
64 UI::PanelStyle style)63 UI::PanelStyle style, ButtonStyle button_style)
65 : UI::Panel(parent,64 : UI::NamedPanel(parent,
65 name,
66 x,66 x,
67 y,67 y,
68 type == DropdownType::kPictorial ? button_dimension : w,68 (type == DropdownType::kPictorial || type == DropdownType::kPictorialMenu) ? button_dimension : w,
69 // Height only to fit the button, so we can use this in Box layout.69 // Height only to fit the button, so we can use this in Box layout.
70 base_height(button_dimension, style)),70 base_height(button_dimension, style)),
71 id_(next_id_++),71 id_(next_id_++),
72 max_list_height_(h - 2 * get_h()),72 max_list_items_(max_list_items),
73 list_width_(w),73 max_list_height_(std::numeric_limits<uint32_t>::max()),
74 list_offset_x_(0),74 list_offset_x_(0),
75 list_offset_y_(0),75 list_offset_y_(0),
76 button_dimension_(button_dimension),
77 base_height_(base_height(button_dimension, style)),76 base_height_(base_height(button_dimension, style)),
78 mouse_tolerance_(50),77 mouse_tolerance_(50),
79 button_box_(this, 0, 0, UI::Box::Horizontal, w, h),78 button_box_(this, 0, 0, UI::Box::Horizontal, w, get_h()),
80 push_button_(type == DropdownType::kTextual ?79 push_button_(type == DropdownType::kTextual ?
81 new UI::Button(&button_box_,80 new UI::Button(&button_box_,
82 "dropdown_select",81 "dropdown_select",
@@ -84,9 +83,7 @@
84 0,83 0,
85 button_dimension,84 button_dimension,
86 get_h(),85 get_h(),
87 style == UI::PanelStyle::kFsMenu ?86 button_style,
88 UI::ButtonStyle::kFsMenuMenu :
89 UI::ButtonStyle::kWuiSecondary,
90 g_gr->images().get("images/ui_basic/scrollbar_down.png")) :87 g_gr->images().get("images/ui_basic/scrollbar_down.png")) :
91 nullptr),88 nullptr),
92 display_button_(&button_box_,89 display_button_(&button_box_,
@@ -97,8 +94,10 @@
97 w - button_dimension :94 w - button_dimension :
98 type == DropdownType::kTextualNarrow ? w : button_dimension,95 type == DropdownType::kTextualNarrow ? w : button_dimension,
99 get_h(),96 get_h(),
100 style == UI::PanelStyle::kFsMenu ? UI::ButtonStyle::kFsMenuSecondary :97 type == DropdownType::kTextual ?
101 UI::ButtonStyle::kWuiSecondary,98 (style == UI::PanelStyle::kFsMenu ? UI::ButtonStyle::kFsMenuSecondary :
99 UI::ButtonStyle::kWuiSecondary) :
100 button_style,
102 label),101 label),
103 label_(label),102 label_(label),
104 type_(type),103 type_(type),
@@ -110,26 +109,30 @@
110 }109 }
111110
112 // Close whenever another dropdown is opened111 // Close whenever another dropdown is opened
113 subscriber_ = Notifications::subscribe<NoteDropdown>([this](const NoteDropdown& note) {112 dropdown_subscriber_ = Notifications::subscribe<NoteDropdown>([this](const NoteDropdown& note) {
114 if (id_ != note.id) {113 if (id_ != note.id) {
115 close();114 close();
116 }115 }
117 });116 });
117 graphic_resolution_changed_subscriber_ = Notifications::subscribe<GraphicResolutionChanged>(
118 [this](const GraphicResolutionChanged&) {
119 layout();
120 });
118121
119 assert(max_list_height_ > 0);122 assert(max_list_items_ > 0);
120 // Hook into highest parent that we can get so that we can drop down outside the panel.123 // Hook into highest parent that we can get so that we can drop down outside the panel.
121 // Positioning breaks down with TabPanels, so we exclude them.124 UI::Panel* list_parent = &display_button_;
122 while (parent->get_parent() && !is_a(UI::TabPanel, parent->get_parent())) {125 while (list_parent->get_parent()) {
123 parent = parent->get_parent();126 list_parent = list_parent->get_parent();
124 }127 }
125 list_ = new UI::Listselect<uintptr_t>(parent, 0, 0, w, 0, style, ListselectLayout::kDropdown);128 list_ = new UI::Listselect<uintptr_t>(list_parent, 0, 0, w, 0, style, ListselectLayout::kDropdown);
126129
127 list_->set_visible(false);130 list_->set_visible(false);
128 button_box_.add(&display_button_);131 button_box_.add(&display_button_, UI::Box::Resizing::kExpandBoth);
129 display_button_.sigclicked.connect(boost::bind(&BaseDropdown::toggle_list, this));132 display_button_.sigclicked.connect(boost::bind(&BaseDropdown::toggle_list, this));
130 if (push_button_ != nullptr) {133 if (push_button_ != nullptr) {
131 display_button_.set_perm_pressed(true);134 display_button_.set_perm_pressed(true);
132 button_box_.add(push_button_);135 button_box_.add(push_button_, UI::Box::Resizing::kFullSize);
133 push_button_->sigclicked.connect(boost::bind(&BaseDropdown::toggle_list, this));136 push_button_->sigclicked.connect(boost::bind(&BaseDropdown::toggle_list, this));
134 }137 }
135 button_box_.set_size(w, get_h());138 button_box_.set_size(w, get_h());
@@ -138,26 +141,19 @@
138 set_can_focus(true);141 set_can_focus(true);
139 set_value();142 set_value();
140143
141 // Find parent windows so that we can move the list along with them144 // Find parent windows, boxes etc. so that we can move the list along with them
142 UI::Panel* parent_window_candidate = get_parent();145 UI::Panel* ancestor = this;
143 while (parent_window_candidate) {146 while ((ancestor = ancestor->get_parent()) != nullptr) {
144 if (upcast(UI::Window, window, parent_window_candidate)) {147 ancestor->position_changed.connect([this] {layout(); });
145 window->position_changed.connect(boost::bind(&BaseDropdown::layout, this));
146 }
147 parent_window_candidate = parent_window_candidate->get_parent();
148 }148 }
149
150 layout();149 layout();
151}150}
152151
153BaseDropdown::~BaseDropdown() {152BaseDropdown::~BaseDropdown() {
154 // The list needs to be able to drop outside of windows, so it won't close with the window.153 // The list needs to be able to drop outside of windows, so it won't close with the window.
155 // Deleting here leads to conflict with who gets to delete it, so we hide it instead.154 // Deleting here leads to a conflict as to who gets to delete it, so we just leave it.
155 // It will be hidden as soon as the mouse moves away anyway.
156 // TODO(GunChleoc): Investigate whether we can find a better solution for this156 // TODO(GunChleoc): Investigate whether we can find a better solution for this
157 if (list_) {
158 list_->clear();
159 list_->set_visible(false);
160 }
161}157}
162158
163void BaseDropdown::set_height(int height) {159void BaseDropdown::set_height(int height) {
@@ -165,27 +161,19 @@
165 layout();161 layout();
166}162}
167163
168void BaseDropdown::set_max_items(int items) {
169 set_height(list_->get_lineheight() * items + base_height_);
170}
171
172void BaseDropdown::layout() {164void BaseDropdown::layout() {
173 const int base_h = base_height_;165 int list_width = list_->calculate_desired_width();
174 const int w = type_ == DropdownType::kPictorial ? button_dimension_ : get_w();166
175 button_box_.set_size(w, base_h);167 const int new_list_height =
176 display_button_.set_desired_size(168 std::min(max_list_height_ / list_->get_lineheight(), std::min(list_->size(), max_list_items_)) * list_->get_lineheight();
177 type_ == DropdownType::kTextual ? w - button_dimension_ : w, base_h);169 list_->set_size(std::max(list_width, button_box_.get_w()), new_list_height);
178 int new_list_height =
179 std::min(static_cast<int>(list_->size()) * list_->get_lineheight(), max_list_height_);
180 list_->set_size(type_ != DropdownType::kPictorial ? w : list_width_, new_list_height);
181 set_desired_size(w, base_h);
182170
183 // Update list position. The list is hooked into the highest parent that we can get so that we171 // Update list position. The list is hooked into the highest parent that we can get so that we
184 // can drop down outside the panel. Positioning breaks down with TabPanels, so we exclude them.172 // can drop down outside the panel.
185 UI::Panel* parent = get_parent();173 UI::Panel* parent = &display_button_;
186 int new_list_x = get_x() + parent->get_x() + parent->get_lborder();174 int new_list_x = display_button_.get_x();
187 int new_list_y = get_y() + parent->get_y() + parent->get_tborder();175 int new_list_y = display_button_.get_y();
188 while (parent->get_parent() && !is_a(UI::TabPanel, parent->get_parent())) {176 while (parent->get_parent()) {
189 parent = parent->get_parent();177 parent = parent->get_parent();
190 new_list_x += parent->get_x() + parent->get_lborder();178 new_list_x += parent->get_x() + parent->get_lborder();
191 new_list_y += parent->get_y() + parent->get_tborder();179 new_list_y += parent->get_y() + parent->get_tborder();
@@ -217,13 +205,24 @@
217 }205 }
218}206}
219207
208void BaseDropdown::set_size(int nw, int nh) {
209 button_box_.set_size(nw, nh);
210 Panel::set_size(nw, nh);
211 layout();
212}
213void BaseDropdown::set_desired_size(int nw, int nh) {
214 button_box_.set_desired_size(nw, nh);
215 Panel::set_desired_size(nw, nh);
216 layout();
217}
218
220void BaseDropdown::add(const std::string& name,219void BaseDropdown::add(const std::string& name,
221 const uint32_t value,220 const uint32_t value,
222 const Image* pic,221 const Image* pic,
223 const bool select_this,222 const bool select_this,
224 const std::string& tooltip_text) {223 const std::string& tooltip_text, const std::string& hotkey = std::string()) {
225 assert(pic != nullptr || type_ != DropdownType::kPictorial);224 assert(pic != nullptr || type_ != DropdownType::kPictorial);
226 list_->add(name, value, pic, select_this, tooltip_text);225 list_->add(name, value, pic, select_this, tooltip_text, hotkey);
227 if (select_this) {226 if (select_this) {
228 set_value();227 set_value();
229 }228 }
@@ -248,7 +247,7 @@
248247
249void BaseDropdown::set_label(const std::string& text) {248void BaseDropdown::set_label(const std::string& text) {
250 label_ = text;249 label_ = text;
251 if (type_ != DropdownType::kPictorial) {250 if (type_ != DropdownType::kPictorial && type_ != DropdownType::kPictorialMenu) {
252 display_button_.set_title(label_);251 display_button_.set_title(label_);
253 }252 }
254}253}
@@ -267,7 +266,7 @@
267266
268void BaseDropdown::set_errored(const std::string& error_message) {267void BaseDropdown::set_errored(const std::string& error_message) {
269 set_tooltip((boost::format(_("%1%: %2%")) % _("Error") % error_message).str());268 set_tooltip((boost::format(_("%1%: %2%")) % _("Error") % error_message).str());
270 if (type_ != DropdownType::kPictorial) {269 if (type_ != DropdownType::kPictorial && type_ != DropdownType::kPictorialMenu) {
271 set_label(_("Error"));270 set_label(_("Error"));
272 } else {271 } else {
273 set_image(g_gr->images().get("images/ui_basic/different.png"));272 set_image(g_gr->images().get("images/ui_basic/different.png"));
@@ -295,7 +294,7 @@
295294
296void BaseDropdown::set_pos(Vector2i point) {295void BaseDropdown::set_pos(Vector2i point) {
297 UI::Panel::set_pos(point);296 UI::Panel::set_pos(point);
298 list_->set_pos(Vector2i(point.x, point.y + get_h()));297 layout();
299}298}
300299
301void BaseDropdown::clear() {300void BaseDropdown::clear() {
@@ -321,6 +320,11 @@
321}320}
322321
323void BaseDropdown::update() {322void BaseDropdown::update() {
323 if (type_ == DropdownType::kPictorialMenu) {
324 // Menus never change their main image and text
325 return;
326 }
327
324 const std::string name = list_->has_selection() ?328 const std::string name = list_->has_selection() ?
325 list_->get_selected_name() :329 list_->get_selected_name() :
326 /** TRANSLATORS: Selection in Dropdown menus. */330 /** TRANSLATORS: Selection in Dropdown menus. */
@@ -349,6 +353,34 @@
349 current_selection_ = list_->selection_index();353 current_selection_ = list_->selection_index();
350}354}
351355
356void BaseDropdown::toggle() {
357 set_list_visibility(!list_->is_visible());
358}
359
360void BaseDropdown::set_list_visibility(bool open) {
361 if (!is_enabled_) {
362 list_->set_visible(false);
363 return;
364 }
365 list_->set_visible(open);
366 if (list_->is_visible()) {
367 list_->move_to_top();
368 focus();
369 set_mouse_pos(
370 Vector2i(
371 display_button_.get_x() + (display_button_.get_w() * 3 / 5),
372 display_button_.get_y() + (display_button_.get_h() * 2 / 5)));
373 if (type_ == DropdownType::kPictorialMenu && !has_selection() && !list_->empty()) {
374 select(0);
375 }
376 }
377 if (type_ != DropdownType::kTextual) {
378 display_button_.set_perm_pressed(list_->is_visible());
379 }
380 // Make sure that the list covers and deactivates the elements below it
381 set_layout_toplevel(list_->is_visible());
382}
383
352void BaseDropdown::toggle_list() {384void BaseDropdown::toggle_list() {
353 if (!is_enabled_) {385 if (!is_enabled_) {
354 list_->set_visible(false);386 list_->set_visible(false);
@@ -387,6 +419,7 @@
387 case SDLK_RETURN:419 case SDLK_RETURN:
388 if (list_->is_visible()) {420 if (list_->is_visible()) {
389 set_value();421 set_value();
422 return true;
390 }423 }
391 break;424 break;
392 case SDLK_ESCAPE:425 case SDLK_ESCAPE:
@@ -397,6 +430,7 @@
397 }430 }
398 break;431 break;
399 case SDLK_DOWN:432 case SDLK_DOWN:
433 case SDLK_UP:
400 if (!list_->is_visible() && !is_mouse_away()) {434 if (!list_->is_visible() && !is_mouse_away()) {
401 toggle_list();435 toggle_list();
402 return true;436 return true;
403437
=== modified file 'src/ui_basic/dropdown.h'
--- src/ui_basic/dropdown.h 2019-04-17 16:52:55 +0000
+++ src/ui_basic/dropdown.h 2019-06-24 16:19:42 +0000
@@ -25,6 +25,7 @@
2525
26#include <boost/signals2.hpp>26#include <boost/signals2.hpp>
2727
28#include "graphic/graphic.h"
28#include "graphic/image.h"29#include "graphic/image.h"
29#include "notifications/note_ids.h"30#include "notifications/note_ids.h"
30#include "notifications/notifications.h"31#include "notifications/notifications.h"
@@ -44,31 +45,35 @@
44 }45 }
45};46};
4647
47/// The narrow textual dropdown omits the extra push button48/// The narrow textual dropdown omits the extra push button.
48enum class DropdownType { kTextual, kTextualNarrow, kPictorial };49/// Use kPictorialMenu if you want to trigger an action without changing the menu button.
50enum class DropdownType { kTextual, kTextualNarrow, kPictorial, kPictorialMenu };
4951
50/// Implementation for a dropdown menu that lets the user select a value.52/// Implementation for a dropdown menu that lets the user select a value.
51class BaseDropdown : public Panel {53class BaseDropdown : public NamedPanel {
52protected:54protected:
53 /// \param parent the parent panel55 /// \param parent the parent panel
56 /// \param name a name so that we can reference the dropdown via Lua
54 /// \param x the x-position within 'parent'57 /// \param x the x-position within 'parent'
55 /// \param y the y-position within 'parent'58 /// \param y the y-position within 'parent'
56 /// \param list_w the dropdown's width59 /// \param list_w the dropdown's width
57 /// \param list_h the maximum height for the dropdown list60 /// \param max_list_items the maximum number of items shown in the list before it starts using a scrollbar
58 /// \param button_dimension the width of the push button in textual dropdowns. For pictorial61 /// \param button_dimension the width of the push button in textual dropdowns. For pictorial
59 /// dropdowns, this is both the width and the height of the button.62 /// dropdowns, this is both the width and the height of the button.
60 /// \param label a label to prefix to the selected entry on the display button.63 /// \param label a label to prefix to the selected entry on the display button.
61 /// \param type whether this is a textual or pictorial dropdown64 /// \param type whether this is a textual or pictorial dropdown
62 /// \param style the style used for buttons and background65 /// \param style the style used for buttons and background
63 BaseDropdown(Panel* parent,66 BaseDropdown(Panel* parent,
67 const std::string& name,
64 int32_t x,68 int32_t x,
65 int32_t y,69 int32_t y,
66 uint32_t list_w,70 uint32_t list_w,
67 uint32_t list_h,71 uint32_t max_list_items,
68 int button_dimension,72 int button_dimension,
69 const std::string& label,73 const std::string& label,
70 const DropdownType type,74 const DropdownType type,
71 PanelStyle style);75 PanelStyle style,
76 ButtonStyle button_style);
72 ~BaseDropdown() override;77 ~BaseDropdown() override;
7378
74public:79public:
@@ -120,10 +125,20 @@
120 /// Handle keypresses125 /// Handle keypresses
121 bool handle_key(bool down, SDL_Keysym code) override;126 bool handle_key(bool down, SDL_Keysym code) override;
122127
128 /// Set maximum available height in the UI
123 void set_height(int height);129 void set_height(int height);
124130
125 /// Set the number of items to fit in the list131 /// Toggle the list on and off and position the mouse on the button so that the dropdown won't close on us.
126 void set_max_items(int items);132 /// If this is a menu and nothing was selected yet, select the first item for easier keyboard navigation.
133 void toggle();
134
135 /// If 'open', show the list and position the mouse on the button so that the dropdown won't close on us.
136 /// If this is a menu and nothing was selected yet, select the first item for easier keyboard navigation.
137 /// If not 'open', close the list.
138 void set_list_visibility(bool open);
139
140 void set_size(int nw, int nh) override;
141 void set_desired_size(int w, int h) override;
127142
128protected:143protected:
129 /// Add an element to the list144 /// Add an element to the list
@@ -132,13 +147,14 @@
132 /// \param pic an image to illustrate the entry. Can be nullptr for textual dropdowns.147 /// \param pic an image to illustrate the entry. Can be nullptr for textual dropdowns.
133 /// \param select_this whether this element should be selected148 /// \param select_this whether this element should be selected
134 /// \param tooltip_text a tooltip for this entry149 /// \param tooltip_text a tooltip for this entry
150 /// \param hotkey a hotkey tip if any
135 ///151 ///
136 /// Text conventions: Title Case for the 'name', Sentence case for the 'tooltip_text'152 /// Text conventions: Title Case for the 'name', Sentence case for the 'tooltip_text'
137 void add(const std::string& name,153 void add(const std::string& name,
138 uint32_t value,154 uint32_t value,
139 const Image* pic = nullptr,155 const Image* pic,
140 const bool select_this = false,156 const bool select_this,
141 const std::string& tooltip_text = std::string());157 const std::string& tooltip_text, const std::string& hotkey);
142158
143 /// \return the index of the selected element159 /// \return the index of the selected element
144 uint32_t get_selected() const;160 uint32_t get_selected() const;
@@ -161,7 +177,7 @@
161177
162 /// Updates the title and tooltip of the display button and triggers a 'selected' signal.178 /// Updates the title and tooltip of the display button and triggers a 'selected' signal.
163 void set_value();179 void set_value();
164 /// Toggles the dropdown list on and off.180 /// Toggles the dropdown list on and off and sends a notification if the list is visible afterwards.
165 void toggle_list();181 void toggle_list();
166 /// Toggle the list closed if the dropdown is currently expanded.182 /// Toggle the list closed if the dropdown is currently expanded.
167 void close();183 void close();
@@ -172,14 +188,14 @@
172 /// Give each dropdown a unique ID188 /// Give each dropdown a unique ID
173 static int next_id_;189 static int next_id_;
174 const int id_;190 const int id_;
175 std::unique_ptr<Notifications::Subscriber<NoteDropdown>> subscriber_;191 std::unique_ptr<Notifications::Subscriber<NoteDropdown>> dropdown_subscriber_;
192 std::unique_ptr<Notifications::Subscriber<GraphicResolutionChanged>> graphic_resolution_changed_subscriber_;
176193
177 // Dimensions194 // Dimensions
178 int max_list_height_;195 unsigned int max_list_items_;
179 int list_width_;196 unsigned int max_list_height_;
180 int list_offset_x_;197 int list_offset_x_;
181 int list_offset_y_;198 int list_offset_y_;
182 const int button_dimension_;
183 const int base_height_;199 const int base_height_;
184 const int mouse_tolerance_; // Allow mouse outside the panel a bit before autocollapse200 const int mouse_tolerance_; // Allow mouse outside the panel a bit before autocollapse
185 UI::Box button_box_;201 UI::Box button_box_;
@@ -199,10 +215,11 @@
199template <typename Entry> class Dropdown : public BaseDropdown {215template <typename Entry> class Dropdown : public BaseDropdown {
200public:216public:
201 /// \param parent the parent panel217 /// \param parent the parent panel
218 /// \param name a name so that we can reference the dropdown via Lua
202 /// \param x the x-position within 'parent'219 /// \param x the x-position within 'parent'
203 /// \param y the y-position within 'parent'220 /// \param y the y-position within 'parent'
204 /// \param list_w the dropdown's width221 /// \param list_w the dropdown's width
205 /// \param list_h the maximum height for the dropdown list222 /// \param max_list_items the maximum number of items shown in the list before it starts using a scrollbar
206 /// \param button_dimension the width of the push button in textual dropdowns. For pictorial223 /// \param button_dimension the width of the push button in textual dropdowns. For pictorial
207 /// dropdowns, this is both the width and the height of the button.224 /// dropdowns, this is both the width and the height of the button.
208 /// \param label a label to prefix to the selected entry on the display button.225 /// \param label a label to prefix to the selected entry on the display button.
@@ -210,15 +227,17 @@
210 /// \param style the style used for buttons and background227 /// \param style the style used for buttons and background
211 /// Text conventions: Title Case for all elements228 /// Text conventions: Title Case for all elements
212 Dropdown(Panel* parent,229 Dropdown(Panel* parent,
230 const std::string& name,
213 int32_t x,231 int32_t x,
214 int32_t y,232 int32_t y,
215 uint32_t list_w,233 uint32_t list_w,
216 uint32_t list_h,234 uint32_t max_list_items,
217 int button_dimension,235 int button_dimension,
218 const std::string& label,236 const std::string& label,
219 const DropdownType type,237 const DropdownType type,
220 PanelStyle style)238 PanelStyle style,
221 : BaseDropdown(parent, x, y, list_w, list_h, button_dimension, label, type, style) {239 ButtonStyle button_style)
240 : BaseDropdown(parent, name, x, y, list_w, max_list_items, button_dimension, label, type, style, button_style) {
222 }241 }
223 ~Dropdown() {242 ~Dropdown() {
224 entry_cache_.clear();243 entry_cache_.clear();
@@ -231,13 +250,14 @@
231 /// only.250 /// only.
232 /// \param select_this whether this element should be selected251 /// \param select_this whether this element should be selected
233 /// \param tooltip_text a tooltip for this entry252 /// \param tooltip_text a tooltip for this entry
253 /// \param hotkey a hotkey tip if any
234 void add(const std::string& name,254 void add(const std::string& name,
235 Entry value,255 Entry value,
236 const Image* pic = nullptr,256 const Image* pic = nullptr,
237 const bool select_this = false,257 const bool select_this = false,
238 const std::string& tooltip_text = std::string()) {258 const std::string& tooltip_text = std::string(), const std::string& hotkey = std::string()) {
239 entry_cache_.push_back(std::unique_ptr<Entry>(new Entry(value)));259 entry_cache_.push_back(std::unique_ptr<Entry>(new Entry(value)));
240 BaseDropdown::add(name, size(), pic, select_this, tooltip_text);260 BaseDropdown::add(name, size(), pic, select_this, tooltip_text, hotkey);
241 }261 }
242262
243 /// \return the selected element263 /// \return the selected element
244264
=== modified file 'src/ui_basic/icongrid.cc'
--- src/ui_basic/icongrid.cc 2019-02-23 11:00:49 +0000
+++ src/ui_basic/icongrid.cc 2019-06-24 16:19:42 +0000
@@ -104,7 +104,7 @@
104}104}
105105
106void IconGrid::clicked_button(uint32_t idx) {106void IconGrid::clicked_button(uint32_t idx) {
107 clicked(idx);107 icon_clicked(idx);
108 play_click();108 play_click();
109}109}
110110
111111
=== modified file 'src/ui_basic/icongrid.h'
--- src/ui_basic/icongrid.h 2019-02-23 11:00:49 +0000
+++ src/ui_basic/icongrid.h 2019-06-24 16:19:42 +0000
@@ -38,7 +38,7 @@
38struct IconGrid : public Panel {38struct IconGrid : public Panel {
39 IconGrid(Panel* parent, int32_t x, int32_t y, int32_t cellw, int32_t cellh, int32_t cols);39 IconGrid(Panel* parent, int32_t x, int32_t y, int32_t cellw, int32_t cellh, int32_t cols);
4040
41 boost::signals2::signal<void(int32_t)> clicked;41 boost::signals2::signal<void(int32_t)> icon_clicked;
42 boost::signals2::signal<void(int32_t)> mouseout;42 boost::signals2::signal<void(int32_t)> mouseout;
43 boost::signals2::signal<void(int32_t)> mousein;43 boost::signals2::signal<void(int32_t)> mousein;
4444
4545
=== modified file 'src/ui_basic/listselect.cc'
--- src/ui_basic/listselect.cc 2019-05-26 17:21:15 +0000
+++ src/ui_basic/listselect.cc 2019-06-24 16:19:42 +0000
@@ -34,8 +34,25 @@
34#include "ui_basic/mouse_constants.h"34#include "ui_basic/mouse_constants.h"
3535
36constexpr int kMargin = 2;36constexpr int kMargin = 2;
37constexpr int kHotkeyGap = 16;
3738
38namespace UI {39namespace UI {
40
41
42BaseListselect::EntryRecord::EntryRecord(const std::string& init_name,
43 uint32_t init_entry,
44 const Image* init_pic,
45 const std::string& tooltip_text, const std::string& hotkey_text, const TableStyleInfo& style) :
46 name(init_name),
47 entry_(init_entry),
48 pic(init_pic),
49 tooltip(tooltip_text),
50 name_alignment(i18n::has_rtl_character(init_name.c_str(), 20) ? Align::kRight : Align::kLeft),
51 hotkey_alignment(i18n::has_rtl_character(hotkey_text.c_str(), 20) ? Align::kRight : Align::kLeft) {
52 rendered_name = UI::g_fh->render(as_richtext_paragraph(richtext_escape(name), style.enabled()));
53 rendered_hotkey = UI::g_fh->render(as_richtext_paragraph(richtext_escape(hotkey_text), style.hotkey()));
54}
55
39/**56/**
40 * Initialize a list select panel57 * Initialize a list select panel
41 *58 *
@@ -53,17 +70,19 @@
53 UI::PanelStyle style,70 UI::PanelStyle style,
54 const ListselectLayout selection_mode)71 const ListselectLayout selection_mode)
55 : Panel(parent, x, y, w, h),72 : Panel(parent, x, y, w, h),
73 widest_text_(0),
74 widest_hotkey_(0),
56 scrollbar_(this, get_w() - Scrollbar::kSize, 0, Scrollbar::kSize, h, style),75 scrollbar_(this, get_w() - Scrollbar::kSize, 0, Scrollbar::kSize, h, style),
57 scrollpos_(0),76 scrollpos_(0),
58 selection_(no_selection_index()),77 selection_(no_selection_index()),
59 last_click_time_(-10000),78 last_click_time_(-10000),
60 last_selection_(no_selection_index()),79 last_selection_(no_selection_index()),
61 selection_mode_(selection_mode),80 selection_mode_(selection_mode),
62 font_style_(&g_gr->styles().table_style(style).enabled()),81 table_style_(g_gr->styles().table_style(style)),
63 background_style_(selection_mode == ListselectLayout::kDropdown ?82 background_style_(selection_mode == ListselectLayout::kDropdown ?
64 g_gr->styles().dropdown_style(style) :83 g_gr->styles().dropdown_style(style) :
65 nullptr),84 nullptr),
66 lineheight_(text_height(*font_style_) + kMargin) {85 lineheight_(text_height(table_style_.enabled()) + kMargin) {
67 set_thinks(false);86 set_thinks(false);
6887
69 scrollbar_.moved.connect(boost::bind(&BaseListselect::set_scrollpos, this, _1));88 scrollbar_.moved.connect(boost::bind(&BaseListselect::set_scrollpos, this, _1));
@@ -115,13 +134,12 @@
115 uint32_t entry,134 uint32_t entry,
116 const Image* pic,135 const Image* pic,
117 bool const sel,136 bool const sel,
118 const std::string& tooltip_text) {137 const std::string& tooltip_text, const std::string& hotkey) {
119 EntryRecord* er = new EntryRecord();138 EntryRecord* er = new EntryRecord(
139 name,
140 entry, pic, tooltip_text,
141 hotkey, table_style_);
120142
121 er->entry_ = entry;
122 er->pic = pic;
123 er->name = name;
124 er->tooltip = tooltip_text;
125 int entry_height = lineheight_;143 int entry_height = lineheight_;
126 if (pic) {144 if (pic) {
127 int w = pic->width();145 int w = pic->width();
@@ -142,41 +160,6 @@
142 select(entry_records_.size() - 1);160 select(entry_records_.size() - 1);
143}161}
144162
145void BaseListselect::add_front(const std::string& name,
146 const Image* pic,
147 bool const sel,
148 const std::string& tooltip_text) {
149 EntryRecord* er = new EntryRecord();
150
151 er->entry_ = 0;
152 for (EntryRecord* temp_entry : entry_records_) {
153 ++(temp_entry)->entry_;
154 }
155
156 er->pic = pic;
157 er->name = name;
158 er->tooltip = tooltip_text;
159
160 int entry_height = lineheight_;
161 if (pic) {
162 int w = pic->width();
163 int h = pic->height();
164 entry_height = (h >= entry_height) ? h : entry_height;
165 if (max_pic_width_ < w)
166 max_pic_width_ = w;
167 }
168
169 if (entry_height > lineheight_)
170 lineheight_ = entry_height;
171
172 entry_records_.push_front(er);
173
174 layout();
175
176 if (sel)
177 select(0);
178}
179
180/**163/**
181 * Sort the listbox alphabetically. make sure that the current selection stays164 * Sort the listbox alphabetically. make sure that the current selection stays
182 * valid (though it might scroll out of visibility).165 * valid (though it might scroll out of visibility).
@@ -275,13 +258,45 @@
275}258}
276259
277int BaseListselect::get_lineheight() const {260int BaseListselect::get_lineheight() const {
278 return lineheight_ + kMargin;261 return lineheight_ + (selection_mode_ == ListselectLayout::kDropdown ? 2 * kMargin : kMargin);
279}262}
280263
281uint32_t BaseListselect::get_eff_w() const {264uint32_t BaseListselect::get_eff_w() const {
282 return scrollbar_.is_enabled() ? get_w() - scrollbar_.get_w() : get_w();265 return scrollbar_.is_enabled() ? get_w() - scrollbar_.get_w() : get_w();
283}266}
284267
268// Make enough room for all texts + hotkeys in tabular format
269int BaseListselect::calculate_desired_width() {
270 if (entry_records_.empty()) {
271 return 0;
272 }
273
274 // Find the widest entries
275 widest_text_ = 0;
276 widest_hotkey_ = 0;
277 for (const EntryRecord* er : entry_records_) {
278 const int current_text_width = er->rendered_name->width();
279 if (current_text_width > widest_text_) {
280 widest_text_ = current_text_width;
281 }
282 const int current_hotkey_width = er->rendered_hotkey->width();
283 if (current_hotkey_width > widest_hotkey_) {
284 widest_hotkey_ = current_hotkey_width;
285 }
286 }
287
288 // Add up the width
289 int text_width = widest_text_;
290 if (widest_hotkey_ > 0) {
291 text_width += kHotkeyGap;
292 text_width += widest_hotkey_;
293 }
294
295 const int picw = max_pic_width_ ? max_pic_width_ + 10 : 0;
296 const int old_width = get_w();
297 return text_width + picw + 8 + old_width - get_eff_w();
298}
299
285void BaseListselect::layout() {300void BaseListselect::layout() {
286 scrollbar_.set_size(scrollbar_.get_w(), get_h());301 scrollbar_.set_size(scrollbar_.get_w(), get_h());
287 scrollbar_.set_pos(Vector2i(get_w() - Scrollbar::kSize, 0));302 scrollbar_.set_pos(Vector2i(get_w() - Scrollbar::kSize, 0));
@@ -294,15 +309,9 @@
294 }309 }
295 // For dropdowns, autoincrease width310 // For dropdowns, autoincrease width
296 if (selection_mode_ == ListselectLayout::kDropdown) {311 if (selection_mode_ == ListselectLayout::kDropdown) {
297 for (size_t i = 0; i < entry_records_.size(); ++i) {312 const int new_width = calculate_desired_width();
298 const EntryRecord& er = *entry_records_[i];313 if (new_width > get_w()) {
299 std::shared_ptr<const UI::RenderedText> rendered_text =314 set_size(new_width, get_h());
300 UI::g_fh->render(as_richtext_paragraph(richtext_escape(er.name), *font_style_));
301 int picw = max_pic_width_ ? max_pic_width_ + 10 : 0;
302 int difference = rendered_text->width() + picw + 8 - get_eff_w();
303 if (difference > 0) {
304 set_size(get_w() + difference, get_h());
305 }
306 }315 }
307 }316 }
308}317}
@@ -341,10 +350,9 @@
341 assert(eff_h < std::numeric_limits<int32_t>::max());350 assert(eff_h < std::numeric_limits<int32_t>::max());
342351
343 const EntryRecord& er = *entry_records_[idx];352 const EntryRecord& er = *entry_records_[idx];
344 std::shared_ptr<const UI::RenderedText> rendered_text =353 const int text_height = std::max(er.rendered_name->height(), er.rendered_hotkey->height());
345 UI::g_fh->render(as_richtext_paragraph(richtext_escape(er.name), *font_style_));
346354
347 int lineheight = std::max(get_lineheight(), rendered_text->height());355 int lineheight = std::max(get_lineheight(), text_height);
348356
349 // Don't draw over the bottom edge357 // Don't draw over the bottom edge
350 lineheight = std::min(eff_h - y, lineheight);358 lineheight = std::min(eff_h - y, lineheight);
@@ -380,26 +388,15 @@
380 // Now draw pictures388 // Now draw pictures
381 if (er.pic) {389 if (er.pic) {
382 dst.blit(Vector2i(UI::g_fh->fontset()->is_rtl() ? get_eff_w() - er.pic->width() - 1 : 1,390 dst.blit(Vector2i(UI::g_fh->fontset()->is_rtl() ? get_eff_w() - er.pic->width() - 1 : 1,
383 y + (get_lineheight() - er.pic->height()) / 2),391 y + (lineheight_ - er.pic->height()) / 2),
384 er.pic);392 er.pic);
385 }393 }
386394
387 // Position the text according to alignment
388 Align alignment = i18n::has_rtl_character(er.name.c_str(), 20) ? Align::kRight : Align::kLeft;
389 if (alignment == UI::Align::kRight) {
390 point.x += maxw - picw;
391 }
392
393 // Shift for image width
394 if (!UI::g_fh->fontset()->is_rtl()) {
395 point.x += picw;
396 }
397
398 // Fix vertical position for mixed font heights395 // Fix vertical position for mixed font heights
399 if (get_lineheight() > rendered_text->height()) {396 if (get_lineheight() > text_height) {
400 point.y += (lineheight_ - rendered_text->height()) / 2;397 point.y += (lineheight_ - text_height) / 2;
401 } else {398 } else {
402 point.y -= (rendered_text->height() - lineheight_) / 2;399 point.y -= (text_height - lineheight_) / 2;
403 }400 }
404401
405 // Don't draw over the bottom edge402 // Don't draw over the bottom edge
@@ -407,8 +404,35 @@
407 if (lineheight < 0) {404 if (lineheight < 0) {
408 break;405 break;
409 }406 }
410 rendered_text->draw(407
411 dst, point, Recti(0, 0, maxw, lineheight), alignment, RenderedText::CropMode::kSelf);408 // Tabular layout for hotkeys + shift for image width
409 Vector2i text_point(point);
410 Vector2i hotkey_point(point);
411 if (UI::g_fh->fontset()->is_rtl()) {
412 if (er.name_alignment == UI::Align::kRight) {
413 text_point.x = maxw - widest_text_ - picw;
414 } else if (widest_hotkey_ > 0) {
415 text_point.x += widest_hotkey_ + kHotkeyGap;
416 }
417 } else {
418 hotkey_point.x = maxw - widest_hotkey_;
419 text_point.x += picw;
420 }
421
422 // Position the text and hotkey according to their alignment
423 if (er.name_alignment == UI::Align::kRight) {
424 text_point.x += widest_text_ - er.rendered_name->width();
425 }
426 if (er.hotkey_alignment == UI::Align::kRight) {
427 hotkey_point.x += widest_hotkey_ - er.rendered_hotkey->width();
428 }
429
430 er.rendered_name->draw(
431 dst, text_point, Recti(0, 0, maxw - widest_hotkey_, lineheight), UI::Align::kLeft, RenderedText::CropMode::kSelf);
432 if (er.rendered_hotkey->width() > 0) {
433 er.rendered_hotkey->draw(
434 dst, hotkey_point, Recti(0, 0, maxw - widest_text_, lineheight), UI::Align::kLeft, RenderedText::CropMode::kSelf);
435 }
412 y += get_lineheight();436 y += get_lineheight();
413 ++idx;437 ++idx;
414 }438 }
@@ -442,7 +466,7 @@
442 return false;466 return false;
443 play_click();467 play_click();
444 select(y);468 select(y);
445 clicked(selection_);469 clicked();
446470
447 if // check if doubleclicked471 if // check if doubleclicked
448 (time - real_last_click_time < DOUBLE_CLICK_INTERVAL && last_selection_ == selection_ &&472 (time - real_last_click_time < DOUBLE_CLICK_INTERVAL && last_selection_ == selection_ &&
449473
=== modified file 'src/ui_basic/listselect.h'
--- src/ui_basic/listselect.h 2019-04-19 05:59:14 +0000
+++ src/ui_basic/listselect.h 2019-06-24 16:19:42 +0000
@@ -26,7 +26,7 @@
26#include <boost/signals2.hpp>26#include <boost/signals2.hpp>
2727
28#include "graphic/color.h"28#include "graphic/color.h"
29#include "graphic/styles/font_style.h"29#include "graphic/styles/table_style.h"
30#include "ui_basic/panel.h"30#include "ui_basic/panel.h"
31#include "ui_basic/scrollbar.h"31#include "ui_basic/scrollbar.h"
3232
@@ -56,7 +56,6 @@
56 ~BaseListselect() override;56 ~BaseListselect() override;
5757
58 boost::signals2::signal<void(uint32_t)> selected;58 boost::signals2::signal<void(uint32_t)> selected;
59 boost::signals2::signal<void(uint32_t)> clicked;
60 boost::signals2::signal<void(uint32_t)> double_clicked;59 boost::signals2::signal<void(uint32_t)> double_clicked;
6160
62 void clear();61 void clear();
@@ -66,16 +65,10 @@
66 */65 */
67 void add(const std::string& name,66 void add(const std::string& name,
68 uint32_t value,67 uint32_t value,
69 const Image* pic = nullptr,68 const Image* pic,
70 const bool select_this = false,69 const bool select_this,
71 const std::string& tooltip_text = std::string());70 const std::string& tooltip_text, const std::string& hotkey);
72 /**71
73 * Text conventions: Title Case for the 'name', Sentence case for the 'tooltip_text'
74 */
75 void add_front(const std::string& name,
76 const Image* pic = nullptr,
77 const bool select_this = false,
78 const std::string& tooltip_text = std::string());
79 void remove(uint32_t);72 void remove(uint32_t);
80 void remove(const char* name);73 void remove(const char* name);
8174
@@ -113,6 +106,8 @@
113106
114 uint32_t get_eff_w() const;107 uint32_t get_eff_w() const;
115108
109 int calculate_desired_width();
110
116 void layout() override;111 void layout() override;
117112
118 // Drawing and event handling113 // Drawing and event handling
@@ -125,22 +120,32 @@
125120
126private:121private:
127 static const int32_t DOUBLE_CLICK_INTERVAL = 500; // half a second122 static const int32_t DOUBLE_CLICK_INTERVAL = 500; // half a second
123 static const int32_t ms_darken_value = -20;
128124
129 void set_scrollpos(int32_t);125 void set_scrollpos(int32_t);
130126
131private:
132 static const int32_t ms_darken_value = -20;
133
134 struct EntryRecord {127 struct EntryRecord {
135 uint32_t entry_;128 explicit EntryRecord(const std::string& init_name,
129 uint32_t init_entry,
130 const Image* init_pic,
131 const std::string& tooltip_text, const std::string& hotkey_text,
132 const UI::TableStyleInfo& style);
133
134 const std::string name;
135 const uint32_t entry_;
136 const Image* pic;136 const Image* pic;
137 std::string name;137 const std::string tooltip;
138 std::string tooltip;138 const Align name_alignment;
139 const Align hotkey_alignment;
140 std::shared_ptr<const UI::RenderedText> rendered_name;
141 std::shared_ptr<const UI::RenderedText> rendered_hotkey;
139 };142 };
140 using EntryRecordDeque = std::deque<EntryRecord*>;
141143
142 int max_pic_width_;144 int max_pic_width_;
143 EntryRecordDeque entry_records_;145 int widest_text_;
146 int widest_hotkey_;
147
148 std::deque<EntryRecord*> entry_records_;
144 Scrollbar scrollbar_;149 Scrollbar scrollbar_;
145 uint32_t scrollpos_; // in pixels150 uint32_t scrollpos_; // in pixels
146 uint32_t selection_;151 uint32_t selection_;
@@ -148,7 +153,7 @@
148 uint32_t last_selection_; // for double clicks153 uint32_t last_selection_; // for double clicks
149 ListselectLayout selection_mode_;154 ListselectLayout selection_mode_;
150 const Image* check_pic_;155 const Image* check_pic_;
151 const FontStyleInfo* font_style_;156 const UI::TableStyleInfo& table_style_;
152 const UI::PanelStyleInfo* background_style_; // Background color and texture. Not owned.157 const UI::PanelStyleInfo* background_style_; // Background color and texture. Not owned.
153 int lineheight_;158 int lineheight_;
154 std::string current_tooltip_;159 std::string current_tooltip_;
@@ -169,17 +174,9 @@
169 Entry value,174 Entry value,
170 const Image* pic = nullptr,175 const Image* pic = nullptr,
171 const bool select_this = false,176 const bool select_this = false,
172 const std::string& tooltip_text = std::string()) {177 const std::string& tooltip_text = std::string(), const std::string& hotkey = std::string()) {
173 entry_cache_.push_back(value);178 entry_cache_.push_back(value);
174 BaseListselect::add(name, entry_cache_.size() - 1, pic, select_this, tooltip_text);179 BaseListselect::add(name, entry_cache_.size() - 1, pic, select_this, tooltip_text, hotkey);
175 }
176 void add_front(const std::string& name,
177 Entry value,
178 const Image* pic = nullptr,
179 const bool select_this = false,
180 const std::string& tooltip_text = std::string()) {
181 entry_cache_.push_front(value);
182 BaseListselect::add_front(name, pic, select_this, tooltip_text);
183 }180 }
184181
185 const Entry& operator[](uint32_t const i) const {182 const Entry& operator[](uint32_t const i) const {
@@ -218,15 +215,8 @@
218 Entry& value,215 Entry& value,
219 const Image* pic = nullptr,216 const Image* pic = nullptr,
220 const bool select_this = false,217 const bool select_this = false,
221 const std::string& tooltip_text = std::string()) {218 const std::string& tooltip_text = std::string(), const std::string& hotkey = std::string()) {
222 Base::add(name, &value, pic, select_this, tooltip_text);219 Base::add(name, &value, pic, select_this, tooltip_text, hotkey);
223 }
224 void add_front(const std::string& name,
225 Entry& value,
226 const Image* pic = nullptr,
227 const bool select_this = false,
228 const std::string& tooltip_text = std::string()) {
229 Base::add_front(name, &value, pic, select_this, tooltip_text);
230 }220 }
231221
232 Entry& operator[](uint32_t const i) const {222 Entry& operator[](uint32_t const i) const {
233223
=== modified file 'src/ui_basic/panel.cc'
--- src/ui_basic/panel.cc 2019-05-03 19:24:19 +0000
+++ src/ui_basic/panel.cc 2019-06-24 16:19:42 +0000
@@ -123,8 +123,12 @@
123 // Scan-build claims this results in double free.123 // Scan-build claims this results in double free.
124 // This is a false positive.124 // This is a false positive.
125 // See https://bugs.launchpad.net/widelands/+bug/1198928125 // See https://bugs.launchpad.net/widelands/+bug/1198928
126 while (first_child_)126 while (first_child_) {
127 Panel* next_child = first_child_->next_;
127 delete first_child_;128 delete first_child_;
129 first_child_ = next_child;
130 }
131 first_child_ = nullptr;
128}132}
129133
130/**134/**
@@ -530,6 +534,7 @@
530bool Panel::handle_mousepress(const uint8_t btn, int32_t, int32_t) {534bool Panel::handle_mousepress(const uint8_t btn, int32_t, int32_t) {
531 if (btn == SDL_BUTTON_LEFT && get_can_focus()) {535 if (btn == SDL_BUTTON_LEFT && get_can_focus()) {
532 focus();536 focus();
537 clicked();
533 }538 }
534 return false;539 return false;
535}540}
@@ -735,10 +740,12 @@
735 Panel* p = next;740 Panel* p = next;
736 next = p->next_;741 next = p->next_;
737742
738 if (p->flags_ & pf_die)743 if (p->flags_ & pf_die) {
739 delete p;744 delete p;
740 else if (p->flags_ & pf_child_die)745 p = nullptr;
746 } else if (p->flags_ & pf_child_die) {
741 p->check_child_death();747 p->check_child_death();
748 }
742 }749 }
743750
744 flags_ &= ~pf_child_die;751 flags_ &= ~pf_child_die;
745752
=== modified file 'src/ui_basic/panel.h'
--- src/ui_basic/panel.h 2019-05-12 07:45:59 +0000
+++ src/ui_basic/panel.h 2019-06-24 16:19:42 +0000
@@ -89,6 +89,7 @@
89 const std::string& tooltip_text = std::string());89 const std::string& tooltip_text = std::string());
90 virtual ~Panel();90 virtual ~Panel();
9191
92 boost::signals2::signal<void()> clicked;
92 boost::signals2::signal<void()> position_changed;93 boost::signals2::signal<void()> position_changed;
9394
94 Panel* get_parent() const {95 Panel* get_parent() const {
@@ -119,8 +120,8 @@
119 virtual void end();120 virtual void end();
120121
121 // Geometry122 // Geometry
122 void set_size(int nw, int nh);123 virtual void set_size(int nw, int nh);
123 void set_desired_size(int w, int h);124 virtual void set_desired_size(int w, int h);
124 virtual void set_pos(Vector2i);125 virtual void set_pos(Vector2i);
125 virtual void move_inside_parent();126 virtual void move_inside_parent();
126 virtual void layout();127 virtual void layout();
127128
=== modified file 'src/ui_basic/radiobutton.cc'
--- src/ui_basic/radiobutton.cc 2019-02-23 11:00:49 +0000
+++ src/ui_basic/radiobutton.cc 2019-06-24 16:19:42 +0000
@@ -48,7 +48,7 @@
48 * Inform the radiogroup about the click; the group is responsible of setting48 * Inform the radiogroup about the click; the group is responsible of setting
49 * button states.49 * button states.
50 */50 */
51void Radiobutton::clicked() {51void Radiobutton::button_clicked() {
52 group_.set_state(id_);52 group_.set_state(id_);
53 play_click();53 play_click();
54}54}
5555
=== modified file 'src/ui_basic/radiobutton.h'
--- src/ui_basic/radiobutton.h 2019-02-23 11:00:49 +0000
+++ src/ui_basic/radiobutton.h 2019-06-24 16:19:42 +0000
@@ -42,7 +42,7 @@
42 }42 }
4343
44private:44private:
45 void clicked() override;45 void button_clicked() override;
4646
47 Radiobutton* nextbtn_;47 Radiobutton* nextbtn_;
48 Radiogroup& group_;48 Radiogroup& group_;
4949
=== modified file 'src/ui_basic/slider.h'
--- src/ui_basic/slider.h 2019-05-03 19:24:19 +0000
+++ src/ui_basic/slider.h 2019-06-24 16:19:42 +0000
@@ -106,7 +106,6 @@
106 void set_highlighted(bool highlighted);106 void set_highlighted(bool highlighted);
107107
108public:108public:
109 boost::signals2::signal<void()> clicked;
110 boost::signals2::signal<void()> changed;109 boost::signals2::signal<void()> changed;
111 boost::signals2::signal<void(int32_t)> changedto;110 boost::signals2::signal<void(int32_t)> changedto;
112111
113112
=== modified file 'src/ui_basic/unique_window.cc'
--- src/ui_basic/unique_window.cc 2019-02-23 11:00:49 +0000
+++ src/ui_basic/unique_window.cc 2019-06-24 16:19:42 +0000
@@ -58,7 +58,13 @@
58 */58 */
59void UniqueWindow::Registry::toggle() {59void UniqueWindow::Registry::toggle() {
60 if (window) {60 if (window) {
61 window->die();61 // There is already a window. If it is minimal, restore it.
62 if (window->is_minimal()) {
63 window->restore();
64 opened();
65 } else {
66 window->die();
67 }
62 } else {68 } else {
63 open_window();69 open_window();
64 }70 }
6571
=== modified file 'src/ui_fsmenu/launch_game.cc'
--- src/ui_fsmenu/launch_game.cc 2019-05-26 17:21:15 +0000
+++ src/ui_fsmenu/launch_game.cc 2019-06-24 16:19:42 +0000
@@ -46,15 +46,15 @@
46 buth_(get_h() * 9 / 200),46 buth_(get_h() * 9 / 200),
4747
48 win_condition_dropdown_(this,48 win_condition_dropdown_(this,
49 "dropdown_wincondition",
49 get_w() * 7 / 10,50 get_w() * 7 / 10,
50 get_h() * 4 / 10 + buth_,51 get_h() * 4 / 10 + buth_,
51 butw_,52 butw_,
52 get_h() - get_h() * 4 / 10 - buth_,53 10, // max number of items
53 buth_,54 buth_,
54 "",55 "",
55 UI::DropdownType::kTextual,56 UI::DropdownType::kTextual,
56 UI::PanelStyle::kFsMenu),57 UI::PanelStyle::kFsMenu, UI::ButtonStyle::kFsMenuMenu),
57
58 peaceful_(this, Vector2i(get_w() * 7 / 10, get_h() * 19 / 40 + buth_), _("Peaceful mode")),58 peaceful_(this, Vector2i(get_w() * 7 / 10, get_h() * 19 / 40 + buth_), _("Peaceful mode")),
59 ok_(this, "ok", 0, 0, butw_, buth_, UI::ButtonStyle::kFsMenuPrimary, _("Start game")),59 ok_(this, "ok", 0, 0, butw_, buth_, UI::ButtonStyle::kFsMenuPrimary, _("Start game")),
60 back_(this, "back", 0, 0, butw_, buth_, UI::ButtonStyle::kFsMenuSecondary, _("Back")),60 back_(this, "back", 0, 0, butw_, buth_, UI::ButtonStyle::kFsMenuSecondary, _("Back")),
6161
=== modified file 'src/ui_fsmenu/launch_spg.cc'
--- src/ui_fsmenu/launch_spg.cc 2019-05-26 17:21:15 +0000
+++ src/ui_fsmenu/launch_spg.cc 2019-06-24 16:19:42 +0000
@@ -114,6 +114,8 @@
114 ok_.set_pos(Vector2i(get_w() * 7 / 10, get_h() * 9 / 10));114 ok_.set_pos(Vector2i(get_w() * 7 / 10, get_h() * 9 / 10));
115 back_.set_pos(Vector2i(get_w() * 7 / 10, get_h() * 17 / 20));115 back_.set_pos(Vector2i(get_w() * 7 / 10, get_h() * 17 / 20));
116 win_condition_dropdown_.set_pos(Vector2i(get_w() * 7 / 10, get_h() * 4 / 10 + buth_));116 win_condition_dropdown_.set_pos(Vector2i(get_w() * 7 / 10, get_h() * 4 / 10 + buth_));
117 win_condition_dropdown_.set_size(select_map_.get_w(), win_condition_dropdown_.get_h());
118
117 title_.set_text(_("Launch Game"));119 title_.set_text(_("Launch Game"));
118 select_map_.sigclicked.connect(120 select_map_.sigclicked.connect(
119 boost::bind(&FullscreenMenuLaunchSPG::select_map, boost::ref(*this)));121 boost::bind(&FullscreenMenuLaunchSPG::select_map, boost::ref(*this)));
120122
=== modified file 'src/ui_fsmenu/options.cc'
--- src/ui_fsmenu/options.cc 2019-05-26 17:21:15 +0000
+++ src/ui_fsmenu/options.cc 2019-06-24 16:19:42 +0000
@@ -105,23 +105,25 @@
105105
106 // Interface options106 // Interface options
107 language_dropdown_(&box_interface_left_,107 language_dropdown_(&box_interface_left_,
108 0,108 "dropdown_language",
109 0,109 0,
110 100, // 100 is arbitrary, will be resized in layout().110 0,
111 100, // 100 is arbitrary, will be resized in layout().111 100, // 100 is arbitrary, will be resized in layout().
112 50,
112 24,113 24,
113 _("Language"),114 _("Language"),
114 UI::DropdownType::kTextual,115 UI::DropdownType::kTextual,
115 UI::PanelStyle::kFsMenu),116 UI::PanelStyle::kFsMenu, UI::ButtonStyle::kFsMenuMenu),
116 resolution_dropdown_(&box_interface_left_,117 resolution_dropdown_(&box_interface_left_,
117 0,118 "dropdown_resolution",
118 0,119 0,
119 100, // 100 is arbitrary, will be resized in layout().120 0,
120 100, // 100 is arbitrary, will be resized in layout().121 100, // 100 is arbitrary, will be resized in layout().
122 50,
121 24,123 24,
122 _("Window Size"),124 _("Window Size"),
123 UI::DropdownType::kTextual,125 UI::DropdownType::kTextual,
124 UI::PanelStyle::kFsMenu),126 UI::PanelStyle::kFsMenu, UI::ButtonStyle::kFsMenuMenu),
125127
126 fullscreen_(&box_interface_left_, Vector2i::zero(), _("Fullscreen"), "", 0),128 fullscreen_(&box_interface_left_, Vector2i::zero(), _("Fullscreen"), "", 0),
127 inputgrab_(&box_interface_left_, Vector2i::zero(), _("Grab Input"), "", 0),129 inputgrab_(&box_interface_left_, Vector2i::zero(), _("Grab Input"), "", 0),
128130
=== modified file 'src/wui/economy_options_window.cc'
--- src/wui/economy_options_window.cc 2019-06-21 15:31:15 +0000
+++ src/wui/economy_options_window.cc 2019-06-24 16:19:42 +0000
@@ -52,7 +52,7 @@
52 &tabpanel_, this, serial_, player_, can_act, Widelands::wwWORKER, kDesiredWidth)),52 &tabpanel_, this, serial_, player_, can_act, Widelands::wwWORKER, kDesiredWidth)),
53 dropdown_box_(this, 0, 0, UI::Box::Horizontal),53 dropdown_box_(this, 0, 0, UI::Box::Horizontal),
54 dropdown_(54 dropdown_(
55 &dropdown_box_, 0, 0, 174, 200, 34, "", UI::DropdownType::kTextual, UI::PanelStyle::kWui),55 &dropdown_box_, "economy_profiles", 0, 0, 174, 10, 34, "", UI::DropdownType::kTextual, UI::PanelStyle::kWui, UI::ButtonStyle::kWuiSecondary), // NOCOM test if this is the correct button style. heap-use-after-free somewhere too.
56 time_last_thought_(0),56 time_last_thought_(0),
57 save_profile_dialog_(nullptr) {57 save_profile_dialog_(nullptr) {
58 set_center_panel(&main_box_);58 set_center_panel(&main_box_);
5959
=== modified file 'src/wui/fieldaction.cc'
--- src/wui/fieldaction.cc 2019-06-23 12:45:29 +0000
+++ src/wui/fieldaction.cc 2019-06-24 16:19:42 +0000
@@ -76,7 +76,7 @@
7676
77BuildGrid::BuildGrid(UI::Panel* parent, Widelands::Player* plr, int32_t x, int32_t y, int32_t cols)77BuildGrid::BuildGrid(UI::Panel* parent, Widelands::Player* plr, int32_t x, int32_t y, int32_t cols)
78 : UI::IconGrid(parent, x, y, kBuildGridCellSize, kBuildGridCellSize, cols), plr_(plr) {78 : UI::IconGrid(parent, x, y, kBuildGridCellSize, kBuildGridCellSize, cols), plr_(plr) {
79 clicked.connect(boost::bind(&BuildGrid::click_slot, this, _1));79 icon_clicked.connect(boost::bind(&BuildGrid::click_slot, this, _1));
80 mouseout.connect(boost::bind(&BuildGrid::mouseout_slot, this, _1));80 mouseout.connect(boost::bind(&BuildGrid::mouseout_slot, this, _1));
81 mousein.connect(boost::bind(&BuildGrid::mousein_slot, this, _1));81 mousein.connect(boost::bind(&BuildGrid::mousein_slot, this, _1));
82}82}
8383
=== modified file 'src/wui/game_client_disconnected.cc'
--- src/wui/game_client_disconnected.cc 2019-02-23 11:00:49 +0000
+++ src/wui/game_client_disconnected.cc 2019-06-24 16:19:42 +0000
@@ -74,16 +74,17 @@
74 /** TRANSLATORS: Button tooltip */74 /** TRANSLATORS: Button tooltip */
75 _("Replace the disconnected player with the selected AI and continue playing")),75 _("Replace the disconnected player with the selected AI and continue playing")),
76 type_dropdown_(&box_h_,76 type_dropdown_(&box_h_,
77 "dropdown_ai",
77 width - 50, // x78 width - 50, // x
78 0, // y79 0, // y
79 60, // width of selection box80 60, // width of selection box
80 800, // height of selection box, shrinks automatically81 16, // maximum number of items in the selection box, shrinks automatically
81 35, // width/height of button82 35, // width/height of button
82 /** TRANSLATORS: Dropdown tooltip to select the AI difficulty when a player has83 /** TRANSLATORS: Dropdown tooltip to select the AI difficulty when a player has
83 disconnected from a game */84 disconnected from a game */
84 _("AI for the disconnected player"),85 _("AI for the disconnected player"),
85 UI::DropdownType::kPictorial,86 UI::DropdownType::kPictorial,
86 UI::PanelStyle::kWui),87 UI::PanelStyle::kWui, UI::ButtonStyle::kWuiMenu),
87 exit_game_(&box_,88 exit_game_(&box_,
88 "exit_game",89 "exit_game",
89 0,90 0,
9091
=== modified file 'src/wui/game_message_menu.cc'
--- src/wui/game_message_menu.cc 2019-05-26 17:21:15 +0000
+++ src/wui/game_message_menu.cc 2019-06-24 16:19:42 +0000
@@ -130,11 +130,9 @@
130 new UI::Button(this, "center_main_mapview_on_location", kWindowWidth - kPadding - kButtonSize,130 new UI::Button(this, "center_main_mapview_on_location", kWindowWidth - kPadding - kButtonSize,
131 archivebtn_->get_y(), kButtonSize, kButtonSize, UI::ButtonStyle::kWuiPrimary,131 archivebtn_->get_y(), kButtonSize, kButtonSize, UI::ButtonStyle::kWuiPrimary,
132 g_gr->images().get("images/wui/menus/menu_goto.png"),132 g_gr->images().get("images/wui/menus/menu_goto.png"),
133 /** TRANSLATORS: %s is a tooltip, G is the corresponding hotkey */133 as_tooltip_text_with_hotkey(
134 (boost::format(_("G: %s"))
135 /** TRANSLATORS: Tooltip in the messages window */134 /** TRANSLATORS: Tooltip in the messages window */
136 % _("Center main mapview on location"))135 _("Center main mapview on location"), "g"));
137 .str());
138 centerviewbtn_->sigclicked.connect(boost::bind(&GameMessageMenu::center_view, this));136 centerviewbtn_->sigclicked.connect(boost::bind(&GameMessageMenu::center_view, this));
139 centerviewbtn_->set_enabled(false);137 centerviewbtn_->set_enabled(false);
140138
@@ -524,10 +522,9 @@
524 message_filter_ = msgtype;522 message_filter_ = msgtype;
525523
526 /** TRANSLATORS: %1% is a tooltip, %2% is the corresponding hotkey */524 /** TRANSLATORS: %1% is a tooltip, %2% is the corresponding hotkey */
527 button.set_tooltip((boost::format(_("%1% (Hotkey: %2%)"))525 button.set_tooltip(as_tooltip_text_with_hotkey(
528 /** TRANSLATORS: Tooltip in the messages window */526 /** TRANSLATORS: Tooltip in the messages window */
529 % _("Show all messages") % pgettext("hotkey", "Alt + 0"))527 _("Show all messages"), pgettext("hotkey", "Alt+0")));
530 .str());
531 }528 }
532}529}
533530
@@ -535,27 +532,22 @@
535 * Helper for filter_messages532 * Helper for filter_messages
536 */533 */
537void GameMessageMenu::set_filter_messages_tooltips() {534void GameMessageMenu::set_filter_messages_tooltips() {
538 geologistsbtn_->set_tooltip((boost::format(_("%1% (Hotkey: %2%)"))535 geologistsbtn_->set_tooltip(as_tooltip_text_with_hotkey(
539 /** TRANSLATORS: Tooltip in the messages window */536 /** TRANSLATORS: Tooltip in the messages window */
540 % _("Show geologists' messages only") %537 _("Show geologists' messages only"),
541 pgettext("hotkey", "Alt + 1"))538 pgettext("hotkey", "Alt+1")));
542 .str());539 economybtn_->set_tooltip(as_tooltip_text_with_hotkey(
543 economybtn_->set_tooltip((boost::format(_("%1% (Hotkey: %2%)"))
544 /** TRANSLATORS: Tooltip in the messages window */540 /** TRANSLATORS: Tooltip in the messages window */
545 % _("Show economy messages only") % pgettext("hotkey", "Alt + 2"))541 _("Show economy messages only"), pgettext("hotkey", "Alt+2")));
546 .str());542 seafaringbtn_->set_tooltip(as_tooltip_text_with_hotkey(
547 seafaringbtn_->set_tooltip((boost::format(_("%1% (Hotkey: %2%)"))
548 /** TRANSLATORS: Tooltip in the messages window */543 /** TRANSLATORS: Tooltip in the messages window */
549 % _("Show seafaring messages only") % pgettext("hotkey", "Alt + 3"))544 _("Show seafaring messages only"), pgettext("hotkey", "Alt+3")));
550 .str());545 warfarebtn_->set_tooltip(as_tooltip_text_with_hotkey(
551 warfarebtn_->set_tooltip((boost::format(_("%1% (Hotkey: %2%)"))
552 /** TRANSLATORS: Tooltip in the messages window */546 /** TRANSLATORS: Tooltip in the messages window */
553 % _("Show warfare messages only") % pgettext("hotkey", "Alt + 4"))547 _("Show warfare messages only"), pgettext("hotkey", "Alt+4")));
554 .str());548 scenariobtn_->set_tooltip(as_tooltip_text_with_hotkey(
555 scenariobtn_->set_tooltip((boost::format(_("%1% (Hotkey: %2%)"))
556 /** TRANSLATORS: Tooltip in the messages window */549 /** TRANSLATORS: Tooltip in the messages window */
557 % _("Show scenario messages only") % pgettext("hotkey", "Alt + 5"))550 _("Show scenario messages only"), pgettext("hotkey", "Alt+5")));
558 .str());
559}551}
560552
561/**553/**
@@ -651,6 +643,6 @@
651 }643 }
652 break;644 break;
653 }645 }
654 /** TRANSLATORS: %s is a tooltip, Del is the corresponding hotkey */646 /** TRANSLATORS: Del is the "Delete" key on the keyboard */
655 archivebtn_->set_tooltip((boost::format(_("Del: %s")) % button_tooltip).str());647 archivebtn_->set_tooltip(as_tooltip_text_with_hotkey(button_tooltip, pgettext("hotkey", "Del")));
656}648}
657649
=== modified file 'src/wui/multiplayersetupgroup.cc'
--- src/wui/multiplayersetupgroup.cc 2019-05-12 07:45:59 +0000
+++ src/wui/multiplayersetupgroup.cc 2019-06-24 16:19:42 +0000
@@ -55,7 +55,8 @@
55 GameSettingsProvider* const settings)55 GameSettingsProvider* const settings)
56 : UI::Box(parent, 0, 0, UI::Box::Horizontal, w, h, kPadding),56 : UI::Box(parent, 0, 0, UI::Box::Horizontal, w, h, kPadding),
57 slot_dropdown_(57 slot_dropdown_(
58 this, 0, 0, h, 200, h, _("Role"), UI::DropdownType::kPictorial, UI::PanelStyle::kFsMenu),58 this, (boost::format("dropdown_slot%d") % static_cast<unsigned int>(id)).str(),
59 0, 0, h, 16, h, _("Role"), UI::DropdownType::kPictorial, UI::PanelStyle::kFsMenu, UI::ButtonStyle::kFsMenuSecondary),
59 // Name needs to be initialized after the dropdown, otherwise the layout function will60 // Name needs to be initialized after the dropdown, otherwise the layout function will
60 // crash.61 // crash.
61 name(this, 0, 0, w - h - UI::Scrollbar::kSize * 11 / 5, h),62 name(this, 0, 0, w - h - UI::Scrollbar::kSize * 11 / 5, h),
@@ -187,34 +188,37 @@
187 (boost::format(_("Player %u")) % static_cast<unsigned int>(id_ + 1)).str(),188 (boost::format(_("Player %u")) % static_cast<unsigned int>(id_ + 1)).str(),
188 UI::Button::VisualState::kFlat),189 UI::Button::VisualState::kFlat),
189 type_dropdown_(this,190 type_dropdown_(this,
191 (boost::format("dropdown_type%d") % static_cast<unsigned int>(id)).str(),
190 0,192 0,
191 0,193 0,
192 50,194 50,
193 200,195 16,
194 h,196 h,
195 _("Type"),197 _("Type"),
196 UI::DropdownType::kPictorial,198 UI::DropdownType::kPictorial,
197 UI::PanelStyle::kFsMenu),199 UI::PanelStyle::kFsMenu, UI::ButtonStyle::kFsMenuSecondary),
198 tribes_dropdown_(this,200 tribes_dropdown_(this,
201 (boost::format("dropdown_tribes%d") % static_cast<unsigned int>(id)).str(),
199 0,202 0,
200 0,203 0,
201 50,204 50,
202 200,205 16,
203 h,206 h,
204 _("Tribe"),207 _("Tribe"),
205 UI::DropdownType::kPictorial,208 UI::DropdownType::kPictorial,
206 UI::PanelStyle::kFsMenu),209 UI::PanelStyle::kFsMenu, UI::ButtonStyle::kFsMenuSecondary),
207 init_dropdown_(this,210 init_dropdown_(this,
211 (boost::format("dropdown_init%d") % static_cast<unsigned int>(id)).str(),
208 0,212 0,
209 0,213 0,
210 w - 4 * h - 3 * kPadding,214 w - 4 * h - 3 * kPadding,
211 200,215 16,
212 h,216 h,
213 "",217 "",
214 UI::DropdownType::kTextualNarrow,218 UI::DropdownType::kTextualNarrow,
215 UI::PanelStyle::kFsMenu),219 UI::PanelStyle::kFsMenu, UI::ButtonStyle::kFsMenuSecondary),
216 team_dropdown_(220 team_dropdown_(
217 this, 0, 0, h, 200, h, _("Team"), UI::DropdownType::kPictorial, UI::PanelStyle::kFsMenu),221 this, (boost::format("dropdown_team%d") % static_cast<unsigned int>(id)).str(), 0, 0, h, 16, h, _("Team"), UI::DropdownType::kPictorial, UI::PanelStyle::kFsMenu, UI::ButtonStyle::kFsMenuSecondary),
218 last_state_(PlayerSettings::State::kClosed),222 last_state_(PlayerSettings::State::kClosed),
219 type_selection_locked_(false),223 type_selection_locked_(false),
220 tribe_selection_locked_(false),224 tribe_selection_locked_(false),
221225
=== modified file 'src/wui/seafaring_statistics_menu.cc'
--- src/wui/seafaring_statistics_menu.cc 2019-02-23 11:00:49 +0000
+++ src/wui/seafaring_statistics_menu.cc 2019-06-24 16:19:42 +0000
@@ -26,6 +26,7 @@
2626
27#include "economy/fleet.h"27#include "economy/fleet.h"
28#include "graphic/graphic.h"28#include "graphic/graphic.h"
29#include "graphic/text_layout.h"
29#include "logic/game.h"30#include "logic/game.h"
30#include "logic/player.h"31#include "logic/player.h"
31#include "logic/playercommand.h"32#include "logic/playercommand.h"
@@ -98,10 +99,8 @@
98 kButtonSize,99 kButtonSize,
99 UI::ButtonStyle::kWuiPrimary,100 UI::ButtonStyle::kWuiPrimary,
100 g_gr->images().get("images/wui/menus/menu_watch_follow.png"),101 g_gr->images().get("images/wui/menus/menu_watch_follow.png"),
101 (boost::format(_("%1% (Hotkey: %2%)")) %
102 /** TRANSLATORS: Tooltip in the seafaring statistics window */102 /** TRANSLATORS: Tooltip in the seafaring statistics window */
103 _("Watch the selected ship") % pgettext("hotkey", "W"))103 as_tooltip_text_with_hotkey(_("Watch the selected ship"), "w")),
104 .str()),
105 openwindowbtn_(104 openwindowbtn_(
106 &navigation_box_,105 &navigation_box_,
107 "seafaring_stats_watch_button",106 "seafaring_stats_watch_button",
@@ -112,13 +111,12 @@
112 UI::ButtonStyle::kWuiPrimary,111 UI::ButtonStyle::kWuiPrimary,
113 g_gr->images().get("images/ui_basic/fsel.png"),112 g_gr->images().get("images/ui_basic/fsel.png"),
114 (boost::format("%s<br>%s") %113 (boost::format("%s<br>%s") %
115 (boost::format(pgettext("hotkey_description", "%1%: %2%")) % pgettext("hotkey", "O") %114 as_tooltip_text_with_hotkey(
116 /** TRANSLATORS: Tooltip in the seafaring statistics window */115 /** TRANSLATORS: Tooltip in the seafaring statistics window */
117 _("Open the selected ship’s window")) %116 _("Open the selected ship’s window"), "o") %
118 (boost::format(pgettext("hotkey_description", "%1%: %2%")) %117 as_tooltip_text_with_hotkey(
119 pgettext("hotkey", "CTRL + O") %118 /** TRANSLATORS: Tooltip in the seafaring statistics window */
120 /** TRANSLATORS: Tooltip in the seafaring statistics window */119 _("Go to the selected ship and open its window"), pgettext("hotkey", "CTRL+o")))
121 _("Go to the selected ship and open its window")))
122 .str()),120 .str()),
123 centerviewbtn_(&navigation_box_,121 centerviewbtn_(&navigation_box_,
124 "seafaring_stats_center_main_mapview_button",122 "seafaring_stats_center_main_mapview_button",
@@ -128,10 +126,9 @@
128 kButtonSize,126 kButtonSize,
129 UI::ButtonStyle::kWuiPrimary,127 UI::ButtonStyle::kWuiPrimary,
130 g_gr->images().get("images/wui/ship/menu_ship_goto.png"),128 g_gr->images().get("images/wui/ship/menu_ship_goto.png"),
131 (boost::format(_("%1% (Hotkey: %2%)")) %129 as_tooltip_text_with_hotkey(
132 /** TRANSLATORS: Tooltip in the seafaring statistics window */130 /** TRANSLATORS: Tooltip in the seafaring statistics window */
133 _("Center the map on the selected ship") % pgettext("hotkey", "G"))131 _("Center the map on the selected ship"), "g")),
134 .str()),
135 table_(&main_box_, 0, 0, get_inner_w() - 2 * kPadding, 100, UI::PanelStyle::kWui) {132 table_(&main_box_, 0, 0, get_inner_w() - 2 * kPadding, 100, UI::PanelStyle::kWui) {
136133
137 const Widelands::TribeDescr& tribe = iplayer().player().tribe();134 const Widelands::TribeDescr& tribe = iplayer().player().tribe();
@@ -495,39 +492,32 @@
495 button.set_perm_pressed(true);492 button.set_perm_pressed(true);
496 ship_filter_ = status;493 ship_filter_ = status;
497494
498 /** TRANSLATORS: %1% is a tooltip, %2% is the corresponding hotkey */495 button.set_tooltip(as_tooltip_text_with_hotkey(
499 button.set_tooltip((boost::format(_("%1% (Hotkey: %2%)"))496 /** TRANSLATORS: Tooltip in the ship statistics window */
500 /** TRANSLATORS: Tooltip in the messages window */497 _("Show all ships"), pgettext("hotkey", "Alt+0")));
501 % _("Show all ships") % pgettext("hotkey", "Alt + 0"))
502 .str());
503 }498 }
504}499}
505500
506void SeafaringStatisticsMenu::set_filter_ships_tooltips() {501void SeafaringStatisticsMenu::set_filter_ships_tooltips() {
507502
508 idle_btn_.set_tooltip((boost::format(_("%1% (Hotkey: %2%)"))503 idle_btn_.set_tooltip(as_tooltip_text_with_hotkey(
509 /** TRANSLATORS: Tooltip in the messages window */504 /** TRANSLATORS: Tooltip in the ship statistics window */
510 % _("Show idle ships") % pgettext("hotkey", "Alt + 1"))505 _("Show idle ships"), pgettext("hotkey", "Alt+1")));
511 .str());506 shipping_btn_.set_tooltip(as_tooltip_text_with_hotkey(
512 shipping_btn_.set_tooltip((boost::format(_("%1% (Hotkey: %2%)"))507 /** TRANSLATORS: Tooltip in the ship statistics window */
513 /** TRANSLATORS: Tooltip in the messages window */508 _("Show ships shipping wares and workers"),
514 % _("Show ships shipping wares and workers") %509 pgettext("hotkey", "Alt+2")));
515 pgettext("hotkey", "Alt + 2"))510 waiting_btn_.set_tooltip(as_tooltip_text_with_hotkey(
516 .str());511 /** TRANSLATORS: Tooltip in the ship statistics window */
517 waiting_btn_.set_tooltip((boost::format(_("%1% (Hotkey: %2%)"))512 _("Show waiting expeditions"), pgettext("hotkey", "Alt+3")));
518 /** TRANSLATORS: Tooltip in the messages window */513 scouting_btn_.set_tooltip(as_tooltip_text_with_hotkey(
519 % _("Show waiting expeditions") % pgettext("hotkey", "Alt + 3"))514 /** TRANSLATORS: Tooltip in the ship statistics window */
520 .str());515 _("Show scouting expeditions"), pgettext("hotkey", "Alt+4")));
521 scouting_btn_.set_tooltip((boost::format(_("%1% (Hotkey: %2%)"))
522 /** TRANSLATORS: Tooltip in the messages window */
523 % _("Show scouting expeditions") % pgettext("hotkey", "Alt + 4"))
524 .str());
525 portspace_btn_.set_tooltip(516 portspace_btn_.set_tooltip(
526 (boost::format(_("%1% (Hotkey: %2%)"))517 as_tooltip_text_with_hotkey(
527 /** TRANSLATORS: Tooltip in the messages window */518 /** TRANSLATORS: Tooltip in the ship statistics window */
528 % _("Show expeditions that have found a port space or are founding a colony") %519 _("Show expeditions that have found a port space or are founding a colony"),
529 pgettext("hotkey", "Alt + 5"))520 pgettext("hotkey", "Alt+5")));
530 .str());
531}521}
532522
533bool SeafaringStatisticsMenu::satisfies_filter(const ShipInfo& info, ShipFilterStatus filter) {523bool SeafaringStatisticsMenu::satisfies_filter(const ShipInfo& info, ShipFilterStatus filter) {

Subscribers

People subscribed via source and target branches

to status/vote changes: