Merge lp:~widelands-dev/widelands/bug-536489-dropdown into lp:widelands
- bug-536489-dropdown
- Merge into trunk
Status: | Merged | ||||||||
---|---|---|---|---|---|---|---|---|---|
Merged at revision: | 8158 | ||||||||
Proposed branch: | lp:~widelands-dev/widelands/bug-536489-dropdown | ||||||||
Merge into: | lp:widelands | ||||||||
Diff against target: |
1660 lines (+726/-199) 23 files modified
src/editor/ui_menus/main_menu_map_options.cc (+1/-1) src/ui_basic/CMakeLists.txt (+2/-0) src/ui_basic/button.cc (+9/-3) src/ui_basic/button.h (+1/-2) src/ui_basic/checkbox.cc (+3/-1) src/ui_basic/dropdown.cc (+215/-0) src/ui_basic/dropdown.h (+166/-0) src/ui_basic/listselect.cc (+85/-34) src/ui_basic/listselect.h (+45/-8) src/ui_basic/panel.cc (+1/-1) src/ui_basic/panel.h (+1/-1) src/ui_basic/progressbar.cc (+2/-2) src/ui_basic/scrollbar.cc (+2/-1) src/ui_basic/slider.cc (+4/-0) src/ui_basic/tabpanel.cc (+10/-12) src/ui_basic/window.cc (+17/-13) src/ui_fsmenu/launch_mpg.cc (+1/-1) src/ui_fsmenu/launch_spg.cc (+101/-63) src/ui_fsmenu/launch_spg.h (+20/-6) src/ui_fsmenu/mapselect.cc (+2/-1) src/ui_fsmenu/options.cc (+34/-40) src/ui_fsmenu/options.h (+3/-8) src/wui/game_objectives_menu.cc (+1/-1) |
||||||||
To merge this branch: | bzr merge lp:~widelands-dev/widelands/bug-536489-dropdown | ||||||||
Related bugs: |
|
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
SirVer | Approve | ||
GunChleoc | Needs Resubmitting | ||
kaputtnik (community) | testing | Approve | |
Klaus Halfmann | compile, test | Approve | |
Review via email: mp+306303@code.launchpad.net |
Commit message
Implemented a textual dropdown menu. It is used in:
- Options: Screen resolution
- Options: Language. Got rid of the extra Language tab and moved it to the Interface tab.
- Launch Single Player Game: Win Condition
Description of the change
My first branch for Build 20 ;)
Implemented a textual dropdown menu. This is already quite a bit of code, so I'll do the win condition for multiplayer in a separate branch. We will also want pictorial dropdowns.
bunnybot (widelandsofficial) wrote : | # |
Klaus Halfmann (klaus-halfmann) wrote : | # |
This Drodown is missing a feature found in all normal GUIs:
when you click outside the dropdown it will NOT collapse,
allowing the user to click somewhere else.
This implementation will do nothing in this case.
So if I forgot that the Dropdown is open and click somwhere else
It seems stuck as I get no visual feedback what to do.
This may frustrate players upto a level where theey will kill the game.
In additon it does not collapse when pressing Escape,
instead the complete dialog is canceled.
Sorry Gun.
Good work otherwise. I will continue to review the usage and code.
GunChleoc (gunchleoc) wrote : | # |
Good points. The escape key thing should be easy to do, for the collapse when clicked outside of it I will need to dig around a bit.
Thanks for testing!
GunChleoc (gunchleoc) wrote : | # |
Ready for the next round. It is now possible to navigate through the list with the keyboard as well.
Note: I forgot that Esc should cancel the change in the list, will still do that.
Handling a mouse click outside of a panel is tricky, so I autoclose if the mouse moves outside the panel. I gave this some distance so that the list won't disappear on users when they move the mouse 1 pixel off, let me know if we need to widen that. The tolerance is currently 50 pixels, and 25 for moving up.
bunnybot (widelandsofficial) wrote : | # |
Continuous integration builds have changed state:
Travis build 1345. State: passed. Details: https:/
Appveyor build 1187. State: success. Details: https:/
Klaus Halfmann (klaus-halfmann) wrote : | # |
Hello Gun,
thanks for your work.
* the Autoclose when leaving feels reasonbale. OK for me
* Esc-Key is handled as expected.
There is one confusion however:
The menu has a Selection - via a background color - and a checkmark.
When I use the cursor keys to scroll both, the selection and the
checkmark change. So I assume I made the choice. When I then leave
the list, I find that my selection was not applied (so the checkmark is wrong).
Please improve as follows: do not move the checkmark until the selection
in finally confirmed by clicking or by pressing return.
kaputtnik (franku) wrote : | # |
I get a crash as soon i click on one of the dropdown menus in options:
src/ui_
Backtrace: https:/
GunChleoc (gunchleoc) wrote : | # |
How would it be if there was no checkmark at all? Would that be confusing when the dropdown gets opens?
Klaus Halfmann (klaus-halfmann) wrote : | # |
> How would it be if there was no checkmark at all?
> Would that be confusing when the dropdown gets opens?
Hm, the checkmark is a good and normal hint what the
current selection is. But you can completly drop it, in case
it is to diffult otherwise.
In the End you are our User-Interface Department.
GunChleoc (gunchleoc) wrote : | # |
Maybe I am, but your input is very helpful!
I just gave it a spin, I think it would be best if the mouse position / current selection gets highlighted, and no tick. Back to the drawing board :)
kaputtnik (franku) wrote : | # |
Thanks for fixing the crash :-)
The checkmark is IMHO good now.
But clicking on the vertical slider in Options->Language let the drop down menu disappear.
GunChleoc (gunchleoc) wrote : | # |
Collapse with the scrollbar should be fixed now. Also instant highlighting for mouse movement.
Klaus Halfmann (klaus-halfmann) wrote : | # |
last night I updated to OSX-Sierra
* The good news: Widelands starts (last status compiled form this branch)
* The bad one: When compiling it complains about some system headers.
I will open another Bug to collect this Issues, maybe I can fix them easily,
Klaus Halfmann (klaus-halfmann) wrote : | # |
OK, fine for me, I did not take a deep look at that code, sorry.
As I can test macOS (Sierra) only, I would like to see Windows and Linux approvals, too
kaputtnik (franku) wrote : | # |
One little thing: If the dropdown box is once opened and closed (with moving away the mouse pointer) and hitting the down arrow of the keyboard, the dropdownmenu sometimes pops up for little time. Best to see if you keep the down arrow down for a longer time.
Not really an issue, so i leave it up to you to fix this.
GunChleoc (gunchleoc) wrote : | # |
Good point - deactivated the key when the mouse is not on the dropdown.
bunnybot (widelandsofficial) wrote : | # |
Continuous integration builds have changed state:
Travis build 1382. State: failed. Details: https:/
Appveyor build 1224. State: success. Details: https:/
bunnybot (widelandsofficial) wrote : | # |
Continuous integration builds have changed state:
Travis build 1384. State: passed. Details: https:/
Appveyor build 1226. State: success. Details: https:/
Klaus Halfmann (klaus-halfmann) wrote : | # |
After merging the trunk I now get (in bzr8108[bug-536489-dropdown])
Spieldatenfehler
economies: player 3: Stream ended unexpectedly (0 bytes read, 4 expected)
So I was unable to continue playing my last save game.
As this happens in trunk to I will not complain more ...
GunChleoc (gunchleoc) wrote : | # |
That error can't possibly be related to this branch, since you already said that it happens in trunk as well. It's worth reporting as a bug though.
SirVer (sirver) wrote : | # |
Just tested under Mac OS. Really nice change. One possible eye candy nit/improvement could be a small line outside of the drop down at the bottom and the sides - to connect the drop down better with the original button.
I will review the code eventually.... no hurry here, build 19 is still a few weeks off.
kaputtnik (franku) wrote : | # |
> One possible eye candy
> nit/improvement could be a small line outside of the drop down at the bottom
> and the sides - to connect the drop down better with the original button.
+1 (i thought something like this also)
GunChleoc (gunchleoc) wrote : | # |
Beautification done :)
I also want to give the scrollbar some love, but I'll have to do that in a separate branch, since that will affect all kinds of stuff.
kaputtnik (franku) wrote : | # |
Looks better now. Have you tested showing it flattened instead of raised?
GunChleoc (gunchleoc) wrote : | # |
No, I think flattened is wrong for it - it is dropped down on top of the other elements, not below them.
bunnybot (widelandsofficial) wrote : | # |
Continuous integration builds have changed state:
Travis build 1445. State: passed. Details: https:/
Appveyor build 1288. State: failed. Details: https:/
SirVer (sirver) wrote : | # |
A couple of change suggestions and questions in code
GunChleoc (gunchleoc) wrote : | # |
Added some replies/questions.
SirVer (sirver) wrote : | # |
you also need to merge trunk it seems.
bunnybot (widelandsofficial) wrote : | # |
Continuous integration builds have changed state:
Travis build 1527. State: failed. Details: https:/
Appveyor build 1288. State: failed. Details: https:/
GunChleoc (gunchleoc) wrote : | # |
All fixed except for 1 comment - have added a comment on my own to the source code.
bunnybot (widelandsofficial) wrote : | # |
Continuous integration builds have changed state:
Travis build 1531. State: passed. Details: https:/
Appveyor build 1373. State: success. Details: https:/
SirVer (sirver) wrote : | # |
I added a inline comment there. Essentially, I still think it is worthwhile for clarity to pull this function out.
otherwise lgtm.
GunChleoc (gunchleoc) wrote : | # |
You are right, it makes the function easier to read, so I've pulled it out.
Thanks for the review :)
@bunnybot merge
Preview Diff
1 | === modified file 'src/editor/ui_menus/main_menu_map_options.cc' |
2 | --- src/editor/ui_menus/main_menu_map_options.cc 2016-10-16 09:31:42 +0000 |
3 | +++ src/editor/ui_menus/main_menu_map_options.cc 2016-10-29 10:23:37 +0000 |
4 | @@ -79,7 +79,7 @@ |
5 | author_(&main_box_, 0, 0, max_w_, 0, 2, g_gr->images().get("images/ui_basic/but1.png")), |
6 | size_(&main_box_, 0, 0, max_w_ - indent_, labelh_, ""), |
7 | |
8 | - teams_list_(&teams_box_, 0, 0, max_w_, 60, true), |
9 | + teams_list_(&teams_box_, 0, 0, max_w_, 60, UI::ListselectLayout::kShowCheck), |
10 | |
11 | modal_(modal) { |
12 | |
13 | |
14 | === modified file 'src/ui_basic/CMakeLists.txt' |
15 | --- src/ui_basic/CMakeLists.txt 2016-04-02 16:45:53 +0000 |
16 | +++ src/ui_basic/CMakeLists.txt 2016-10-29 10:23:37 +0000 |
17 | @@ -6,6 +6,8 @@ |
18 | button.h |
19 | checkbox.cc |
20 | checkbox.h |
21 | + dropdown.cc |
22 | + dropdown.h |
23 | editbox.cc |
24 | editbox.h |
25 | fileview_panel.cc |
26 | |
27 | === modified file 'src/ui_basic/button.cc' |
28 | --- src/ui_basic/button.cc 2016-10-24 14:04:00 +0000 |
29 | +++ src/ui_basic/button.cc 2016-10-29 10:23:37 +0000 |
30 | @@ -65,6 +65,7 @@ |
31 | set_size(w, new_height); |
32 | } |
33 | set_thinks(false); |
34 | + set_can_focus(true); |
35 | } |
36 | |
37 | Button::Button // for pictorial buttons |
38 | @@ -91,6 +92,7 @@ |
39 | pic_custom_(fg_pic), |
40 | clr_down_(229, 161, 2) { |
41 | set_thinks(false); |
42 | + set_can_focus(true); |
43 | } |
44 | |
45 | Button::~Button() { |
46 | @@ -127,6 +129,8 @@ |
47 | if (enabled_ == on) |
48 | return; |
49 | |
50 | + set_can_focus(on); |
51 | + |
52 | // disabled buttons should look different... |
53 | if (on) |
54 | enabled_ = true; |
55 | @@ -148,7 +152,8 @@ |
56 | // Draw the background |
57 | if (pic_background_) { |
58 | dst.fill_rect(Rectf(0.f, 0.f, get_w(), get_h()), RGBAColor(0, 0, 0, 255)); |
59 | - dst.tile(Recti(Vector2i(0, 0), get_w(), get_h()), pic_background_, Vector2i(get_x(), get_y())); |
60 | + dst.tile( |
61 | + Recti(Vector2i(0, 0), get_w(), get_h()), pic_background_, Vector2i(get_x(), get_y())); |
62 | } |
63 | |
64 | if (enabled_ && highlighted_ && style_ != Style::kFlat) |
65 | @@ -198,8 +203,8 @@ |
66 | enabled_ ? UI_FONT_CLR_FG : UI_FONT_CLR_DISABLED); |
67 | // Blit on pixel boundary (not float), so that the text is blitted pixel perfect. |
68 | dst.blit( |
69 | - Vector2f((get_w() - entry_text_im->width()) / 2, (get_h() - entry_text_im->height()) / 2), |
70 | - entry_text_im); |
71 | + Vector2f((get_w() - entry_text_im->width()) / 2, (get_h() - entry_text_im->height()) / 2), |
72 | + entry_text_im); |
73 | } |
74 | |
75 | // draw border |
76 | @@ -294,6 +299,7 @@ |
77 | return false; |
78 | |
79 | if (enabled_) { |
80 | + focus(); |
81 | grab_mouse(true); |
82 | pressed_ = true; |
83 | if (repeating_) { |
84 | |
85 | === modified file 'src/ui_basic/button.h' |
86 | --- src/ui_basic/button.h 2016-09-25 13:07:06 +0000 |
87 | +++ src/ui_basic/button.h 2016-10-29 10:23:37 +0000 |
88 | @@ -39,7 +39,7 @@ |
89 | enum class Style { |
90 | kRaised, // Normal raised Button |
91 | kPermpressed, // Button will appear pressed |
92 | - kFlat // Flat button with simple coloured outline |
93 | + kFlat // Flat button with simple coloured outline |
94 | }; |
95 | |
96 | enum class ImageMode { |
97 | @@ -108,7 +108,6 @@ |
98 | /// Convenience function. If 'pressed', sets the style to kPermpressed, otherwise to kRaised. |
99 | void set_perm_pressed(bool pressed); |
100 | |
101 | - |
102 | /// Convenience function. Toggles between raised and permpressed style |
103 | void toggle(); |
104 | |
105 | |
106 | === modified file 'src/ui_basic/checkbox.cc' |
107 | --- src/ui_basic/checkbox.cc 2016-10-16 20:35:47 +0000 |
108 | +++ src/ui_basic/checkbox.cc 2016-10-29 10:23:37 +0000 |
109 | @@ -43,7 +43,7 @@ |
110 | uint16_t h = pic->height(); |
111 | set_desired_size(w, h); |
112 | set_size(w, h); |
113 | - |
114 | + set_can_focus(true); |
115 | set_flags(Has_Custom_Picture, true); |
116 | pic_graphics_ = pic; |
117 | } |
118 | @@ -79,6 +79,7 @@ |
119 | * Args: enabled true if the checkbox should be enabled, false otherwise |
120 | */ |
121 | void Statebox::set_enabled(bool const enabled) { |
122 | + set_can_focus(enabled); |
123 | if (((flags_ & Is_Enabled) > 1) && enabled) |
124 | return; |
125 | |
126 | @@ -157,6 +158,7 @@ |
127 | */ |
128 | bool Statebox::handle_mousepress(const uint8_t btn, int32_t, int32_t) { |
129 | if (btn == SDL_BUTTON_LEFT && (flags_ & Is_Enabled)) { |
130 | + focus(); |
131 | clicked(); |
132 | return true; |
133 | } |
134 | |
135 | === added file 'src/ui_basic/dropdown.cc' |
136 | --- src/ui_basic/dropdown.cc 1970-01-01 00:00:00 +0000 |
137 | +++ src/ui_basic/dropdown.cc 2016-10-29 10:23:37 +0000 |
138 | @@ -0,0 +1,215 @@ |
139 | +/* |
140 | + * Copyright (C) 2016 by the Widelands Development Team |
141 | + * |
142 | + * This program is free software; you can redistribute it and/or |
143 | + * modify it under the terms of the GNU General Public License |
144 | + * as published by the Free Software Foundation; either version 2 |
145 | + * of the License, or (at your option) any later version. |
146 | + * |
147 | + * This program is distributed in the hope that it will be useful, |
148 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
149 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
150 | + * GNU General Public License for more details. |
151 | + * |
152 | + * You should have received a copy of the GNU General Public License |
153 | + * along with this program; if not, write to the Free Software |
154 | + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. |
155 | + * |
156 | + */ |
157 | + |
158 | +#include "ui_basic/dropdown.h" |
159 | + |
160 | +#include <algorithm> |
161 | + |
162 | +#include <boost/format.hpp> |
163 | + |
164 | +#include "base/i18n.h" |
165 | +#include "graphic/align.h" |
166 | +#include "graphic/font_handler1.h" |
167 | +#include "graphic/graphic.h" |
168 | +#include "graphic/image.h" |
169 | +#include "graphic/rendertarget.h" |
170 | +#include "ui_basic/mouse_constants.h" |
171 | + |
172 | +namespace UI { |
173 | + |
174 | +BaseDropdown::BaseDropdown( |
175 | + UI::Panel* parent, int32_t x, int32_t y, uint32_t w, uint32_t h, const std::string& label) |
176 | + : UI::Panel( |
177 | + parent, |
178 | + x, |
179 | + y, |
180 | + w, |
181 | + std::max(24, |
182 | + UI::g_fh1->render(as_uifont(UI::g_fh1->fontset()->representative_character())) |
183 | + ->height() + |
184 | + 2)), // Height only to fit the button, so we can use this in Box layout. |
185 | + max_list_height_(h - 2 * get_h()), |
186 | + mouse_tolerance_(50), |
187 | + button_box_(this, 0, 0, UI::Box::Horizontal, w, h), |
188 | + push_button_(&button_box_, |
189 | + "dropdown_select", |
190 | + 0, |
191 | + 0, |
192 | + 24, |
193 | + get_h(), |
194 | + g_gr->images().get("images/ui_basic/but3.png"), |
195 | + g_gr->images().get("images/ui_basic/scrollbar_down.png"), |
196 | + pgettext("dropdown", "Select Item")), |
197 | + display_button_(&button_box_, |
198 | + "dropdown_label", |
199 | + 0, |
200 | + 0, |
201 | + w - 24, |
202 | + get_h(), |
203 | + g_gr->images().get("images/ui_basic/but1.png"), |
204 | + label), |
205 | + // Hook into parent so we can drop down outside the panel |
206 | + list_(parent, x, y + get_h(), w, 0, ListselectLayout::kDropdown), |
207 | + label_(label) { |
208 | + list_.set_visible(false); |
209 | + list_.set_background(g_gr->images().get("images/ui_basic/but1.png")); |
210 | + display_button_.set_perm_pressed(true); |
211 | + button_box_.add(&display_button_, UI::Align::kLeft); |
212 | + button_box_.add(&push_button_, UI::Align::kLeft); |
213 | + button_box_.set_size(w, get_h()); |
214 | + |
215 | + display_button_.sigclicked.connect(boost::bind(&BaseDropdown::toggle_list, this)); |
216 | + push_button_.sigclicked.connect(boost::bind(&BaseDropdown::toggle_list, this)); |
217 | + list_.clicked.connect(boost::bind(&BaseDropdown::set_value, this)); |
218 | + list_.clicked.connect(boost::bind(&BaseDropdown::toggle_list, this)); |
219 | + set_can_focus(true); |
220 | +} |
221 | + |
222 | +BaseDropdown::~BaseDropdown() { |
223 | + clear(); |
224 | +} |
225 | + |
226 | +void BaseDropdown::add(const std::string& name, |
227 | + const uint32_t value, |
228 | + const Image* pic, |
229 | + const bool select_this, |
230 | + const std::string& tooltip_text) { |
231 | + list_.set_size( |
232 | + list_.get_w(), std::min(list_.get_h() + list_.get_lineheight(), max_list_height_)); |
233 | + list_.add(name, value, pic, select_this, tooltip_text); |
234 | + if (select_this) { |
235 | + set_value(); |
236 | + } |
237 | +} |
238 | + |
239 | +bool BaseDropdown::has_selection() const { |
240 | + return list_.has_selection(); |
241 | +} |
242 | + |
243 | +uint32_t BaseDropdown::get_selected() const { |
244 | + return list_.get_selected(); |
245 | +} |
246 | + |
247 | +void BaseDropdown::set_label(const std::string& text) { |
248 | + label_ = text; |
249 | + display_button_.set_title(label_); |
250 | +} |
251 | + |
252 | +void BaseDropdown::set_tooltip(const std::string& text) { |
253 | + tooltip_ = text; |
254 | + display_button_.set_tooltip(tooltip_); |
255 | +} |
256 | + |
257 | +void BaseDropdown::set_enabled(bool on) { |
258 | + set_can_focus(on); |
259 | + push_button_.set_enabled(on); |
260 | + push_button_.set_tooltip(on ? pgettext("dropdown", "Select Item") : ""); |
261 | + display_button_.set_enabled(on); |
262 | + list_.set_visible(false); |
263 | +} |
264 | + |
265 | +void BaseDropdown::set_pos(Vector2i point) { |
266 | + UI::Panel::set_pos(point); |
267 | + list_.set_pos(Vector2i(point.x, point.y + get_h())); |
268 | +} |
269 | + |
270 | +void BaseDropdown::clear() { |
271 | + list_.clear(); |
272 | + list_.set_size(list_.get_w(), 0); |
273 | + set_layout_toplevel(false); |
274 | +} |
275 | + |
276 | +void BaseDropdown::think() { |
277 | + if (list_.is_visible()) { |
278 | + // Autocollapse with a bit of tolerance for the mouse movement to make it less fiddly. |
279 | + if (!(has_focus() || list_.has_focus()) || is_mouse_away()) { |
280 | + toggle_list(); |
281 | + } |
282 | + } |
283 | +} |
284 | + |
285 | +uint32_t BaseDropdown::size() const { |
286 | + return list_.size(); |
287 | +} |
288 | + |
289 | +void BaseDropdown::set_value() { |
290 | + const std::string name = list_.has_selection() ? list_.get_selected_name() : |
291 | + /** TRANSLATORS: Selection in Dropdown menus. */ |
292 | + pgettext("dropdown", "Not Selected"); |
293 | + |
294 | + if (label_.empty()) { |
295 | + display_button_.set_title(name); |
296 | + } else { |
297 | + /** TRANSLATORS: Label: Value. */ |
298 | + display_button_.set_title((boost::format(_("%1%: %2%")) % label_ % (name)).str()); |
299 | + } |
300 | + display_button_.set_tooltip(list_.has_selection() ? list_.get_selected_tooltip() : tooltip_); |
301 | + selected(); |
302 | + current_selection_ = list_.selection_index(); |
303 | +} |
304 | + |
305 | +void BaseDropdown::toggle_list() { |
306 | + list_.set_visible(!list_.is_visible()); |
307 | + if (list_.is_visible()) { |
308 | + list_.move_to_top(); |
309 | + focus(); |
310 | + } |
311 | + // Make sure that the list covers and deactivates the elements below it |
312 | + set_layout_toplevel(list_.is_visible()); |
313 | +} |
314 | + |
315 | +bool BaseDropdown::is_mouse_away() const { |
316 | + return (get_mouse_position().x + mouse_tolerance_) < 0 || |
317 | + get_mouse_position().x > (get_w() + mouse_tolerance_) || |
318 | + (get_mouse_position().y + mouse_tolerance_ / 2) < 0 || |
319 | + get_mouse_position().y > (get_h() + list_.get_h() + mouse_tolerance_); |
320 | +} |
321 | + |
322 | +bool BaseDropdown::handle_key(bool down, SDL_Keysym code) { |
323 | + if (down) { |
324 | + switch (code.sym) { |
325 | + case SDLK_KP_ENTER: |
326 | + case SDLK_RETURN: |
327 | + if (list_.is_visible()) { |
328 | + set_value(); |
329 | + } |
330 | + case SDLK_ESCAPE: |
331 | + if (list_.is_visible()) { |
332 | + list_.select(current_selection_); |
333 | + toggle_list(); |
334 | + return true; |
335 | + } |
336 | + break; |
337 | + case SDLK_DOWN: |
338 | + if (!list_.is_visible() && !is_mouse_away()) { |
339 | + toggle_list(); |
340 | + return true; |
341 | + } |
342 | + break; |
343 | + default: |
344 | + break; // not handled |
345 | + } |
346 | + } |
347 | + if (list_.is_visible()) { |
348 | + return list_.handle_key(down, code); |
349 | + } |
350 | + return false; |
351 | +} |
352 | + |
353 | +} // namespace UI |
354 | |
355 | === added file 'src/ui_basic/dropdown.h' |
356 | --- src/ui_basic/dropdown.h 1970-01-01 00:00:00 +0000 |
357 | +++ src/ui_basic/dropdown.h 2016-10-29 10:23:37 +0000 |
358 | @@ -0,0 +1,166 @@ |
359 | +/* |
360 | + * Copyright (C) 2016 by the Widelands Development Team |
361 | + * |
362 | + * This program is free software; you can redistribute it and/or |
363 | + * modify it under the terms of the GNU General Public License |
364 | + * as published by the Free Software Foundation; either version 2 |
365 | + * of the License, or (at your option) any later version. |
366 | + * |
367 | + * This program is distributed in the hope that it will be useful, |
368 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
369 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
370 | + * GNU General Public License for more details. |
371 | + * |
372 | + * You should have received a copy of the GNU General Public License |
373 | + * along with this program; if not, write to the Free Software |
374 | + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. |
375 | + * |
376 | + */ |
377 | + |
378 | +#ifndef WL_UI_BASIC_DROPDOWN_H |
379 | +#define WL_UI_BASIC_DROPDOWN_H |
380 | + |
381 | +#include <deque> |
382 | +#include <memory> |
383 | + |
384 | +#include <boost/signals2.hpp> |
385 | + |
386 | +#include "ui_basic/box.h" |
387 | +#include "ui_basic/button.h" |
388 | +#include "ui_basic/listselect.h" |
389 | +#include "ui_basic/panel.h" |
390 | + |
391 | +namespace UI { |
392 | + |
393 | +/// Implementation for a dropdown menu that lets the user select a value. |
394 | +class BaseDropdown : public Panel { |
395 | +protected: |
396 | + /// \param parent the parent panel |
397 | + /// \param x the x-position within 'parent' |
398 | + /// \param y the y-position within 'parent' |
399 | + /// \param w the dropdown's width |
400 | + /// \param h the maximum height for the dropdown list |
401 | + /// \param label a label to prefix to the selected entry on the display button. |
402 | + BaseDropdown( |
403 | + Panel* parent, int32_t x, int32_t y, uint32_t w, uint32_t h, const std::string& label); |
404 | + ~BaseDropdown(); |
405 | + |
406 | +public: |
407 | + boost::signals2::signal<void()> selected; |
408 | + |
409 | + /// \return true if an element has been selected from the list |
410 | + bool has_selection() const; |
411 | + |
412 | + /// Sets a label that will be prefixed to the currently selected element's name |
413 | + /// and displayed on the display button. |
414 | + void set_label(const std::string& text); |
415 | + |
416 | + /// Sets the tooltip for the display button. |
417 | + void set_tooltip(const std::string& text); |
418 | + |
419 | + /// Enables/disables the dropdown selection. |
420 | + void set_enabled(bool on); |
421 | + |
422 | + /// Move the dropdown. The dropdown's position is relative to the parent in |
423 | + /// pixels. |
424 | + void set_pos(Vector2i point) override; |
425 | + |
426 | + /// The number of elements listed in the dropdown. |
427 | + uint32_t size() const; |
428 | + |
429 | + /// Handle keypresses |
430 | + bool handle_key(bool down, SDL_Keysym code) override; |
431 | + |
432 | +protected: |
433 | + /// Add an element to the list |
434 | + /// \param name the display name of the entry |
435 | + /// \param value the index of the entry |
436 | + /// \param pic an image to illustrate the entry |
437 | + /// \param select_this whether this element should be selected |
438 | + /// \param tooltip_text a tooltip for this entry |
439 | + void add(const std::string& name, |
440 | + uint32_t value, |
441 | + const Image* pic = nullptr, |
442 | + const bool select_this = false, |
443 | + const std::string& tooltip_text = std::string()); |
444 | + |
445 | + /// \return the index of the selected element |
446 | + uint32_t get_selected() const; |
447 | + |
448 | + /// Removes all elements from the list. |
449 | + void clear(); |
450 | + |
451 | + /// Automatically collapses the list if the mouse gets too far away from the dropdown, or if it |
452 | + /// loses focus. |
453 | + void think() override; |
454 | + |
455 | +private: |
456 | + /// Updates the title and tooltip of the display button and triggers a 'selected' signal. |
457 | + void set_value(); |
458 | + /// Toggles the dropdown list on and off. |
459 | + void toggle_list(); |
460 | + |
461 | + /// Returns true if the mouse pointer left the vicinity of the dropdown. |
462 | + bool is_mouse_away() const; |
463 | + |
464 | + uint32_t max_list_height_; |
465 | + const int mouse_tolerance_; // Allow mouse outside the panel a bit before autocollapse |
466 | + UI::Box button_box_; |
467 | + UI::Button push_button_; |
468 | + UI::Button display_button_; |
469 | + UI::Listselect<uintptr_t> list_; |
470 | + std::string label_; |
471 | + std::string tooltip_; |
472 | + uint32_t current_selection_; |
473 | +}; |
474 | + |
475 | +/// A dropdown menu that lets the user select a value of the datatype 'Entry'. |
476 | +template <typename Entry> class Dropdown : public BaseDropdown { |
477 | +public: |
478 | + /// \param parent the parent panel |
479 | + /// \param x the x-position within 'parent' |
480 | + /// \param y the y-position within 'parent' |
481 | + /// \param w the dropdown's width |
482 | + /// \param h the maximum height for the dropdown list |
483 | + /// \param label a label to prefix to the selected entry on the display button. |
484 | + /// entry. |
485 | + Dropdown(Panel* parent, int32_t x, int32_t y, uint32_t w, uint32_t h, const std::string& label) |
486 | + : BaseDropdown(parent, x, y, w, h, label) { |
487 | + } |
488 | + ~Dropdown() { |
489 | + clear(); |
490 | + } |
491 | + |
492 | + /// Add an element to the list |
493 | + /// \param name the display name of the entry |
494 | + /// \param value the value for the entry |
495 | + /// \param pic an image to illustrate the entry |
496 | + /// \param select_this whether this element should be selected |
497 | + /// \param tooltip_text a tooltip for this entry |
498 | + void add(const std::string& name, |
499 | + Entry value, |
500 | + const Image* pic = nullptr, |
501 | + const bool select_this = false, |
502 | + const std::string& tooltip_text = std::string()) { |
503 | + entry_cache_.push_back(std::unique_ptr<Entry>(new Entry(value))); |
504 | + BaseDropdown::add(name, size(), pic, select_this, tooltip_text); |
505 | + } |
506 | + |
507 | + /// \return the selected element |
508 | + const Entry& get_selected() const { |
509 | + return *entry_cache_[BaseDropdown::get_selected()]; |
510 | + } |
511 | + |
512 | + /// Removes all elements from the list. |
513 | + void clear() { |
514 | + BaseDropdown::clear(); |
515 | + } |
516 | + |
517 | +private: |
518 | + // Contains the actual elements. The BaseDropdown registers the indices only. |
519 | + std::deque<std::unique_ptr<Entry>> entry_cache_; |
520 | +}; |
521 | + |
522 | +} // namespace UI |
523 | + |
524 | +#endif // end of include guard: WL_UI_BASIC_DROPDOWN_H |
525 | |
526 | === modified file 'src/ui_basic/listselect.cc' |
527 | --- src/ui_basic/listselect.cc 2016-10-16 20:35:47 +0000 |
528 | +++ src/ui_basic/listselect.cc 2016-10-29 10:23:37 +0000 |
529 | @@ -31,6 +31,7 @@ |
530 | #include "graphic/text/bidi.h" |
531 | #include "graphic/text_constants.h" |
532 | #include "graphic/text_layout.h" |
533 | +#include "ui_basic/mouse_constants.h" |
534 | #include "wlapplication.h" |
535 | |
536 | constexpr int kMargin = 2; |
537 | @@ -46,11 +47,11 @@ |
538 | * h |
539 | */ |
540 | BaseListselect::BaseListselect(Panel* const parent, |
541 | - int32_t const x, |
542 | - int32_t const y, |
543 | - uint32_t const w, |
544 | - uint32_t const h, |
545 | - bool const show_check) |
546 | + const int32_t x, |
547 | + const int32_t y, |
548 | + const uint32_t w, |
549 | + const uint32_t h, |
550 | + const ListselectLayout selection_mode) |
551 | : Panel(parent, x, y, w, h), |
552 | lineheight_( |
553 | UI::g_fh1->render(as_uifont(UI::g_fh1->fontset()->representative_character()))->height() + |
554 | @@ -60,7 +61,8 @@ |
555 | selection_(no_selection_index()), |
556 | last_click_time_(-10000), |
557 | last_selection_(no_selection_index()), |
558 | - show_check_(show_check) { |
559 | + selection_mode_(selection_mode), |
560 | + background_(nullptr) { |
561 | set_thinks(false); |
562 | |
563 | scrollbar_.moved.connect(boost::bind(&BaseListselect::set_scrollpos, this, _1)); |
564 | @@ -68,7 +70,7 @@ |
565 | scrollbar_.set_pagesize(h - 2 * lineheight_); |
566 | scrollbar_.set_steps(1); |
567 | |
568 | - if (show_check) { |
569 | + if (selection_mode_ == ListselectLayout::kShowCheck) { |
570 | uint32_t pic_h; |
571 | check_pic_ = g_gr->images().get("images/ui_basic/list_selected.png"); |
572 | max_pic_width_ = check_pic_->width(); |
573 | @@ -252,7 +254,7 @@ |
574 | if (selection_ == i) |
575 | return; |
576 | |
577 | - if (show_check_) { |
578 | + if (selection_mode_ == ListselectLayout::kShowCheck) { |
579 | if (selection_ != no_selection_index()) |
580 | entry_records_[selection_]->pic = nullptr; |
581 | entry_records_[i]->pic = check_pic_; |
582 | @@ -274,26 +276,37 @@ |
583 | * \return the ID/entry value of the currently selected item. |
584 | * The entry value is given as a parameter to \ref add |
585 | * |
586 | - * Throws an exception when no item is selected. |
587 | + * Returns no_selection_index() if no item has been selected. |
588 | */ |
589 | uint32_t BaseListselect::get_selected() const { |
590 | - if (selection_ == no_selection_index()) |
591 | - throw NoSelection(); |
592 | - |
593 | - return entry_records_[selection_]->entry_; |
594 | + return selection_ < entry_records_.size() ? entry_records_[selection_]->entry_ : |
595 | + no_selection_index(); |
596 | } |
597 | |
598 | /** |
599 | - * Remove the currently selected item. Throws an exception when no |
600 | - * item is selected. |
601 | + * Remove the currently selected item. Requires an element to have been selected first. |
602 | */ |
603 | void BaseListselect::remove_selected() { |
604 | - if (selection_ == no_selection_index()) |
605 | - throw NoSelection(); |
606 | - |
607 | + assert(selection_ != no_selection_index()); |
608 | remove(selection_); |
609 | } |
610 | |
611 | +/** |
612 | + * \return The name of the currently selected entry. Requires an entry to have been selected. |
613 | + */ |
614 | +const std::string& BaseListselect::get_selected_name() const { |
615 | + assert(selection_ < entry_records_.size()); |
616 | + return entry_records_[selection_]->name; |
617 | +} |
618 | + |
619 | +/** |
620 | + * \return The tooltip for the currently selected entry. Requires an entry to have been selected. |
621 | + */ |
622 | +const std::string& BaseListselect::get_selected_tooltip() const { |
623 | + assert(selection_ < entry_records_.size()); |
624 | + return entry_records_[selection_]->tooltip; |
625 | +} |
626 | + |
627 | uint32_t BaseListselect::get_lineheight() const { |
628 | return lineheight_ + kMargin; |
629 | } |
630 | @@ -302,27 +315,56 @@ |
631 | return scrollbar_.is_enabled() ? get_w() - scrollbar_.get_w() : get_w(); |
632 | } |
633 | |
634 | +void BaseListselect::layout() { |
635 | + scrollbar_.set_size(scrollbar_.get_w(), get_h()); |
636 | + scrollbar_.set_pagesize(get_h() - 2 * get_lineheight()); |
637 | + scrollbar_.set_steps(entry_records_.size() * get_lineheight() - get_h()); |
638 | +} |
639 | + |
640 | /** |
641 | Redraw the listselect box |
642 | */ |
643 | void BaseListselect::draw(RenderTarget& dst) { |
644 | // draw text lines |
645 | - const uint32_t lineheight = get_lineheight(); |
646 | - uint32_t idx = scrollpos_ / lineheight; |
647 | - float y = 1 + idx * lineheight - scrollpos_; |
648 | - |
649 | - dst.brighten_rect(Rectf(0.f, 0.f, get_w(), get_h()), ms_darken_value); |
650 | - |
651 | + const int eff_h = |
652 | + selection_mode_ == ListselectLayout::kDropdown ? get_inner_h() - 4 : get_inner_h(); |
653 | + uint32_t idx = scrollpos_ / get_lineheight(); |
654 | + int y = 1 + idx * get_lineheight() - scrollpos_; |
655 | + |
656 | + if (background_ != nullptr) { |
657 | + dst.tile(Recti(Vector2i(0, 0), get_w(), get_h()), background_, Vector2i(0, 0)); |
658 | + } |
659 | + |
660 | + if (selection_mode_ == ListselectLayout::kDropdown) { |
661 | + RGBAColor black(0, 0, 0, 255); |
662 | + // left edge |
663 | + dst.brighten_rect(Rectf(0.f, 0.f, 2.f, get_h()), BUTTON_EDGE_BRIGHT_FACTOR); |
664 | + // bottom edge |
665 | + dst.fill_rect(Rectf(2.f, get_h() - 2.f, get_eff_w() - 2.f, 1.f), black); |
666 | + dst.fill_rect(Rectf(1.f, get_h() - 1.f, get_eff_w() - 1.f, 1.f), black); |
667 | + // right edge |
668 | + dst.fill_rect(Rectf(get_w() - 2.f, 1.f, 1.f, get_h() - 1.f), black); |
669 | + dst.fill_rect(Rectf(get_w() - 1.f, 0.f, 1.f, get_h()), black); |
670 | + } else { |
671 | + dst.brighten_rect(Rectf(0.f, 0.f, get_w(), get_h()), ms_darken_value); |
672 | + } |
673 | + |
674 | + int lineheight = lineheight_; |
675 | while (idx < entry_records_.size()) { |
676 | - assert(get_h() < std::numeric_limits<int32_t>::max()); |
677 | - if (y >= get_h()) { |
678 | + assert(eff_h < std::numeric_limits<int32_t>::max()); |
679 | + |
680 | + // Don't draw over the bottom edge |
681 | + lineheight = std::min(eff_h - y, lineheight); |
682 | + if (lineheight < 0) { |
683 | break; |
684 | } |
685 | |
686 | const EntryRecord& er = *entry_records_[idx]; |
687 | |
688 | - Vector2f point(1.f, y); |
689 | - uint32_t maxw = get_eff_w() - 2; |
690 | + Vector2f point(selection_mode_ == ListselectLayout::kDropdown ? 3.f : 1.f, y); |
691 | + uint32_t maxw = |
692 | + get_eff_w() - |
693 | + (selection_mode_ == ListselectLayout::kDropdown ? scrollbar_.is_enabled() ? 4 : 5 : 2); |
694 | |
695 | // Highlight the current selected entry |
696 | if (idx == selection_) { |
697 | @@ -347,7 +389,7 @@ |
698 | // Now draw pictures |
699 | if (er.pic) { |
700 | dst.blit(Vector2f(UI::g_fh1->fontset()->is_rtl() ? get_eff_w() - er.pic->width() - 1 : 1, |
701 | - y + (get_lineheight() - er.pic->height()) / 2.f), |
702 | + y + (lineheight_ - er.pic->height()) / 2), |
703 | er.pic); |
704 | } |
705 | |
706 | @@ -368,12 +410,18 @@ |
707 | } |
708 | |
709 | // Fix vertical position for mixed font heights |
710 | - if (get_lineheight() > static_cast<uint32_t>(entry_text_im->height())) { |
711 | + if (lineheight_ > static_cast<uint32_t>(entry_text_im->height())) { |
712 | point.y += (lineheight_ - entry_text_im->height()) / 2; |
713 | } else { |
714 | point.y -= (entry_text_im->height() - lineheight_) / 2; |
715 | } |
716 | |
717 | + // Don't draw over the bottom edge |
718 | + lineheight = std::min(eff_h - static_cast<int>(point.y), lineheight); |
719 | + if (lineheight < 0) { |
720 | + break; |
721 | + } |
722 | + |
723 | // Crop to column width while blitting |
724 | if (static_cast<int>(alignment & UI::Align::kRight) && |
725 | (maxw + picw) < static_cast<uint32_t>(entry_text_im->width())) { |
726 | @@ -382,13 +430,13 @@ |
727 | |
728 | // We want this always on, e.g. for mixed language savegame filenames, or the languages |
729 | // list |
730 | - dst.blitrect(point, entry_text_im, |
731 | - Recti(entry_text_im->width() - maxw + picw, 0, maxw, entry_text_im->height())); |
732 | + dst.blitrect(point, entry_text_im, Recti(entry_text_im->width() - maxw + picw, 0, maxw, |
733 | + entry_text_im->height())); |
734 | } else { |
735 | - dst.blitrect(point, entry_text_im, Recti(0, 0, maxw, entry_text_im->height())); |
736 | + dst.blitrect(point, entry_text_im, Recti(0, 0, maxw, lineheight)); |
737 | } |
738 | |
739 | - y += lineheight; |
740 | + y += get_lineheight(); |
741 | ++idx; |
742 | } |
743 | } |
744 | @@ -445,6 +493,9 @@ |
745 | set_tooltip(""); |
746 | return false; |
747 | } |
748 | + if (selection_mode_ == ListselectLayout::kDropdown) { |
749 | + select(y); |
750 | + } |
751 | set_tooltip(entry_records_.at(y)->tooltip); |
752 | return true; |
753 | } |
754 | |
755 | === modified file 'src/ui_basic/listselect.h' |
756 | --- src/ui_basic/listselect.h 2016-08-04 15:49:05 +0000 |
757 | +++ src/ui_basic/listselect.h 2016-10-29 10:23:37 +0000 |
758 | @@ -32,6 +32,12 @@ |
759 | namespace UI { |
760 | struct Scrollbar; |
761 | |
762 | +enum class ListselectLayout { |
763 | + kPlain, // Highlight the selected element |
764 | + kDropdown, // When the mouse moves, instantly select the element that the mouse hovers over |
765 | + kShowCheck // Show a green arrow in front of the selected element |
766 | +}; |
767 | + |
768 | /** |
769 | * This class defines a list-select box whose entries are defined by a name |
770 | * and an associated numeric ID. |
771 | @@ -39,8 +45,12 @@ |
772 | * Use the \ref Listselect template to use arbitrary IDs. |
773 | */ |
774 | struct BaseListselect : public Panel { |
775 | - BaseListselect( |
776 | - Panel* parent, int32_t x, int32_t y, uint32_t w, uint32_t h, bool show_check = false); |
777 | + BaseListselect(Panel* parent, |
778 | + int32_t x, |
779 | + int32_t y, |
780 | + uint32_t w, |
781 | + uint32_t h, |
782 | + ListselectLayout selection_mode = ListselectLayout::kPlain); |
783 | ~BaseListselect(); |
784 | |
785 | boost::signals2::signal<void(uint32_t)> selected; |
786 | @@ -88,15 +98,23 @@ |
787 | void select(uint32_t i); |
788 | bool has_selection() const; |
789 | |
790 | - struct NoSelection {}; |
791 | uint32_t get_selected() const; |
792 | void remove_selected(); |
793 | |
794 | + const std::string& get_selected_name() const; |
795 | + const std::string& get_selected_tooltip() const; |
796 | + |
797 | + void set_background(const Image* background) { |
798 | + background_ = background; |
799 | + } |
800 | + |
801 | /// Return the total height (text + spacing) occupied by a single line. |
802 | uint32_t get_lineheight() const; |
803 | |
804 | uint32_t get_eff_w() const; |
805 | |
806 | + void layout() override; |
807 | + |
808 | // Drawing and event handling |
809 | void draw(RenderTarget&) override; |
810 | bool handle_mousepress(uint8_t btn, int32_t x, int32_t y) override; |
811 | @@ -132,14 +150,20 @@ |
812 | uint32_t selection_; |
813 | uint32_t last_click_time_; |
814 | uint32_t last_selection_; // for double clicks |
815 | - bool show_check_; // show a green arrow left of selected element |
816 | + ListselectLayout selection_mode_; |
817 | const Image* check_pic_; |
818 | + const Image* background_; |
819 | std::string current_tooltip_; |
820 | }; |
821 | |
822 | template <typename Entry> struct Listselect : public BaseListselect { |
823 | - Listselect(Panel* parent, int32_t x, int32_t y, uint32_t w, uint32_t h, bool show_check = false) |
824 | - : BaseListselect(parent, x, y, w, h, show_check) { |
825 | + Listselect(Panel* parent, |
826 | + int32_t x, |
827 | + int32_t y, |
828 | + uint32_t w, |
829 | + uint32_t h, |
830 | + ListselectLayout selection_mode = ListselectLayout::kPlain) |
831 | + : BaseListselect(parent, x, y, w, h, selection_mode) { |
832 | } |
833 | |
834 | void add(const std::string& name, |
835 | @@ -167,6 +191,10 @@ |
836 | return entry_cache_[BaseListselect::get_selected()]; |
837 | } |
838 | |
839 | + void set_background(const Image* background) { |
840 | + BaseListselect::set_background(background); |
841 | + } |
842 | + |
843 | private: |
844 | std::deque<Entry> entry_cache_; |
845 | }; |
846 | @@ -181,8 +209,13 @@ |
847 | template <typename Entry> struct Listselect<Entry&> : public Listselect<Entry*> { |
848 | using Base = Listselect<Entry*>; |
849 | |
850 | - Listselect(Panel* parent, int32_t x, int32_t y, uint32_t w, uint32_t h, bool show_check = false) |
851 | - : Base(parent, x, y, w, h, show_check) { |
852 | + Listselect(Panel* parent, |
853 | + int32_t x, |
854 | + int32_t y, |
855 | + uint32_t w, |
856 | + uint32_t h, |
857 | + ListselectLayout selection_mode = ListselectLayout::kPlain) |
858 | + : Base(parent, x, y, w, h, selection_mode) { |
859 | } |
860 | |
861 | void add(const std::string& name, |
862 | @@ -207,6 +240,10 @@ |
863 | Entry& get_selected() const { |
864 | return *Base::get_selected(); |
865 | } |
866 | + |
867 | + void set_background(const Image* background) { |
868 | + *Base::set_background(background); |
869 | + } |
870 | }; |
871 | } |
872 | |
873 | |
874 | === modified file 'src/ui_basic/panel.cc' |
875 | --- src/ui_basic/panel.cc 2016-10-16 20:35:47 +0000 |
876 | +++ src/ui_basic/panel.cc 2016-10-29 10:23:37 +0000 |
877 | @@ -820,7 +820,7 @@ |
878 | // Found a child at the position |
879 | if (child->do_mousewheel( |
880 | which, x, y, rel_mouse_pos - Vector2i(child->get_x() + child->get_lborder(), |
881 | - child->get_y() + child->get_tborder()))) { |
882 | + child->get_y() + child->get_tborder()))) { |
883 | return true; |
884 | } |
885 | // Break after the first hit panel in the list. The panels are ordered from top to bottom, |
886 | |
887 | === modified file 'src/ui_basic/panel.h' |
888 | --- src/ui_basic/panel.h 2016-10-16 09:31:42 +0000 |
889 | +++ src/ui_basic/panel.h 2016-10-29 10:23:37 +0000 |
890 | @@ -113,7 +113,7 @@ |
891 | // Geometry |
892 | void set_size(int nw, int nh); |
893 | void set_desired_size(int w, int h); |
894 | - void set_pos(Vector2i); |
895 | + virtual void set_pos(Vector2i); |
896 | virtual void move_inside_parent(); |
897 | virtual void layout(); |
898 | |
899 | |
900 | === modified file 'src/ui_basic/progressbar.cc' |
901 | --- src/ui_basic/progressbar.cc 2016-10-16 20:35:47 +0000 |
902 | +++ src/ui_basic/progressbar.cc 2016-10-29 10:23:37 +0000 |
903 | @@ -67,8 +67,8 @@ |
904 | assert(fraction <= 1); |
905 | |
906 | const RGBColor color = fraction <= 0.33f ? RGBColor(255, 0, 0) : fraction <= 0.67f ? |
907 | - RGBColor(255, 255, 0) : |
908 | - RGBColor(0, 255, 0); |
909 | + RGBColor(255, 255, 0) : |
910 | + RGBColor(0, 255, 0); |
911 | |
912 | // Draw the actual bar |
913 | if (orientation_ == Horizontal) { |
914 | |
915 | === modified file 'src/ui_basic/scrollbar.cc' |
916 | --- src/ui_basic/scrollbar.cc 2016-10-23 12:59:11 +0000 |
917 | +++ src/ui_basic/scrollbar.cc 2016-10-29 10:23:37 +0000 |
918 | @@ -271,7 +271,8 @@ |
919 | dst.fill_rect(Rectf(r.origin() + Vector2f(r.w - 1, 1), 1, r.h - 1), black); |
920 | } else { |
921 | // bottom edge |
922 | - dst.brighten_rect(Rectf(r.origin() + Vector2f(0, r.h - 2), r.w, 2), BUTTON_EDGE_BRIGHT_FACTOR); |
923 | + dst.brighten_rect( |
924 | + Rectf(r.origin() + Vector2f(0, r.h - 2), r.w, 2), BUTTON_EDGE_BRIGHT_FACTOR); |
925 | // right edge |
926 | dst.brighten_rect( |
927 | Rectf(r.origin() + Vector2f(r.w - 2, 0), 2, r.h - 2), BUTTON_EDGE_BRIGHT_FACTOR); |
928 | |
929 | === modified file 'src/ui_basic/slider.cc' |
930 | --- src/ui_basic/slider.cc 2016-10-16 20:35:47 +0000 |
931 | +++ src/ui_basic/slider.cc 2016-10-29 10:23:37 +0000 |
932 | @@ -75,6 +75,7 @@ |
933 | bar_size_(bar_size), |
934 | cursor_size_(cursor_size) { |
935 | set_thinks(false); |
936 | + set_can_focus(true); |
937 | calculate_cursor_position(); |
938 | } |
939 | |
940 | @@ -200,6 +201,7 @@ |
941 | if (enabled_ == enabled) |
942 | return; |
943 | |
944 | + set_can_focus(enabled); |
945 | enabled_ = enabled; |
946 | if (!enabled) { |
947 | pressed_ = false; |
948 | @@ -399,6 +401,7 @@ |
949 | if (btn != SDL_BUTTON_LEFT) |
950 | return false; |
951 | |
952 | + focus(); |
953 | if (x >= cursor_pos_ && x <= cursor_pos_ + cursor_size_) { |
954 | // click on cursor |
955 | cursor_pressed(x); |
956 | @@ -469,6 +472,7 @@ |
957 | if (btn != SDL_BUTTON_LEFT) |
958 | return false; |
959 | |
960 | + focus(); |
961 | if (y >= cursor_pos_ && y <= cursor_pos_ + cursor_size_) { |
962 | // click on cursor |
963 | cursor_pressed(y); |
964 | |
965 | === modified file 'src/ui_basic/tabpanel.cc' |
966 | --- src/ui_basic/tabpanel.cc 2016-10-16 20:35:47 +0000 |
967 | +++ src/ui_basic/tabpanel.cc 2016-10-29 10:23:37 +0000 |
968 | @@ -260,13 +260,13 @@ |
969 | if (pic_background_) { |
970 | if (!tabs_.empty()) { |
971 | dst.tile(Recti(Vector2i(0, 0), tabs_.back()->get_x() + tabs_.back()->get_w(), |
972 | - kTabPanelButtonHeight - 2), |
973 | + kTabPanelButtonHeight - 2), |
974 | pic_background_, Vector2i(get_x(), get_y())); |
975 | } |
976 | assert(kTabPanelButtonHeight - 2 <= get_h()); |
977 | - dst.tile( |
978 | - Recti(Vector2i(0, kTabPanelButtonHeight - 2), get_w(), get_h() - kTabPanelButtonHeight + 2), |
979 | - pic_background_, Vector2i(get_x(), get_y() + kTabPanelButtonHeight - 2)); |
980 | + dst.tile(Recti(Vector2i(0, kTabPanelButtonHeight - 2), get_w(), |
981 | + get_h() - kTabPanelButtonHeight + 2), |
982 | + pic_background_, Vector2i(get_x(), get_y() + kTabPanelButtonHeight - 2)); |
983 | } |
984 | |
985 | RGBColor black(0, 0, 0); |
986 | @@ -279,8 +279,7 @@ |
987 | tab_width = tabs_[idx]->get_w(); |
988 | |
989 | if (highlight_ == idx) { |
990 | - dst.brighten_rect( |
991 | - Rectf(x, 0, tab_width, kTabPanelButtonHeight), MOUSE_OVER_BRIGHT_FACTOR); |
992 | + dst.brighten_rect(Rectf(x, 0, tab_width, kTabPanelButtonHeight), MOUSE_OVER_BRIGHT_FACTOR); |
993 | } |
994 | |
995 | assert(tabs_[idx]->pic); |
996 | @@ -297,13 +296,13 @@ |
997 | uint16_t picture_height = image_scale * tabs_[idx]->pic->height(); |
998 | dst.blitrect_scale( |
999 | Rectf(x + (kTabPanelButtonHeight - picture_width) / 2.f, |
1000 | - (kTabPanelButtonHeight - picture_height) / 2.f, picture_width, picture_height), |
1001 | + (kTabPanelButtonHeight - picture_height) / 2.f, picture_width, picture_height), |
1002 | tabs_[idx]->pic, Recti(0, 0, tabs_[idx]->pic->width(), tabs_[idx]->pic->height()), 1., |
1003 | BlendMode::UseAlpha); |
1004 | } else { |
1005 | - dst.blit( |
1006 | - Vector2f(x + kTabPanelTextMargin, (kTabPanelButtonHeight - tabs_[idx]->pic->height()) / 2.f), |
1007 | - tabs_[idx]->pic, BlendMode::UseAlpha, UI::Align::kLeft); |
1008 | + dst.blit(Vector2f(x + kTabPanelTextMargin, |
1009 | + (kTabPanelButtonHeight - tabs_[idx]->pic->height()) / 2.f), |
1010 | + tabs_[idx]->pic, BlendMode::UseAlpha, UI::Align::kLeft); |
1011 | } |
1012 | |
1013 | // Draw top part of border |
1014 | @@ -317,8 +316,7 @@ |
1015 | dst.brighten_rect( |
1016 | Rectf(x, kTabPanelButtonHeight - 2, tab_width, 2), 2 * BUTTON_EDGE_BRIGHT_FACTOR); |
1017 | else { |
1018 | - dst.brighten_rect( |
1019 | - Rectf(x, kTabPanelButtonHeight - 2, 2, 2), BUTTON_EDGE_BRIGHT_FACTOR); |
1020 | + dst.brighten_rect(Rectf(x, kTabPanelButtonHeight - 2, 2, 2), BUTTON_EDGE_BRIGHT_FACTOR); |
1021 | |
1022 | dst.brighten_rect(Rectf(x + tab_width - 2, kTabPanelButtonHeight - 2, 2, 2), |
1023 | 2 * BUTTON_EDGE_BRIGHT_FACTOR); |
1024 | |
1025 | === modified file 'src/ui_basic/window.cc' |
1026 | --- src/ui_basic/window.cc 2016-10-23 12:59:11 +0000 |
1027 | +++ src/ui_basic/window.cc 2016-10-29 10:23:37 +0000 |
1028 | @@ -226,7 +226,7 @@ |
1029 | Panel& parent = *get_parent(); |
1030 | |
1031 | set_pos(Vector2i((static_cast<int32_t>(parent.get_inner_w()) - get_w()) / 2, |
1032 | - (static_cast<int32_t>(parent.get_inner_h()) - get_h()) / 2)); |
1033 | + (static_cast<int32_t>(parent.get_inner_h()) - get_h()) / 2)); |
1034 | } |
1035 | |
1036 | /** |
1037 | @@ -234,7 +234,8 @@ |
1038 | */ |
1039 | void Window::draw(RenderTarget& dst) { |
1040 | if (!is_minimal()) { |
1041 | - dst.tile(Recti(Vector2i(0, 0), get_inner_w(), get_inner_h()), pic_background_, Vector2i(0, 0)); |
1042 | + dst.tile( |
1043 | + Recti(Vector2i(0, 0), get_inner_w(), get_inner_h()), pic_background_, Vector2i(0, 0)); |
1044 | } |
1045 | } |
1046 | |
1047 | @@ -257,8 +258,9 @@ |
1048 | // top bar |
1049 | static_assert(0 <= HZ_B_CORNER_PIXMAP_LEN, "assert(0 <= HZ_B_CORNER_PIXMAP_LEN) failed."); |
1050 | for (; pos < hz_bar_end_minus_middle; pos += HZ_B_MIDDLE_PIXMAP_LEN) |
1051 | - dst.blitrect(Vector2f(pos, 0), pic_top_, Recti(Vector2i(HZ_B_CORNER_PIXMAP_LEN, 0), |
1052 | - HZ_B_MIDDLE_PIXMAP_LEN, TP_B_PIXMAP_THICKNESS)); |
1053 | + dst.blitrect( |
1054 | + Vector2f(pos, 0), pic_top_, Recti(Vector2i(HZ_B_CORNER_PIXMAP_LEN, 0), |
1055 | + HZ_B_MIDDLE_PIXMAP_LEN, TP_B_PIXMAP_THICKNESS)); |
1056 | |
1057 | // odd pixels of top bar and top right corner |
1058 | const int32_t width = hz_bar_end - pos + HZ_B_CORNER_PIXMAP_LEN; |
1059 | @@ -295,15 +297,16 @@ |
1060 | // left bar |
1061 | static_assert(0 <= VT_B_THINGY_PIXMAP_LEN, "assert(0 <= VT_B_THINGY_PIXMAP_LEN) failed."); |
1062 | for (; pos < vt_bar_end_minus_middle; pos += VT_B_MIDDLE_PIXMAP_LEN) |
1063 | - dst.blitrect( |
1064 | - Vector2f(0, pos), pic_lborder_, Recti(Vector2i(0, VT_B_THINGY_PIXMAP_LEN), |
1065 | - VT_B_PIXMAP_THICKNESS, VT_B_MIDDLE_PIXMAP_LEN)); |
1066 | + dst.blitrect(Vector2f(0, pos), pic_lborder_, |
1067 | + Recti(Vector2i(0, VT_B_THINGY_PIXMAP_LEN), VT_B_PIXMAP_THICKNESS, |
1068 | + VT_B_MIDDLE_PIXMAP_LEN)); |
1069 | |
1070 | // odd pixels of left bar and left bottom thingy |
1071 | const int32_t height = vt_bar_end - pos + VT_B_THINGY_PIXMAP_LEN; |
1072 | assert(0 <= VT_B_TOTAL_PIXMAP_LEN - height); |
1073 | - dst.blitrect(Vector2f(0, pos), pic_lborder_, Recti(Vector2i(0, VT_B_TOTAL_PIXMAP_LEN - height), |
1074 | - VT_B_PIXMAP_THICKNESS, height)); |
1075 | + dst.blitrect( |
1076 | + Vector2f(0, pos), pic_lborder_, |
1077 | + Recti(Vector2i(0, VT_B_TOTAL_PIXMAP_LEN - height), VT_B_PIXMAP_THICKNESS, height)); |
1078 | } |
1079 | |
1080 | { // Right border |
1081 | @@ -320,7 +323,7 @@ |
1082 | for (; pos < vt_bar_end_minus_middle; pos += VT_B_MIDDLE_PIXMAP_LEN) |
1083 | dst.blitrect(Vector2f(right_border_x, pos), pic_rborder_, |
1084 | Recti(Vector2i(0, VT_B_THINGY_PIXMAP_LEN), VT_B_PIXMAP_THICKNESS, |
1085 | - VT_B_MIDDLE_PIXMAP_LEN)); |
1086 | + VT_B_MIDDLE_PIXMAP_LEN)); |
1087 | |
1088 | // odd pixels of right bar and right bottom thingy |
1089 | const int32_t height = vt_bar_end - pos + VT_B_THINGY_PIXMAP_LEN; |
1090 | @@ -340,12 +343,13 @@ |
1091 | for (; pos < hz_bar_end_minus_middle; pos += HZ_B_MIDDLE_PIXMAP_LEN) |
1092 | dst.blitrect(Vector2f(pos, get_h() - BT_B_PIXMAP_THICKNESS), pic_bottom_, |
1093 | Recti(Vector2i(HZ_B_CORNER_PIXMAP_LEN, 0), HZ_B_MIDDLE_PIXMAP_LEN, |
1094 | - BT_B_PIXMAP_THICKNESS)); |
1095 | + BT_B_PIXMAP_THICKNESS)); |
1096 | |
1097 | // odd pixels of bottom bar and bottom right corner |
1098 | const int32_t width = hz_bar_end - pos + HZ_B_CORNER_PIXMAP_LEN; |
1099 | - dst.blitrect(Vector2f(pos, get_h() - BT_B_PIXMAP_THICKNESS), pic_bottom_, |
1100 | - Recti(Vector2i(HZ_B_TOTAL_PIXMAP_LEN - width, 0), width, BT_B_PIXMAP_THICKNESS)); |
1101 | + dst.blitrect( |
1102 | + Vector2f(pos, get_h() - BT_B_PIXMAP_THICKNESS), pic_bottom_, |
1103 | + Recti(Vector2i(HZ_B_TOTAL_PIXMAP_LEN - width, 0), width, BT_B_PIXMAP_THICKNESS)); |
1104 | } |
1105 | } |
1106 | } |
1107 | |
1108 | === modified file 'src/ui_fsmenu/launch_mpg.cc' |
1109 | --- src/ui_fsmenu/launch_mpg.cc 2016-10-25 07:07:14 +0000 |
1110 | +++ src/ui_fsmenu/launch_mpg.cc 2016-10-29 10:23:37 +0000 |
1111 | @@ -650,7 +650,7 @@ |
1112 | suggested_teams_box_->show(map.get_suggested_teams()); |
1113 | suggested_teams_box_->set_pos( |
1114 | Vector2i(suggested_teams_box_->get_x(), |
1115 | - back_.get_y() - padding_ - suggested_teams_box_->get_h() - padding_)); |
1116 | + back_.get_y() - padding_ - suggested_teams_box_->get_h() - padding_)); |
1117 | } |
1118 | |
1119 | /// Show help |
1120 | |
1121 | === modified file 'src/ui_fsmenu/launch_spg.cc' |
1122 | --- src/ui_fsmenu/launch_spg.cc 2016-10-24 14:05:58 +0000 |
1123 | +++ src/ui_fsmenu/launch_spg.cc 2016-10-29 10:23:37 +0000 |
1124 | @@ -33,7 +33,6 @@ |
1125 | #include "logic/game.h" |
1126 | #include "logic/game_controller.h" |
1127 | #include "logic/game_settings.h" |
1128 | -#include "logic/map.h" |
1129 | #include "logic/map_objects/map_object.h" |
1130 | #include "logic/player.h" |
1131 | #include "map_io/map_loader.h" |
1132 | @@ -61,14 +60,12 @@ |
1133 | buth_, |
1134 | g_gr->images().get("images/ui_basic/but1.png"), |
1135 | _("Select map")), |
1136 | - wincondition_(this, |
1137 | - "win_condition", |
1138 | - get_w() * 7 / 10, |
1139 | - get_h() * 4 / 10 + buth_, |
1140 | - butw_, |
1141 | - buth_, |
1142 | - g_gr->images().get("images/ui_basic/but1.png"), |
1143 | - ""), |
1144 | + win_condition_dropdown_(this, |
1145 | + get_w() * 7 / 10, |
1146 | + get_h() * 4 / 10 + buth_, |
1147 | + butw_, |
1148 | + get_h() - get_h() * 4 / 10 - buth_, |
1149 | + ""), |
1150 | back_(this, |
1151 | "back", |
1152 | get_w() * 7 / 10, |
1153 | @@ -127,15 +124,12 @@ |
1154 | is_scenario_(false) { |
1155 | select_map_.sigclicked.connect( |
1156 | boost::bind(&FullscreenMenuLaunchSPG::select_map, boost::ref(*this))); |
1157 | - wincondition_.sigclicked.connect( |
1158 | - boost::bind(&FullscreenMenuLaunchSPG::win_condition_clicked, boost::ref(*this))); |
1159 | + win_condition_dropdown_.selected.connect( |
1160 | + boost::bind(&FullscreenMenuLaunchSPG::win_condition_selected, this)); |
1161 | back_.sigclicked.connect(boost::bind(&FullscreenMenuLaunchSPG::clicked_back, boost::ref(*this))); |
1162 | ok_.sigclicked.connect(boost::bind(&FullscreenMenuLaunchSPG::clicked_ok, boost::ref(*this))); |
1163 | |
1164 | lua_ = new LuaInterface(); |
1165 | - win_condition_scripts_ = settings_->settings().win_condition_scripts; |
1166 | - cur_wincondition_ = -1; |
1167 | - win_condition_clicked(); |
1168 | |
1169 | title_.set_fontsize(UI_FONT_SIZE_BIG); |
1170 | |
1171 | @@ -203,68 +197,113 @@ |
1172 | } |
1173 | |
1174 | /** |
1175 | - * WinCondition button has been pressed |
1176 | - */ |
1177 | -void FullscreenMenuLaunchSPG::win_condition_clicked() { |
1178 | - if (settings_->can_change_map()) { |
1179 | - cur_wincondition_++; |
1180 | - cur_wincondition_ %= win_condition_scripts_.size(); |
1181 | - settings_->set_win_condition_script(win_condition_scripts_[cur_wincondition_]); |
1182 | - } |
1183 | - |
1184 | - win_condition_update(); |
1185 | -} |
1186 | - |
1187 | -/** |
1188 | - * update win conditions information |
1189 | - */ |
1190 | -void FullscreenMenuLaunchSPG::win_condition_update() { |
1191 | + * Fill the dropdown with the available win conditions. |
1192 | + */ |
1193 | +void FullscreenMenuLaunchSPG::update_win_conditions() { |
1194 | + win_condition_dropdown_.clear(); |
1195 | if (settings_->settings().scenario) { |
1196 | - wincondition_.set_title(_("Scenario")); |
1197 | - wincondition_.set_tooltip(_("Win condition is set through the scenario")); |
1198 | + win_condition_dropdown_.set_label(_("Scenario")); |
1199 | + win_condition_dropdown_.set_tooltip(_("Win condition is set through the scenario")); |
1200 | + win_condition_dropdown_.set_enabled(false); |
1201 | } else { |
1202 | - win_condition_load(); |
1203 | - } |
1204 | -} |
1205 | - |
1206 | -/** |
1207 | - * Loads the current win condition script from the settings provider. |
1208 | - * Calls win_condition_clicked() if the current map can't handle the win condition. |
1209 | - */ |
1210 | -void FullscreenMenuLaunchSPG::win_condition_load() { |
1211 | + win_condition_dropdown_.set_label(""); |
1212 | + win_condition_dropdown_.set_tooltip(""); |
1213 | + Widelands::Map map; |
1214 | + std::unique_ptr<Widelands::MapLoader> ml = |
1215 | + map.get_correct_loader(settings_->settings().mapfilename); |
1216 | + if (ml != nullptr) { |
1217 | + ml->preload_map(true); |
1218 | + load_win_conditions(map); |
1219 | + } else { |
1220 | + const std::string error_message = |
1221 | + (boost::format(_("Unable to determine valid win conditions because the map '%s' could " |
1222 | + "not be loaded.")) % |
1223 | + settings_->settings().mapfilename) |
1224 | + .str(); |
1225 | + win_condition_dropdown_.set_label(_("Error")); |
1226 | + win_condition_dropdown_.set_tooltip(error_message); |
1227 | + log("LaunchSPG: No map loader: %s\n", error_message.c_str()); |
1228 | + } |
1229 | + win_condition_dropdown_.set_enabled(true); |
1230 | + } |
1231 | +} |
1232 | + |
1233 | +void FullscreenMenuLaunchSPG::load_win_conditions(const Widelands::Map& map) { |
1234 | + try { |
1235 | + const std::set<std::string> tags = map.get_tags(); |
1236 | + // Make sure that the last win condition is still valid. If not, pick the first one |
1237 | + // available. |
1238 | + if (last_win_condition_.empty()) { |
1239 | + last_win_condition_ = settings_->settings().win_condition_scripts.front(); |
1240 | + } |
1241 | + std::unique_ptr<LuaTable> t = win_condition_if_valid(last_win_condition_, tags); |
1242 | + for (const std::string& win_condition_script : settings_->settings().win_condition_scripts) { |
1243 | + if (t) { |
1244 | + break; |
1245 | + } else { |
1246 | + last_win_condition_ = win_condition_script; |
1247 | + t = win_condition_if_valid(last_win_condition_, tags); |
1248 | + } |
1249 | + } |
1250 | + |
1251 | + // Now fill the dropdown. |
1252 | + for (const std::string& win_condition_script : settings_->settings().win_condition_scripts) { |
1253 | + try { |
1254 | + t = win_condition_if_valid(win_condition_script, tags); |
1255 | + if (t) { |
1256 | + i18n::Textdomain td("win_conditions"); |
1257 | + win_condition_dropdown_.add(_(t->get_string("name")), win_condition_script, nullptr, |
1258 | + win_condition_script == last_win_condition_, |
1259 | + t->get_string("description")); |
1260 | + } |
1261 | + } catch (LuaTableKeyError& e) { |
1262 | + log("LaunchSPG: Error loading win condition: %s %s\n", win_condition_script.c_str(), |
1263 | + e.what()); |
1264 | + } |
1265 | + } |
1266 | + } catch (const std::exception& e) { |
1267 | + const std::string error_message = |
1268 | + (boost::format(_("Unable to determine valid win conditions because the map '%s' " |
1269 | + "could not be loaded.")) % |
1270 | + settings_->settings().mapfilename) |
1271 | + .str(); |
1272 | + win_condition_dropdown_.set_label(_("Error")); |
1273 | + win_condition_dropdown_.set_tooltip(error_message); |
1274 | + log("LaunchSPG: Exception: %s %s\n", error_message.c_str(), e.what()); |
1275 | + } |
1276 | +} |
1277 | + |
1278 | +void FullscreenMenuLaunchSPG::win_condition_selected() { |
1279 | + last_win_condition_ = win_condition_dropdown_.get_selected(); |
1280 | +} |
1281 | + |
1282 | +// TODO(GunChleoc): Turn this into a free standing function. It seems it is not using any state. |
1283 | +std::unique_ptr<LuaTable> |
1284 | +FullscreenMenuLaunchSPG::win_condition_if_valid(const std::string& win_condition_script, |
1285 | + std::set<std::string> tags) const { |
1286 | bool is_usable = true; |
1287 | + std::unique_ptr<LuaTable> t; |
1288 | try { |
1289 | - std::unique_ptr<LuaTable> t = lua_->run_script(settings_->get_win_condition_script()); |
1290 | + t = lua_->run_script(win_condition_script); |
1291 | t->do_not_warn_about_unaccessed_keys(); |
1292 | |
1293 | // Skip this win condition if the map doesn't have all the required tags |
1294 | - if (t->has_key("map_tags") && !settings_->settings().mapfilename.empty()) { |
1295 | - Widelands::Map map; |
1296 | - std::unique_ptr<Widelands::MapLoader> ml = |
1297 | - map.get_correct_loader(settings_->settings().mapfilename); |
1298 | - ml->preload_map(true); |
1299 | + if (t->has_key("map_tags")) { |
1300 | for (const std::string& map_tag : t->get_table("map_tags")->array_entries<std::string>()) { |
1301 | - if (!map.has_tag(map_tag)) { |
1302 | + if (!tags.count(map_tag)) { |
1303 | is_usable = false; |
1304 | break; |
1305 | } |
1306 | } |
1307 | } |
1308 | - |
1309 | - const std::string name = t->get_string("name"); |
1310 | - const std::string descr = t->get_string("description"); |
1311 | - { |
1312 | - i18n::Textdomain td("win_conditions"); |
1313 | - wincondition_.set_title(_(name)); |
1314 | - } |
1315 | - wincondition_.set_tooltip(descr.c_str()); |
1316 | - } catch (LuaTableKeyError&) { |
1317 | - // might be that this is not a win condition after all. |
1318 | - is_usable = false; |
1319 | + } catch (LuaTableKeyError& e) { |
1320 | + log( |
1321 | + "LaunchSPG: Error loading win condition: %s %s\n", win_condition_script.c_str(), e.what()); |
1322 | } |
1323 | if (!is_usable) { |
1324 | - win_condition_clicked(); |
1325 | + t.reset(nullptr); |
1326 | } |
1327 | + return t; |
1328 | } |
1329 | |
1330 | /** |
1331 | @@ -285,6 +324,7 @@ |
1332 | if (is_scenario_) { |
1333 | end_modal<FullscreenMenuBase::MenuTarget>(FullscreenMenuBase::MenuTarget::kScenarioGame); |
1334 | } else { |
1335 | + settings_->set_win_condition_script(win_condition_dropdown_.get_selected()); |
1336 | end_modal<FullscreenMenuBase::MenuTarget>(FullscreenMenuBase::MenuTarget::kNormalGame); |
1337 | } |
1338 | } |
1339 | @@ -310,7 +350,6 @@ |
1340 | |
1341 | select_map_.set_visible(settings_->can_change_map()); |
1342 | select_map_.set_enabled(settings_->can_change_map()); |
1343 | - wincondition_.set_enabled(settings_->can_change_map() && !settings.scenario); |
1344 | |
1345 | if (settings.scenario) { |
1346 | set_scenario_values(); |
1347 | @@ -329,8 +368,6 @@ |
1348 | // update the player description groups |
1349 | for (uint32_t i = 0; i < kMaxPlayers; ++i) |
1350 | players_[i]->refresh(); |
1351 | - |
1352 | - win_condition_update(); |
1353 | } |
1354 | |
1355 | /** |
1356 | @@ -357,6 +394,7 @@ |
1357 | |
1358 | safe_place_for_host(nr_players_); |
1359 | settings_->set_map(mapdata.name, mapdata.filename, nr_players_); |
1360 | + update_win_conditions(); |
1361 | } |
1362 | |
1363 | /** |
1364 | |
1365 | === modified file 'src/ui_fsmenu/launch_spg.h' |
1366 | --- src/ui_fsmenu/launch_spg.h 2016-10-21 08:21:41 +0000 |
1367 | +++ src/ui_fsmenu/launch_spg.h 2016-10-29 10:23:37 +0000 |
1368 | @@ -20,10 +20,13 @@ |
1369 | #ifndef WL_UI_FSMENU_LAUNCH_SPG_H |
1370 | #define WL_UI_FSMENU_LAUNCH_SPG_H |
1371 | |
1372 | +#include <memory> |
1373 | #include <string> |
1374 | |
1375 | #include "graphic/playercolor.h" |
1376 | +#include "logic/map.h" |
1377 | #include "ui_basic/button.h" |
1378 | +#include "ui_basic/dropdown.h" |
1379 | #include "ui_basic/multilinetextarea.h" |
1380 | #include "ui_basic/textarea.h" |
1381 | #include "ui_fsmenu/base.h" |
1382 | @@ -64,9 +67,19 @@ |
1383 | LuaInterface* lua_; |
1384 | |
1385 | void select_map(); |
1386 | - void win_condition_clicked(); |
1387 | - void win_condition_update(); |
1388 | - void win_condition_load(); |
1389 | + /// Loads all win conditions that can be played with the map into the selection dropdown. |
1390 | + /// Disables the dropdown if the map is a scenario. |
1391 | + void update_win_conditions(); |
1392 | + /// Reads the win conditions that are available for the given map and adds the entries to the |
1393 | + /// dropdown. |
1394 | + void load_win_conditions(const Widelands::Map& map); |
1395 | + /// Remembers the win condition that is currently selected in the dropdown. |
1396 | + void win_condition_selected(); |
1397 | + /// If the win condition in 'win_condition_script' can be played with the map tags, |
1398 | + /// parses the win condition and returns it as a std::unique_ptr<LuaTable>. |
1399 | + /// If this win condition can't be played with the map tags, returns a unique_ptr to nullptr. |
1400 | + std::unique_ptr<LuaTable> win_condition_if_valid(const std::string& win_condition_script, |
1401 | + std::set<std::string> tags) const; |
1402 | void set_scenario_values(); |
1403 | void switch_to_position(uint8_t); |
1404 | void safe_place_for_host(uint8_t); |
1405 | @@ -74,7 +87,10 @@ |
1406 | uint32_t butw_; |
1407 | uint32_t buth_; |
1408 | |
1409 | - UI::Button select_map_, wincondition_, back_, ok_; |
1410 | + UI::Button select_map_; |
1411 | + UI::Dropdown<std::string> win_condition_dropdown_; |
1412 | + std::string last_win_condition_; |
1413 | + UI::Button back_, ok_; |
1414 | UI::Button* pos_[kMaxPlayers]; |
1415 | UI::Textarea title_, mapname_; |
1416 | UI::Textarea name_, type_, team_, tribe_, init_, wincondition_type_; |
1417 | @@ -87,8 +103,6 @@ |
1418 | std::string player_save_tribe_[kMaxPlayers]; |
1419 | int8_t nr_players_; |
1420 | bool is_scenario_; |
1421 | - std::vector<std::string> win_condition_scripts_; |
1422 | - uint8_t cur_wincondition_; |
1423 | }; |
1424 | |
1425 | #endif // end of include guard: WL_UI_FSMENU_LAUNCH_SPG_H |
1426 | |
1427 | === modified file 'src/ui_fsmenu/mapselect.cc' |
1428 | --- src/ui_fsmenu/mapselect.cc 2016-10-24 14:04:00 +0000 |
1429 | +++ src/ui_fsmenu/mapselect.cc 2016-10-29 10:23:37 +0000 |
1430 | @@ -81,7 +81,8 @@ |
1431 | new UI::Box(this, tablex_, checkboxes_y_, UI::Box::Horizontal, checkbox_space_, get_w()); |
1432 | |
1433 | // Must be initialized before tag checkboxes |
1434 | - cb_dont_localize_mapnames_ = new UI::Checkbox(vbox, Vector2i(0, 0), _("Show original map names")); |
1435 | + cb_dont_localize_mapnames_ = |
1436 | + new UI::Checkbox(vbox, Vector2i(0, 0), _("Show original map names")); |
1437 | cb_dont_localize_mapnames_->set_state(false); |
1438 | cb_dont_localize_mapnames_->changedto.connect( |
1439 | boost::bind(&FullscreenMenuMapSelect::fill_table, boost::ref(*this))); |
1440 | |
1441 | === modified file 'src/ui_fsmenu/options.cc' |
1442 | --- src/ui_fsmenu/options.cc 2016-10-24 14:04:00 +0000 |
1443 | +++ src/ui_fsmenu/options.cc 2016-10-29 10:23:37 +0000 |
1444 | @@ -144,11 +144,20 @@ |
1445 | box_sound_(&tabs_, 0, 0, UI::Box::Vertical, 0, 0, padding_), |
1446 | box_saving_(&tabs_, 0, 0, UI::Box::Vertical, 0, 0, padding_), |
1447 | box_game_(&tabs_, 0, 0, UI::Box::Vertical, 0, 0, padding_), |
1448 | - box_language_(&tabs_, 0, 0, UI::Box::Vertical, 0, 0, padding_), |
1449 | |
1450 | // Interface options |
1451 | - label_resolution_(&box_interface_, _("In-game resolution"), UI::Align::kLeft), |
1452 | - resolution_list_(&box_interface_, 0, 0, column_width_ / 2, 80, true), |
1453 | + language_dropdown_(&box_interface_, |
1454 | + 0, |
1455 | + 0, |
1456 | + column_width_ / 2, |
1457 | + get_inner_h() - tab_panel_y_ - buth_ - hmargin_ - 4 * padding_, |
1458 | + _("Language")), |
1459 | + resolution_dropdown_(&box_interface_, |
1460 | + 0, |
1461 | + 0, |
1462 | + column_width_ / 2, |
1463 | + get_inner_h() - tab_panel_y_ - 2 * buth_ - hmargin_ - 5 * padding_, |
1464 | + _("In-game resolution")), |
1465 | |
1466 | fullscreen_(&box_interface_, Vector2i(0, 0), _("Fullscreen"), "", column_width_), |
1467 | inputgrab_(&box_interface_, Vector2i(0, 0), _("Grab Input"), "", column_width_), |
1468 | @@ -236,7 +245,8 @@ |
1469 | column_width_), |
1470 | |
1471 | // Game options |
1472 | - auto_roadbuild_mode_(&box_game_, Vector2i(0, 0), _("Start building road after placing a flag")), |
1473 | + auto_roadbuild_mode_( |
1474 | + &box_game_, Vector2i(0, 0), _("Start building road after placing a flag")), |
1475 | show_workarea_preview_(&box_game_, Vector2i(0, 0), _("Show buildings area preview")), |
1476 | transparent_chat_(&box_game_, |
1477 | Vector2i(0, 0), |
1478 | @@ -248,15 +258,6 @@ |
1479 | /** TRANSLATORS: and it also lets you jump to it on the map. */ |
1480 | single_watchwin_(&box_game_, Vector2i(0, 0), _("Use single watchwindow mode")), |
1481 | |
1482 | - // Language options |
1483 | - label_language_(&box_language_, _("Language"), UI::Align::kLeft), |
1484 | - language_list_(&box_language_, |
1485 | - 0, |
1486 | - 0, |
1487 | - column_width_ / 2, |
1488 | - get_inner_h() - tab_panel_y_ - 2 * buth_ - hmargin_ - 5 * padding_, |
1489 | - true), |
1490 | - |
1491 | os_(opt) { |
1492 | // Set up UI Elements |
1493 | title_.set_fontsize(UI_FONT_SIZE_BIG); |
1494 | @@ -266,7 +267,6 @@ |
1495 | tabs_.add("options_sound", _("Sound"), &box_sound_, ""); |
1496 | tabs_.add("options_saving", _("Saving"), &box_saving_, ""); |
1497 | tabs_.add("options_game", _("Game"), &box_game_, ""); |
1498 | - tabs_.add("options_language", _("Language"), &box_language_, ""); |
1499 | |
1500 | // We want the last active tab when "Apply" was clicked. |
1501 | if (os_.active_tab < tabs_.tabs().size()) { |
1502 | @@ -280,11 +280,10 @@ |
1503 | box_sound_.set_size(tabs_.get_inner_w(), tabs_.get_inner_h()); |
1504 | box_saving_.set_size(tabs_.get_inner_w(), tabs_.get_inner_h()); |
1505 | box_game_.set_size(tabs_.get_inner_w(), tabs_.get_inner_h()); |
1506 | - box_language_.set_size(tabs_.get_inner_w(), tabs_.get_inner_h()); |
1507 | |
1508 | // Interface |
1509 | - box_interface_.add(&label_resolution_, UI::Align::kLeft); |
1510 | - box_interface_.add(&resolution_list_, UI::Align::kLeft); |
1511 | + box_interface_.add(&language_dropdown_, UI::Align::kLeft); |
1512 | + box_interface_.add(&resolution_dropdown_, UI::Align::kLeft); |
1513 | box_interface_.add(&fullscreen_, UI::Align::kLeft); |
1514 | box_interface_.add(&inputgrab_, UI::Align::kLeft); |
1515 | box_interface_.add(&sb_maxfps_, UI::Align::kLeft); |
1516 | @@ -312,10 +311,6 @@ |
1517 | box_game_.add(&transparent_chat_, UI::Align::kLeft); |
1518 | box_game_.add(&single_watchwin_, UI::Align::kLeft); |
1519 | |
1520 | - // Language |
1521 | - box_language_.add(&label_language_, UI::Align::kLeft); |
1522 | - box_language_.add(&language_list_, UI::Align::kLeft); |
1523 | - |
1524 | // Bind actions |
1525 | cancel_.sigclicked.connect(boost::bind(&FullscreenMenuOptions::clicked_back, this)); |
1526 | apply_.sigclicked.connect(boost::bind(&FullscreenMenuOptions::clicked_apply, this)); |
1527 | @@ -346,18 +341,18 @@ |
1528 | for (uint32_t i = 0; i < resolutions_.size(); ++i) { |
1529 | const bool selected = resolutions_[i].xres == opt.xres && resolutions_[i].yres == opt.yres; |
1530 | did_select_a_res |= selected; |
1531 | - /** TRANSLATORS: Screen resolution, e.g. 800 x 600*/ |
1532 | - resolution_list_.add( |
1533 | - (boost::format(_("%1% x %2%")) % resolutions_[i].xres % resolutions_[i].yres).str(), |
1534 | - nullptr, nullptr, selected); |
1535 | + resolution_dropdown_.add( |
1536 | + /** TRANSLATORS: Screen resolution, e.g. 800 x 600*/ |
1537 | + (boost::format(_("%1% x %2%")) % resolutions_[i].xres % resolutions_[i].yres).str(), i, |
1538 | + nullptr, selected); |
1539 | } |
1540 | if (!did_select_a_res) { |
1541 | - resolution_list_.add( |
1542 | - (boost::format(_("%1% x %2%")) % opt.xres % opt.yres).str(), nullptr, nullptr, true); |
1543 | uint32_t entry = resolutions_.size(); |
1544 | resolutions_.resize(entry + 1); |
1545 | resolutions_[entry].xres = opt.xres; |
1546 | resolutions_[entry].yres = opt.yres; |
1547 | + resolution_dropdown_.add( |
1548 | + (boost::format(_("%1% x %2%")) % opt.xres % opt.yres).str(), entry, nullptr, true); |
1549 | } |
1550 | |
1551 | fullscreen_.set_state(opt.fullscreen); |
1552 | @@ -386,14 +381,13 @@ |
1553 | |
1554 | // Language options |
1555 | add_languages_to_list(opt.language); |
1556 | - language_list_.focus(); |
1557 | } |
1558 | |
1559 | void FullscreenMenuOptions::add_languages_to_list(const std::string& current_locale) { |
1560 | |
1561 | // We want these two entries on top - the most likely user's choice and the default. |
1562 | - language_list_.add(_("Try system language"), "", nullptr, current_locale == ""); |
1563 | - language_list_.add("English", "en", nullptr, current_locale == "en"); |
1564 | + language_dropdown_.add(_("Try system language"), "", nullptr, current_locale == ""); |
1565 | + language_dropdown_.add("English", "en", nullptr, current_locale == "en"); |
1566 | |
1567 | // Add translation directories to the list |
1568 | std::vector<LanguageEntry> entries; |
1569 | @@ -442,8 +436,8 @@ |
1570 | find_selected_locale(&selected_locale, current_locale); |
1571 | std::sort(entries.begin(), entries.end()); |
1572 | for (const LanguageEntry& entry : entries) { |
1573 | - language_list_.add(entry.descname.c_str(), entry.localename, nullptr, |
1574 | - entry.localename == selected_locale, ""); |
1575 | + language_dropdown_.add(entry.descname.c_str(), entry.localename, nullptr, |
1576 | + entry.localename == selected_locale, ""); |
1577 | } |
1578 | } |
1579 | |
1580 | @@ -454,9 +448,14 @@ |
1581 | OptionsCtrl::OptionsStruct FullscreenMenuOptions::get_values() { |
1582 | // Write all data from UI elements |
1583 | // Interface options |
1584 | - const uint32_t res_index = resolution_list_.selection_index(); |
1585 | - os_.xres = resolutions_[res_index].xres; |
1586 | - os_.yres = resolutions_[res_index].yres; |
1587 | + if (language_dropdown_.has_selection()) { |
1588 | + os_.language = language_dropdown_.get_selected(); |
1589 | + } |
1590 | + if (resolution_dropdown_.has_selection()) { |
1591 | + const uint32_t res_index = resolution_dropdown_.get_selected(); |
1592 | + os_.xres = resolutions_[res_index].xres; |
1593 | + os_.yres = resolutions_[res_index].yres; |
1594 | + } |
1595 | os_.fullscreen = fullscreen_.get_state(); |
1596 | os_.inputgrab = inputgrab_.get_state(); |
1597 | os_.maxfps = sb_maxfps_.get_value(); |
1598 | @@ -484,11 +483,6 @@ |
1599 | os_.transparent_chat = transparent_chat_.get_state(); |
1600 | os_.single_watchwin = single_watchwin_.get_state(); |
1601 | |
1602 | - // Language options |
1603 | - if (language_list_.has_selection()) { |
1604 | - os_.language = language_list_.get_selected(); |
1605 | - } |
1606 | - |
1607 | // Last tab for reloading the options menu |
1608 | os_.active_tab = tabs_.active(); |
1609 | return os_; |
1610 | |
1611 | === modified file 'src/ui_fsmenu/options.h' |
1612 | --- src/ui_fsmenu/options.h 2016-08-04 15:49:05 +0000 |
1613 | +++ src/ui_fsmenu/options.h 2016-10-29 10:23:37 +0000 |
1614 | @@ -27,7 +27,7 @@ |
1615 | |
1616 | #include "ui_basic/button.h" |
1617 | #include "ui_basic/checkbox.h" |
1618 | -#include "ui_basic/listselect.h" |
1619 | +#include "ui_basic/dropdown.h" |
1620 | #include "ui_basic/multilinetextarea.h" |
1621 | #include "ui_basic/spinbox.h" |
1622 | #include "ui_basic/tabpanel.h" |
1623 | @@ -122,11 +122,10 @@ |
1624 | UI::Box box_sound_; |
1625 | UI::Box box_saving_; |
1626 | UI::Box box_game_; |
1627 | - UI::Box box_language_; |
1628 | |
1629 | // Interface options |
1630 | - UI::Textarea label_resolution_; |
1631 | - UI::Listselect<void*> resolution_list_; |
1632 | + UI::Dropdown<std::string> language_dropdown_; |
1633 | + UI::Dropdown<uintptr_t> resolution_dropdown_; |
1634 | UI::Checkbox fullscreen_; |
1635 | UI::Checkbox inputgrab_; |
1636 | UI::SpinBox sb_maxfps_; |
1637 | @@ -154,10 +153,6 @@ |
1638 | UI::Checkbox transparent_chat_; |
1639 | UI::Checkbox single_watchwin_; |
1640 | |
1641 | - // Language options |
1642 | - UI::Textarea label_language_; |
1643 | - UI::Listselect<std::string> language_list_; |
1644 | - |
1645 | OptionsCtrl::OptionsStruct os_; |
1646 | |
1647 | class ScreenResolution { |
1648 | |
1649 | === modified file 'src/wui/game_objectives_menu.cc' |
1650 | --- src/wui/game_objectives_menu.cc 2016-08-04 15:49:05 +0000 |
1651 | +++ src/wui/game_objectives_menu.cc 2016-10-29 10:23:37 +0000 |
1652 | @@ -40,7 +40,7 @@ |
1653 | 580, |
1654 | 5 + OBJECTIVE_LIST + 5 + FULL_OBJECTIVE_TEXT + 5 + BUTTON_HEIGHT + 5, |
1655 | _("Objectives")), |
1656 | - list(this, 5, 5, get_inner_w() - 10, OBJECTIVE_LIST, false), |
1657 | + list(this, 5, 5, get_inner_w() - 10, OBJECTIVE_LIST), |
1658 | objectivetext(this, |
1659 | 5, |
1660 | 130, |
Continuous integration builds have changed state:
Travis build 1345. State: passed. Details: https:/ /travis- ci.org/ widelands/ widelands/ builds/ 161548228. /ci.appveyor. com/project/ widelands- dev/widelands/ build/_ widelands_ dev_widelands_ bug_536489_ dropdown- 1187.
Appveyor build 1187. State: success. Details: https:/