Merge lp:~widelands-dev/widelands/toggle_resources into lp:widelands
- toggle_resources
- Merge into trunk
Status: | Merged | ||||||||
---|---|---|---|---|---|---|---|---|---|
Merged at revision: | 8421 | ||||||||
Proposed branch: | lp:~widelands-dev/widelands/toggle_resources | ||||||||
Merge into: | lp:widelands | ||||||||
Prerequisite: | lp:~widelands-dev/widelands/buildhelp_overlay | ||||||||
Diff against target: |
337 lines (+107/-74) 6 files modified
data/scripting/editor/editor_controls.lua (+2/-0) src/editor/editorinteractive.cc (+37/-19) src/editor/editorinteractive.h (+3/-1) src/graphic/game_renderer.cc (+7/-13) src/wui/field_overlay_manager.cc (+13/-28) src/wui/field_overlay_manager.h (+45/-13) |
||||||||
To merge this branch: | bzr merge lp:~widelands-dev/widelands/toggle_resources | ||||||||
Related bugs: |
|
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
GunChleoc | Approve | ||
Review via email: mp+328958@code.launchpad.net |
Commit message
Make resources toggable in the editor.
- Changes drawing of resources to use a callback instead of a vector of objects which saves some code and might be slightly more efficient.
Description of the change
SirVer (sirver) wrote : | # |
SirVer (sirver) wrote : | # |
Oh, and this is diffbased on https:/
bunnybot (widelandsofficial) wrote : | # |
Continuous integration builds have changed state:
Travis build 2534. State: passed. Details: https:/
Appveyor build 2358. State: success. Details: https:/
toptopple (7010622-q-deactivatedaccount) wrote : | # |
Thanks for taking this up, SirVer!
Looks good to me on first sight (not extensively tested) with one exception:
-- issue: the resource editing tool has to be deactivated while the RESOURCE VIEW is switched OFF. Deactivated means it must not be possible to create or remove any resources on the map.
Extended:
-- the 'q' key is targeted as a shortcut for the resource view button
SirVer (sirver) wrote : | # |
> the 'q' key is targeted as a shortcut for the resource view button
done.
> issue: the resource editing tool has to be deactivated while the RESOURCE VIEW is switched OFF. Deactivated means it must not be possible to create or remove any resources on the map.
I disagree with this design for two reasons:
1) precedence: right now it is already possible to change stuff with UI disabled. For example you can change terrain and this changes the buildhelp even if it is toggled off. You can also add/remove port spaces without the buildhelp being enabled.
2) mental model: right now, the editor is simple: you have exactly one tool selected and you can toggle some visualization (buildhelp) on and off. Both are independent. Now we add a new toggle for another visualization (resources), but it is another toggle, not bound to any of the other state.
If you now make some tools dependent on which visualization is toggled on you get a decision tree that the user has to understand first. I argue this is more complex in code and more complex to use.
toptopple (7010622-q-deactivatedaccount) wrote : | # |
I assume people, including me, can live with such arrangement as you propose. I find it not so convincing, however. While I see your point, I don't quite agree that the current state of the Buildhelp view and related tools has to remain as is.
If I had to decide I probably would prefer the WYSIWIG principle and regard it undesirable you have feasibility to perform editions which you are not visibly aware of, or made aware of, so to speak. So I would agree the edition principles should hold strong for all similar tools, but I rather suggest to investigate to change the Buildhelp settings than allow for invisible editions for other tools.
The modifications of terrain can be ignored as Buildhelp display can be defined as derivation from the current terrain shape, so it's not really that you create or remove Buildhelp patterns. In that there is no tool involved. Only thing relevant are edifice creation or Portspace creation and these would have to be changed and made dependent on Buildhelp visibility. On a sidenote: once Portspaces are indicated by Anchors, as is planned for the future, the Buildhelp arguments falls aside for this tool.
SirVer (sirver) wrote : | # |
>>> from Gun
I think we could further simplify FieldOverlayMan
auto it = overlays_
while (it != overlays_.end() && it->first == c) {
if (buildhelp_ && it->second.level == OverlayLevel:
} else {
result-
++it;
}
}
We should then be able to use a range-bases for loop too?
<<<
OverlayLevel is part of the public API of this class and kLevelForBuildHelp should never be used when 'register_overlay' is called. I therefore opted to not make it part of the enum class to make misuse harder.
Also, you cannot range base over all values in an enum class easily [1] and since no overlay will ever be inserted into 'overlays_' where 'it->second.level == OverlayLevel:
I am not super happy with how this class looks and agree with your push to make it simpler. Unfortunately the correct way I see for this would be to make buildhelp not be special - which would mean that the class will require much more memory which is also not super desirable. Alternatively we could scratch the multimap and instead have callbacks for each OverlayLevel and calculate which overlays are required each frame on the fly. But that is also a pretty deep change. I think for now I am satisfied with having pushed the class a bit towards simplification.
[1] https:/
SirVer (sirver) wrote : | # |
At #12: I think we just disagree here. I prefer a simple mental model, you prefer a tool that always shows what is currently changing. I would then argue: what happens for undo-redo, would this toggle resources on and off? Also, tools like Gimp or photoshop also support my position [1]: If you make a layer invisible in gimp and draw into the image, the layer is modified though there is no visual feedback. I also argue that your implementation is harder to get right because it has more cross-dependencies between tools and UI.
For now I suggest we submit this as is and see if people actually have trouble with the simple mental model - we can always revisit in the future.
toptopple (7010622-q-deactivatedaccount) wrote : | # |
Ok, I don't think I have any personal problem here! ;)
Are you going to work on the second view filter as well? This should switch all objects of the "Immovable" class/tool. And independent thereof: can you implement or make a suggestion for the Immovable View switch button?
SirVer (sirver) wrote : | # |
> Are you going to work on the second view filter as well? This should switch all objects of the "Immovable" class/tool.
This and independent toggling of animals/bobs is done in https:/
> And independent thereof: can you implement or make a suggestion for the Immovable View switch button?
Not sure I understand the question. Are you talking about the button graphic? I am not an artist, I simply copied the icons for the corresponding tool menus. Together with the grouping of the buttons into toggable UI things I think that is sufficiently understandable.
GunChleoc (gunchleoc) wrote : | # |
> OverlayLevel is part of the public API of this class and kLevelForBuildHelp should never be used when 'register_overlay' is called. I therefore opted to not make it part of the enum class to make misuse harder.
This has convinced me, plus that we really don't want to use more memory. Let's keep it as it is then.
Regarding the overlay on/off thing, how about switching it on when the tool gets activated? IMO it might be annoying with the increase/decrease tool if the user had to remember to switch it on in order to see what they are doing.
I agree that deactivating the tool when the overlay is switched off would be confusing for the user and cause some hair-pulling - reactions would probably be "Where on Earth has my bl** tool gone?"
Code LGTM, I'll do a bit of testing.
GunChleoc (gunchleoc) wrote : | # |
Testing works. I'll leave it up to you if you want to real with the auto-switchon in this merge request or in the follow-up branch.
SirVer (sirver) wrote : | # |
> Regarding the overlay on/off thing, how about switching it on when the tool gets activated?
I do not like this sort of covenience magic. It has the ability to annoy users just as much as help them. If we design the hotkeys correctly it will be cheap to toggle the overlays on and off, so this convenience will not add much.
SirVer (sirver) wrote : | # |
@bunnybot merge
GunChleoc (gunchleoc) wrote : | # |
OK, let's leave this as it is then.
Preview Diff
1 | === added file 'data/images/wui/menus/menu_toggle_resources.png' |
2 | Binary files data/images/wui/menus/menu_toggle_resources.png 1970-01-01 00:00:00 +0000 and data/images/wui/menus/menu_toggle_resources.png 2017-08-15 09:35:42 +0000 differ |
3 | === modified file 'data/scripting/editor/editor_controls.lua' |
4 | --- data/scripting/editor/editor_controls.lua 2016-10-01 07:45:42 +0000 |
5 | +++ data/scripting/editor/editor_controls.lua 2017-08-15 09:35:42 +0000 |
6 | @@ -23,6 +23,8 @@ |
7 | toggle_building_spaces_hotkey .. |
8 | -- TRANSLATORS: This is an access key combination. The hotkey is 'p' |
9 | dl(help_format_hotkey("P"), _"Toggle player menu") .. |
10 | + -- TRANSLATORS: This is an access key combination. The hotkey is 'q' |
11 | + dl(help_format_hotkey("Q"), _"Toggle resources display") .. |
12 | -- TRANSLATORS: This is an access key combination. Localize, but do not change the key. |
13 | dl(help_format_hotkey("Ctrl + Z"), _"Undo") .. |
14 | -- TRANSLATORS: This is an access key combination. Localize, but do not change the key. |
15 | |
16 | === modified file 'src/editor/editorinteractive.cc' |
17 | --- src/editor/editorinteractive.cc 2017-08-13 18:38:27 +0000 |
18 | +++ src/editor/editorinteractive.cc 2017-08-15 09:35:42 +0000 |
19 | @@ -115,16 +115,22 @@ |
20 | |
21 | toolbar_.add_space(15); |
22 | |
23 | - add_toolbar_button( |
24 | - "wui/menus/menu_toggle_minimap", "minimap", _("Minimap"), &minimap_registry(), true); |
25 | - minimap_registry().open_window = [this] { toggle_minimap(); }; |
26 | - |
27 | toggle_buildhelp_ = add_toolbar_button( |
28 | "wui/menus/menu_toggle_buildhelp", "buildhelp", _("Show Building Spaces (on/off)")); |
29 | toggle_buildhelp_->sigclicked.connect(boost::bind(&EditorInteractive::toggle_buildhelp, this)); |
30 | - |
31 | - reset_zoom_ = add_toolbar_button("wui/menus/menu_reset_zoom", "reset_zoom", _("Reset zoom")); |
32 | - reset_zoom_->sigclicked.connect([this] { |
33 | + toggle_resources_ = add_toolbar_button( |
34 | + "wui/menus/menu_toggle_resources", "resources", _("Show Resources (on/off)")); |
35 | + toggle_resources_->set_perm_pressed(true); |
36 | + toggle_resources_->sigclicked.connect([this]() { toggle_resources(); }); |
37 | + |
38 | + toolbar_.add_space(15); |
39 | + |
40 | + add_toolbar_button( |
41 | + "wui/menus/menu_toggle_minimap", "minimap", _("Minimap"), &minimap_registry(), true); |
42 | + minimap_registry().open_window = [this] { toggle_minimap(); }; |
43 | + |
44 | + auto zoom = add_toolbar_button("wui/menus/menu_reset_zoom", "reset_zoom", _("Reset zoom")); |
45 | + zoom->sigclicked.connect([this] { |
46 | zoom_around(1.f, Vector2f(get_w() / 2.f, get_h() / 2.f), MapView::Transition::Smooth); |
47 | }); |
48 | |
49 | @@ -326,6 +332,13 @@ |
50 | toggle_buildhelp_->set_perm_pressed(value); |
51 | } |
52 | |
53 | +void EditorInteractive::toggle_resources() { |
54 | + auto* overlay_manager = mutable_field_overlay_manager(); |
55 | + const bool value = !overlay_manager->is_enabled(OverlayLevel::kResource); |
56 | + overlay_manager->set_enabled(OverlayLevel::kResource, value); |
57 | + toggle_resources_->set_perm_pressed(value); |
58 | +} |
59 | + |
60 | bool EditorInteractive::handle_key(bool const down, SDL_Keysym const code) { |
61 | bool handled = InteractiveBase::handle_key(down, code); |
62 | |
63 | @@ -416,22 +429,27 @@ |
64 | handled = true; |
65 | break; |
66 | |
67 | + case SDLK_l: |
68 | + if (code.mod & (KMOD_LCTRL | KMOD_RCTRL)) |
69 | + new MainMenuLoadMap(*this); |
70 | + handled = true; |
71 | + break; |
72 | + |
73 | case SDLK_m: |
74 | minimap_registry().toggle(); |
75 | handled = true; |
76 | break; |
77 | |
78 | - case SDLK_l: |
79 | - if (code.mod & (KMOD_LCTRL | KMOD_RCTRL)) |
80 | - new MainMenuLoadMap(*this); |
81 | - handled = true; |
82 | - break; |
83 | - |
84 | case SDLK_p: |
85 | playermenu_.toggle(); |
86 | handled = true; |
87 | break; |
88 | |
89 | + case SDLK_q: |
90 | + toggle_resources(); |
91 | + handled = true; |
92 | + break; |
93 | + |
94 | case SDLK_s: |
95 | if (code.mod & (KMOD_LCTRL | KMOD_RCTRL)) |
96 | new MainMenuSaveMap(*this); |
97 | @@ -443,6 +461,12 @@ |
98 | handled = true; |
99 | break; |
100 | |
101 | + case SDLK_y: |
102 | + if (code.mod & (KMOD_LCTRL | KMOD_RCTRL)) |
103 | + history_->redo_action(egbase().world()); |
104 | + handled = true; |
105 | + break; |
106 | + |
107 | case SDLK_z: |
108 | if ((code.mod & (KMOD_LCTRL | KMOD_RCTRL)) && (code.mod & (KMOD_LSHIFT | KMOD_RSHIFT))) |
109 | history_->redo_action(egbase().world()); |
110 | @@ -451,12 +475,6 @@ |
111 | handled = true; |
112 | break; |
113 | |
114 | - case SDLK_y: |
115 | - if (code.mod & (KMOD_LCTRL | KMOD_RCTRL)) |
116 | - history_->redo_action(egbase().world()); |
117 | - handled = true; |
118 | - break; |
119 | - |
120 | case SDLK_F1: |
121 | helpmenu_.toggle(); |
122 | handled = true; |
123 | |
124 | === modified file 'src/editor/editorinteractive.h' |
125 | --- src/editor/editorinteractive.h 2017-06-24 08:47:46 +0000 |
126 | +++ src/editor/editorinteractive.h 2017-08-15 09:35:42 +0000 |
127 | @@ -156,6 +156,8 @@ |
128 | |
129 | void on_buildhelp_changed(const bool value) override; |
130 | |
131 | + void toggle_resources(); |
132 | + |
133 | // state variables |
134 | bool need_save_; |
135 | std::vector<PlayerReferences> player_tribe_references_; |
136 | @@ -178,7 +180,7 @@ |
137 | UI::UniqueWindow::Registry helpmenu_; |
138 | |
139 | UI::Button* toggle_buildhelp_; |
140 | - UI::Button* reset_zoom_; |
141 | + UI::Button* toggle_resources_; |
142 | UI::Button* undo_; |
143 | UI::Button* redo_; |
144 | |
145 | |
146 | === modified file 'src/graphic/game_renderer.cc' |
147 | --- src/graphic/game_renderer.cc 2017-08-13 18:02:53 +0000 |
148 | +++ src/graphic/game_renderer.cc 2017-08-15 09:35:42 +0000 |
149 | @@ -210,7 +210,6 @@ |
150 | const Player* player, |
151 | const TextToDraw text_to_draw, |
152 | RenderTarget* dst) { |
153 | - std::vector<FieldOverlayManager::OverlayInfo> overlay_info; |
154 | for (size_t current_index = 0; current_index < fields_to_draw.size(); ++current_index) { |
155 | const FieldsToDraw::Field& field = fields_to_draw.at(current_index); |
156 | if (!field.all_neighbors_valid()) { |
157 | @@ -248,18 +247,13 @@ |
158 | draw_objects_for_formerly_visible_field(field, player_field, zoom, dst); |
159 | } |
160 | |
161 | - const FieldOverlayManager& overlay_manager = egbase.get_ibase()->field_overlay_manager(); |
162 | - { |
163 | - overlay_info.clear(); |
164 | - overlay_manager.get_overlays(field.fcoords, &overlay_info); |
165 | - for (const auto& overlay : overlay_info) { |
166 | - dst->blitrect_scale( |
167 | - Rectf(field.rendertarget_pixel - overlay.hotspot.cast<float>() * zoom, |
168 | - overlay.pic->width() * zoom, overlay.pic->height() * zoom), |
169 | - overlay.pic, Recti(0, 0, overlay.pic->width(), overlay.pic->height()), 1.f, |
170 | - BlendMode::UseAlpha); |
171 | - } |
172 | - } |
173 | + egbase.get_ibase()->field_overlay_manager().foreach_overlay( |
174 | + field.fcoords, [dst, &field, zoom](const Image* pic, const Vector2i& hotspot) { |
175 | + dst->blitrect_scale(Rectf(field.rendertarget_pixel - hotspot.cast<float>() * zoom, |
176 | + pic->width() * zoom, pic->height() * zoom), |
177 | + pic, Recti(0, 0, pic->width(), pic->height()), 1.f, |
178 | + BlendMode::UseAlpha); |
179 | + }); |
180 | } |
181 | } |
182 | |
183 | |
184 | === modified file 'src/wui/field_overlay_manager.cc' |
185 | --- src/wui/field_overlay_manager.cc 2017-08-15 07:47:21 +0000 |
186 | +++ src/wui/field_overlay_manager.cc 2017-08-15 09:35:42 +0000 |
187 | @@ -26,12 +26,6 @@ |
188 | #include "graphic/graphic.h" |
189 | #include "logic/field.h" |
190 | |
191 | -namespace { |
192 | - |
193 | -constexpr int kLevelForBuildHelp = 5; |
194 | - |
195 | -} // namespace |
196 | - |
197 | FieldOverlayManager::FieldOverlayManager() : current_overlay_id_(0) { |
198 | OverlayInfo* buildhelp_info = buildhelp_infos_; |
199 | const char* filenames[] = {"images/wui/overlays/set_flag.png", "images/wui/overlays/small.png", |
200 | @@ -64,28 +58,6 @@ |
201 | buildhelp_ = value; |
202 | } |
203 | |
204 | -void FieldOverlayManager::get_overlays(const Widelands::FCoords& c, |
205 | - std::vector<OverlayInfo>* result) const { |
206 | - auto it = overlays_.lower_bound(c); |
207 | - while (it != overlays_.end() && it->first == c && |
208 | - static_cast<int>(it->second.level) <= kLevelForBuildHelp) { |
209 | - result->emplace_back(it->second.pic, it->second.hotspot); |
210 | - ++it; |
211 | - } |
212 | - |
213 | - if (buildhelp_) { |
214 | - int buildhelp_overlay_index = get_buildhelp_overlay(c); |
215 | - if (buildhelp_overlay_index < Widelands::Field::Buildhelp_None) { |
216 | - result->emplace_back(buildhelp_infos_[buildhelp_overlay_index]); |
217 | - } |
218 | - } |
219 | - |
220 | - while (it != overlays_.end() && it->first == c) { |
221 | - result->emplace_back(it->second.pic, it->second.hotspot); |
222 | - ++it; |
223 | - } |
224 | -} |
225 | - |
226 | int FieldOverlayManager::get_buildhelp_overlay(const Widelands::FCoords& fc) const { |
227 | Widelands::NodeCaps const caps = |
228 | callback_ ? static_cast<Widelands::NodeCaps>(callback_(fc)) : fc.field->nodecaps(); |
229 | @@ -195,3 +167,16 @@ |
230 | ++current_overlay_id_; |
231 | return current_overlay_id_; |
232 | } |
233 | + |
234 | +bool FieldOverlayManager::is_enabled(const OverlayLevel& level) const { |
235 | + return disabled_layers_.count(level) == 0; |
236 | +} |
237 | + |
238 | +void FieldOverlayManager::set_enabled(const OverlayLevel& level, const bool enabled) { |
239 | + if (enabled) { |
240 | + disabled_layers_.erase(level); |
241 | + } else { |
242 | + disabled_layers_.insert(level); |
243 | + } |
244 | +} |
245 | + |
246 | |
247 | === modified file 'src/wui/field_overlay_manager.h' |
248 | --- src/wui/field_overlay_manager.h 2017-08-15 07:47:21 +0000 |
249 | +++ src/wui/field_overlay_manager.h 2017-08-15 09:35:42 +0000 |
250 | @@ -63,17 +63,6 @@ |
251 | /// A unique id identifying a registered overlay. |
252 | using OverlayId = uint32_t; |
253 | |
254 | - /// A overlay as drawn onto the screen. |
255 | - struct OverlayInfo { |
256 | - OverlayInfo() = default; |
257 | - OverlayInfo(const Image* init_pic, const Vector2i& init_hotspot) |
258 | - : pic(init_pic), hotspot(init_hotspot) { |
259 | - } |
260 | - |
261 | - const Image* pic; |
262 | - Vector2i hotspot = Vector2i::zero(); |
263 | - }; |
264 | - |
265 | /// A function returning Field::nodecaps() for the build overlay. This can be |
266 | /// registered to hide or change some of the nodecaps during rendering. |
267 | using CallbackFn = std::function<int32_t(const Widelands::FCoords& coordinates)>; |
268 | @@ -89,6 +78,10 @@ |
269 | /// Register callback function. |
270 | void register_overlay_callback_function(CallbackFn function); |
271 | |
272 | + /// Like 'buildhelp', but for an individual layer. |
273 | + bool is_enabled(const OverlayLevel& level) const; |
274 | + void set_enabled(const OverlayLevel& level, bool value); |
275 | + |
276 | /// Get a unique, unused id that can be passed to register_overlay. |
277 | OverlayId next_overlay_id(); |
278 | |
279 | @@ -111,10 +104,44 @@ |
280 | // TODO(sirver): It would be preferable to just delete and recreate the object. |
281 | void remove_all_overlays(); |
282 | |
283 | - /// Returns the currently registered overlays and the buildhelp for a node. |
284 | - void get_overlays(const Widelands::FCoords& c, std::vector<OverlayInfo>* result) const; |
285 | + /// Calls 'func' for each of the the currently registered and enabled |
286 | + /// overlays and the buildhelp. |
287 | + template <typename T> void foreach_overlay(const Widelands::FCoords& c, T func) const { |
288 | + auto it = overlays_.lower_bound(c); |
289 | + while (it != overlays_.end() && it->first == c && |
290 | + static_cast<int>(it->second.level) <= kLevelForBuildHelp) { |
291 | + if (is_enabled(it->second.level)) { |
292 | + func(it->second.pic, it->second.hotspot); |
293 | + } |
294 | + ++it; |
295 | + } |
296 | + |
297 | + if (buildhelp_) { |
298 | + int buildhelp_overlay_index = get_buildhelp_overlay(c); |
299 | + if (buildhelp_overlay_index < Widelands::Field::Buildhelp_None) { |
300 | + auto& overlay_info = buildhelp_infos_[buildhelp_overlay_index]; |
301 | + func(overlay_info.pic, overlay_info.hotspot); |
302 | + } |
303 | + } |
304 | + |
305 | + while (it != overlays_.end() && it->first == c) { |
306 | + if (is_enabled(it->second.level)) { |
307 | + func(it->second.pic, it->second.hotspot); |
308 | + } |
309 | + ++it; |
310 | + } |
311 | +} |
312 | + |
313 | |
314 | private: |
315 | + static constexpr int kLevelForBuildHelp = 5; |
316 | + |
317 | + /// A overlay as drawn onto the screen. |
318 | + struct OverlayInfo { |
319 | + const Image* pic = nullptr; |
320 | + Vector2i hotspot = Vector2i::zero(); |
321 | + }; |
322 | + |
323 | struct RegisteredOverlays { |
324 | RegisteredOverlays(const OverlayId init_overlay_id, |
325 | const Image* init_pic, |
326 | @@ -137,6 +164,11 @@ |
327 | |
328 | OverlayInfo buildhelp_infos_[Widelands::Field::Buildhelp_None]; |
329 | bool buildhelp_; |
330 | + // We are inverting the logic here, since new layers are by default enabled |
331 | + // and we only support to toggle some of them off. Otherwise whenever a new |
332 | + // layer is added in 'OverlayLevel' we would also need to add it to the |
333 | + // 'enabled_layers_' set on construction. |
334 | + std::set<OverlayLevel> disabled_layers_; |
335 | |
336 | // this callback is used to define where overlays are drawn. |
337 | CallbackFn callback_; |
Might also fix bug 1696064, at least I could not repro it with this branch.
Bug 1649958 contains more discussion about this feature, for example I did not add a hotkey yet (which should be simple to do), because I am unsure about the current consensus.
Not drawing map objects is not part of this branch, but should be relatively simple to implement.