Merge lp:~widelands-dev/widelands/economy-target-profiles into lp:widelands

Proposed by Benedikt Straub
Status: Merged
Merged at revision: 9130
Proposed branch: lp:~widelands-dev/widelands/economy-target-profiles
Merge into: lp:widelands
Diff against target: 2067 lines (+1248/-203)
20 files modified
data/tribes/economy_profiles/atlanteans (+93/-0)
data/tribes/economy_profiles/barbarians (+85/-0)
data/tribes/economy_profiles/empire (+93/-0)
data/tribes/economy_profiles/frisians (+97/-0)
src/logic/filesystem_constants.h (+2/-0)
src/logic/map_objects/tribes/tribe_descr.cc (+0/-40)
src/logic/map_objects/tribes/tribe_descr.h (+0/-11)
src/logic/map_objects/tribes/tribes.cc (+1/-6)
src/logic/map_objects/tribes/ware_descr.h (+2/-4)
src/logic/playercommand.h (+2/-0)
src/notifications/note_ids.h (+1/-0)
src/wui/CMakeLists.txt (+2/-0)
src/wui/economy_options_window.cc (+512/-55)
src/wui/economy_options_window.h (+91/-2)
src/wui/inputqueuedisplay.cc (+6/-6)
src/wui/inputqueuedisplay.h (+1/-1)
src/wui/ware_statistics_menu.cc (+49/-22)
src/wui/ware_statistics_menu.h (+14/-0)
src/wui/waresdisplay.cc (+154/-54)
src/wui/waresdisplay.h (+43/-2)
To merge this branch: bzr merge lp:~widelands-dev/widelands/economy-target-profiles
Reviewer Review Type Date Requested Status
GunChleoc Approve
Toni Förster Approve
kaputtnik (community) testing Approve
Review via email: mp+366987@code.launchpad.net

This proposal supersedes a proposal from 2019-05-04.

Commit message

Users can define and save their own profiles of economy target quantities.
Redesigned the economy options menu.
WaresDisplays and will relayout themselves dynamically on fullscreen switch.

Description of the change

For each tribe, I added two profiles: "Efficiency" is equal to the changes proposed previously, and "Stockpile" is for people who, well, like to stockpile stuff. Additionally there is an unchangeable "Default" pseudo-profile that resets items to the default settings.
To apply a profile, select the items you wish to change and choose the profile from the dropdown. Use "Save" to save your current settings as a profile. The save window also allows you to delete profiles.

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

Continuous integration builds have changed state:

Travis build 4886. State: failed. Details: https://travis-ci.org/widelands/widelands/builds/528245915.
Appveyor build 4667. State: success. Details: https://ci.appveyor.com/project/widelands-dev/widelands/build/_widelands_dev_widelands_economy_target_profiles-4667.

Revision history for this message
GunChleoc (gunchleoc) wrote : Posted in a previous version of this proposal

2 Comments. Will do some testing.

Revision history for this message
Benedikt Straub (nordfriese) wrote : Posted in a previous version of this proposal

Addressed the reviews: The panels now set hgaps to fill the entire available space, and the profiles are stored in tribes/economy_profiles. I added translation markup to the predefined profiles.

Revision history for this message
GunChleoc (gunchleoc) wrote : Posted in a previous version of this proposal

New code LGTM.

I am wondering whether we want to save this as Lua tables? I already have the code finished in the spritesheets branch and could pull it out into a separate branch, since spritesheets aren't ready yet.

Revision history for this message
Benedikt Straub (nordfriese) wrote : Posted in a previous version of this proposal

Is there a reason why you prefer LuaTables over profile? Personally I find Profile much easier to use for configs that the user has no reason to manually edit.

Regarding the suggestion in the bug report – A spinbox would make sense, but what value should it display when several wares with different settings are selected?

Revision history for this message
GunChleoc (gunchleoc) wrote : Posted in a previous version of this proposal

All the tribe's configuration is in LuaTables, so I guess mainly for consistency - I don't feel strongly about this though.

Good point about the value in the spinbox - I still think we should have the possibility of having steps of 10 though. Maybe fake it with 4 buttons and make them look like the spinbox buttons?

Revision history for this message
bunnybot (widelandsofficial) wrote : Posted in a previous version of this proposal

Continuous integration builds have changed state:

Travis build 4901. State: failed. Details: https://travis-ci.org/widelands/widelands/builds/528550026.
Appveyor build 4682. State: failed. Details: https://ci.appveyor.com/project/widelands-dev/widelands/build/_widelands_dev_widelands_economy_target_profiles-4682.

Revision history for this message
bunnybot (widelandsofficial) wrote :

Continuous integration builds have changed state:

Travis build 4904. State: passed. Details: https://travis-ci.org/widelands/widelands/builds/528827570.
Appveyor build 4685. State: failed. Details: https://ci.appveyor.com/project/widelands-dev/widelands/build/_widelands_dev_widelands_economy_target_profiles-4685.

9104. By Nordfriese

Adressed testing review

Revision history for this message
Benedikt Straub (nordfriese) wrote :

Implemented the comments from the bug report

Revision history for this message
kaputtnik (franku) wrote :

Really nice, thanks :-)

It's good that changing a value will show no text in the editbox, so one can immediately see that there is no profile saved for the chosen value(s).

After playing around with the tabs and switching between wares/workers, i found that loading a profile will only apply to the items in the actual tab (wares/workers). Is this correct?

We may need an entry in the help for this window. But this can be done in a separate branch.

review: Approve (testing)
Revision history for this message
Benedikt Straub (nordfriese) wrote :

Yes, all changes are only ever applied to the active tab. The inactive tab will never change.
A helpful explanation should be added to the economy tutorial IMHO.

Revision history for this message
bunnybot (widelandsofficial) wrote :

Continuous integration builds have changed state:

Travis build 4911. State: passed. Details: https://travis-ci.org/widelands/widelands/builds/529823274.
Appveyor build 4692. State: success. Details: https://ci.appveyor.com/project/widelands-dev/widelands/build/_widelands_dev_widelands_economy_target_profiles-4692.

Revision history for this message
Toni Förster (stonerl) wrote :

One major problem. Pressing the save button pauses the game without actually showing, that the game is paused. That might tolerable in single player, but in multiplayer it pauses the game for all participants. This should not happen.

Also get this warning during compiling:

src/wui/economy_options_window.h:40:2: warning: '~EconomyOptionsWindow' overrides a destructor but is not marked 'override'

review: Needs Fixing
Revision history for this message
Toni Förster (stonerl) wrote :

Figured it is worse. The game stalls, it is not paused. The game stalls as long as the save window is open, when closing the windows it fast forwards the time. e.g. Opening the window at 1.30 and leaving it open for 1 minute, means after closing it, the time will fast-forward to 2.30.

9105. By Nordfriese

Do not make save dialog modal

Revision history for this message
Benedikt Straub (nordfriese) wrote :

I made the save dialog modal, my mistake. This was supposed to ensure that only one save window is open at the time and the parent window won´t disappear while it´s open.
Now I ensure this another way; the game progresses normally and input isn´t blocked.
The last revision now has a strange behaviour though that the save window doesn´t close by itself if you destroy the last flag of the economy, which I don´t understand yet…

Revision history for this message
GunChleoc (gunchleoc) wrote :

If you want to ensure that there is onyl 1 save dialog, you can make it a UniqueWindow.

The economy options should control the lifetime of the save window. This is a bit tricky when you merge 2 economies.

Revision history for this message
Benedikt Straub (nordfriese) wrote :

Thanks for the suggestion, I´ll use UniqueWindow then :)

Currently, I am closing the save dialog from the EconomyOptionsWindow destructor. In both cases – closing by right-click and closing after economy destruction – die() is called on the EconomyOptionsWindow, I don´t understand why it makes a difference…

Revision history for this message
GunChleoc (gunchleoc) wrote :

Have a look at EconomyOptionsWindow::on_economy_note - maybe you will need to do some changes there too

9106. By Nordfriese

Fixed save dialog not closing

Revision history for this message
Benedikt Straub (nordfriese) wrote :

I decided against UniqueWindow after all, it seems a bit overkill. All the remaining problems should be fixed now.

Revision history for this message
bunnybot (widelandsofficial) wrote :

Continuous integration builds have changed state:

Travis build 4930. State: passed. Details: https://travis-ci.org/widelands/widelands/builds/530872873.
Appveyor build 4711. State: failed. Details: https://ci.appveyor.com/project/widelands-dev/widelands/build/_widelands_dev_widelands_economy_target_profiles-4711.

Revision history for this message
Toni Förster (stonerl) wrote :

Very good so far, two nits though.

Stockpile and Efficiency Profiles should be changeable but not deletable. Also, when deleting the active profile it should switch back to default. Otherwise, the deleted one is still shown as active and the drop-down list has an empty entry.

Revision history for this message
Toni Förster (stonerl) wrote :

BTW while we're on it. Should we consider presetting all Profiles with sane values?

Revision history for this message
Benedikt Straub (nordfriese) wrote :

> Stockpile and Efficiency Profiles should be changeable but not deletable
Okay, I´ll add a "undeletable" tag to these profiles

> when deleting the active profile it should switch back to default. Otherwise, the deleted one is still shown as active
I thought we had agreed to not show "Default" if the settings are not equal to the default values? Sounds like the bug is that deleted profiles need to be removed from the dropdown and the dropdown set to "" then.

> Should we consider presetting all Profiles with sane values?
I preset "Stockpile" with my own personal preferences, and "Efficiency" is a copy of the old proposal. There is room for discussion of better presets…

9107. By Nordfriese

Made predefined profiles undeletable

Revision history for this message
Benedikt Straub (nordfriese) wrote :

The predefined profiles are now undeletable.
But I can´t reproduce this:

> Otherwise, the deleted one is still shown as active and the drop-down list has an empty entry.

It works fine for me: When deleting the active profile, the dropdown immediately switches to "".

Revision history for this message
Toni Förster (stonerl) wrote :

Have a look at the picture. I deleted the profile test.

https://fosuta.org/pics/empty_profile.png

Revision history for this message
Benedikt Straub (nordfriese) wrote :

I still can´t reproduce, it works fine for me:
https://launchpadlibrarian.net/423268219/wl-economy-profiles.png
(screenshot taken directly after I deleted the profile "Zero")

Revision history for this message
Toni Förster (stonerl) wrote :

Maybe I just failed to explain it. Have a look at the video.

https://fosuta.org/pics/profile.mov

Revision history for this message
Benedikt Straub (nordfriese) wrote :
Revision history for this message
bunnybot (widelandsofficial) wrote :

Continuous integration builds have changed state:

Travis build 4940. State: passed. Details: https://travis-ci.org/widelands/widelands/builds/531134022.
Appveyor build 4721. State: success. Details: https://ci.appveyor.com/project/widelands-dev/widelands/build/_widelands_dev_widelands_economy_target_profiles-4721.

Revision history for this message
GunChleoc (gunchleoc) wrote :

In your video, I don't see you accessing the dropdown in the economy window after the deletion.

Revision history for this message
Benedikt Straub (nordfriese) wrote :

Updated video: https://bugs.launchpad.net/widelands/+bug/1827696/+attachment/5263625/+files/vokoscreen-2019-05-14_14-29-18.mkv
I still get a different result by the same steps and have no idea why :(

Revision history for this message
Toni Förster (stonerl) :
9108. By Nordfriese

Rewrote update_profiles()

Revision history for this message
Benedikt Straub (nordfriese) wrote :

Hmm... it seems like the dropdown´s entries and selection are not updated correctly for some reason which is beyond my understanding. The "Not Selected" message is only the consequence of an upstream failure which I suspect in update_profiles(). I have rewritten it, can you try if the problem still exists?

Revision history for this message
bunnybot (widelandsofficial) wrote :

Continuous integration builds have changed state:

Travis build 4972. State: failed. Details: https://travis-ci.org/widelands/widelands/builds/532432343.
Appveyor build 4753. State: success. Details: https://ci.appveyor.com/project/widelands-dev/widelands/build/_widelands_dev_widelands_economy_target_profiles-4753.

Revision history for this message
Toni Förster (stonerl) wrote :

Still shows "Not Selected" instead of an empty entry.

Revision history for this message
Benedikt Straub (nordfriese) wrote :

I am at a loss. I have now pushed a revision that adds lots of logging output.
When I perform these steps:

– Open the options window from a flag
– Change some values
– Click the save button
– Save the profile as "test"
– (now the dropdown shows "test")
– close the options window
– re-open it (the dropdown shows "test")
– Click the save button
– select profile "test"
– click delete
– (the dropdown now shows "")
– close the save dialog
– open the dropdown (still showing "")
– close the options window
– re-open it (the dropdown shows "")

I attached the log output I received to the bug. Please post the log output you receive so we can compare…
https://launchpadlibrarian.net/423973416/eco-options-profiles-log

9109. By Nordfriese

Added log output

Revision history for this message
Toni Förster (stonerl) wrote :

crash:

NOCOM: EconomyOptionsWindow::update_profiles_select(Default)
       Planning to select »Default«
       Did not select it because it is already selected
NOCOM: EconomyOptionsWindow::update_profiles, applicable »«
       Desired and actual state of the empty »« profile don´t match
NOCOM: EconomyOptionsWindow::update_profiles_needed()
       Added Default
       Added Efficiency
       Added Stockpile
       Added the empty profile
NOCOM: EconomyOptionsWindow::update_profiles_select()
       Planning to select »Project-Id-Version: Widelands
Report-Msgid-Bugs-To: https://wl.widelands.org/wiki/ReportingBugs/
POT-Creation-Date: 2019-04-22 05:17+0000
PO-Revision-Date: 2019-03-03 08:20+0000
Last-Translator: GunChleoc
Language-Team: English (United States) (http://www.transifex.com/widelands/widelands/language/en_US/)
Language: en_US
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Plural-Forms: nplurals=2; plural=(n != 1);
«
       Selected it!
Assertion failed: (dropdown_.has_selection()), function update_profiles_select, file /Users/toni/Launchpad/widelands-repo/working_tree/src/wui/economy_options_window.cc, line 427.
Abort trap: 6

9110. By Nordfriese

Don´t translate »«

Revision history for this message
Benedikt Straub (nordfriese) wrote :

Ah yes, it seems like the translation for the empty string is not as accurate as I expected it...
The dropdown contains translated entries, so I tell it to select the translated name of the applicable profile. I don´t know what the cause of this weird translation is, perhaps your i18n library dislikes _("") expressions? Please retry with the last revision…

Revision history for this message
Toni Förster (stonerl) wrote :

r9110 fixed it. Thanks allot.

9111. By Nordfriese

Removed log output

Revision history for this message
bunnybot (widelandsofficial) wrote :

Continuous integration builds have changed state:

Travis build 5002. State: passed. Details: https://travis-ci.org/widelands/widelands/builds/533445474.
Appveyor build 4783. State: success. Details: https://ci.appveyor.com/project/widelands-dev/widelands/build/_widelands_dev_widelands_economy_target_profiles-4783.

Revision history for this message
GunChleoc (gunchleoc) wrote :

Yep, translating the empty string is a bad idea.

Dropdowns, table and listselect entries have 2 data elements - one is the actual data (Entry value), the other one is a translatable label (const std::string& name). You need to compare the actual data, not the translatable label.

 void add(const std::string& name,
          Entry value,
          const Image* pic = nullptr,
          const bool select_this = false,
          const std::string& tooltip_text = std::string()) {
  entry_cache_.push_back(std::unique_ptr<Entry>(new Entry(value)));
  BaseDropdown::add(name, size(), pic, select_this, tooltip_text);
 }

Revision history for this message
Toni Förster (stonerl) wrote :

I think I forgot to approve this :)

review: Approve
Revision history for this message
GunChleoc (gunchleoc) wrote :

This will break once translations come in.

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

I have just tested the relayouting when fullscreen switching. It works well for warehouses and the stock statistics.

In the ware statistics, the bottom slider and text are cut off.
In the economy window, we get wide gaps between the columns and some extra space on the left and right of the button rows.

Revision history for this message
GunChleoc (gunchleoc) wrote :

Also:

================================================================
==6158==ERROR: AddressSanitizer: heap-use-after-free on address 0x61800003eda0 at pc 0x55fbbb8f1299 bp 0x7ffc346f2020 sp 0x7ffc346f2010
READ of size 8 at 0x61800003eda0 thread T0
    #0 0x55fbbb8f1298 in std::_Deque_iterator<UI::BaseListselect::EntryRecord*, UI::BaseListselect::EntryRecord*&, UI::BaseListselect::EntryRecord**>::_Deque_iterator(std::_Deque_iterator<UI::BaseListselect::EntryRecord*, UI::BaseListselect::EntryRecord*&, UI::BaseListselect::EntryRecord**> const&) /usr/include/c++/7/bits/stl_deque.h:152
    #1 0x55fbbb8f1232 in std::deque<UI::BaseListselect::EntryRecord*, std::allocator<UI::BaseListselect::EntryRecord*> >::begin() /usr/include/c++/7/bits/stl_deque.h:1167
    #2 0x55fbbb8ea1a3 in UI::BaseListselect::clear() ../src/ui_basic/listselect.cc:95
    #3 0x55fbbb8cf6fc in UI::BaseDropdown::~BaseDropdown() ../src/ui_basic/dropdown.cc:151
    #4 0x55fbbb52cb26 in UI::Dropdown<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > >::~Dropdown() ../src/ui_basic/dropdown.h:224
    #5 0x55fbbc30e0ec in EconomyOptionsWindow::~EconomyOptionsWindow() ../src/wui/economy_options_window.cc:126
    #6 0x55fbbc30e17f in EconomyOptionsWindow::~EconomyOptionsWindow() ../src/wui/economy_options_window.cc:134
    #7 0x55fbbb910d1d in UI::Panel::free_children() ../src/ui_basic/panel.cc:128
    #8 0x55fbbb9108ce in UI::Panel::~Panel() ../src/ui_basic/panel.cc:100
    #9 0x55fbbba82826 in InteractiveBase::~InteractiveBase() ../src/wui/interactive_base.cc:189
    #10 0x55fbbbae54c1 in InteractiveGameBase::~InteractiveGameBase() ../src/wui/interactive_gamebase.h:58
    #11 0x55fbbbaea715 in InteractivePlayer::~InteractivePlayer() ../src/wui/interactive_player.h:38
    #12 0x55fbbbaea73d in InteractivePlayer::~InteractivePlayer() ../src/wui/interactive_player.h:38
    #13 0x55fbbb6a9884 in std::default_delete<InteractiveBase>::operator()(InteractiveBase*) const (economy-target-profiles/widelands+0x1150884)
    #14 0x55fbbb6a817e in std::unique_ptr<InteractiveBase, std::default_delete<InteractiveBase> >::reset(InteractiveBase*) /usr/include/c++/7/bits/unique_ptr.h:376
    #15 0x55fbbb69d8f8 in Widelands::EditorGameBase::set_ibase(InteractiveBase*) ../src/logic/editor_game_base.cc:222
    #16 0x55fbbb6b7242 in Widelands::Game::run(UI::ProgressWindow*, Widelands::Game::StartGameType, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const&, bool, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const&) ../src/logic/game.cc:576
    #17 0x55fbbb39b1a0 in WLApplication::new_game() ../src/wlapplication.cc:1350
    #18 0x55fbbb399280 in WLApplication::mainmenu_singleplayer() ../src/wlapplication.cc:1204
    #19 0x55fbbb3983cb in WLApplication::mainmenu() ../src/wlapplication.cc:1110
    #20 0x55fbbb38f3b1 in WLApplication::run() ../src/wlapplication.cc:466
    #21 0x55fbbb38b14e in main ../src/main.cc:44
    #22 0x7f3374bb0b96 in __libc_start_main (/lib/x86_64-linux-gnu/libc.so.6+0x21b96)
    #23 0x55fbbb38afc9 in _start (economy-target-profiles/widelands+0xe31fc9)

Revision history for this message
Benedikt Straub (nordfriese) wrote :

Implemented or replied to the diff comments.

The ASan error is in trunk and can happen everywhere there are dropdowns. For example, when I´m thrown back to the main menu by an error from a fsmenu screen that has one I already got this crash. It can appear whenever destroying a window that contains a dropdown on a lower level. Happens very rarely though, so I did not report it…

> In the ware statistics, the bottom slider and text are cut off.
> In the economy window, we get wide gaps between the columns and some extra space on the left and right of the button rows.
They probably do not layout properly yet… will look into it

9112. By Nordfriese

Adressed code review

9113. By Nordfriese

Fixed layouting issues

Revision history for this message
Benedikt Straub (nordfriese) wrote :

Layouting fixed. Ware statistics now sets a hgap as well

Revision history for this message
GunChleoc (gunchleoc) wrote :

You can now get rid of the second_phase variable entirely. Just check for !anything_selected at the end of the loop, then you can also get rid of

  if (anything_selected) {
   return;
  }

Confirmed that the layouting is fixed. The ware statistics doesn't fit yet at 800x600 resolution, can you give it 1 less row for that?

Also added 1 comment.

Revision history for this message
Benedikt Straub (nordfriese) wrote :

Ware stats menu fixed. It is already too large in trunk, but I made it shorter now.

We still need knowledge of the phase we´re in during the loop so we can know whether to change the target for all wares or only for the selected. Since anything_selected may change during the loop, we need two variables here.

The Dropdown implementation specifies:

template <typename Entry> class Dropdown : public BaseDropdown {
  void add(const std::string& name,
           Entry value, …);
  void select(const Entry& entry);
  const Entry& get_selected() const;
  …
}

(With Entry = std::string here.) Since Entry is the descname (right?), this looks as if the selection is handled by descname rather than internal name…

9114. By Nordfriese

Adressed review. Layouting fixes.

Revision history for this message
GunChleoc (gunchleoc) wrote :

> Since Entry is the descname (right?),

Wrong. The dropdown also comes with documentation:

 /// Add an element to the list
 /// \param name the display name of the entry
 /// \param value the index of the entry
 /// \param pic an image to illustrate the entry. Can be nullptr for textual dropdowns.
 /// \param select_this whether this element should be selected
 /// \param tooltip_text a tooltip for this entry
 ///
 /// Text conventions: Title Case for the 'name', Sentence case for the 'tooltip_text'
 void add(const std::string& name,
          uint32_t value,
          const Image* pic = nullptr,
          const bool select_this = false,
          const std::string& tooltip_text = std::string());

So, the translatable element is:

 /// \param name the display name of the entry

Revision history for this message
GunChleoc (gunchleoc) wrote :

That was the BaseDropdown. The Dropdown template looks like this:

template <typename Entry> class Dropdown : public BaseDropdown {

...

 /// Add an element to the list
 /// \param name the display name of the entry
 /// \param value the value for the entry
 /// \param pic an image to illustrate the entry. Can be nullptr in textual dropdowns
 /// only.
 /// \param select_this whether this element should be selected
 /// \param tooltip_text a tooltip for this entry
 void add(const std::string& name,
          Entry value,
          const Image* pic = nullptr,
          const bool select_this = false,
          const std::string& tooltip_text = std::string()) {

"Entry value" can be any data type you like, "const std::string& name" is the translatable name.

Revision history for this message
Benedikt Straub (nordfriese) wrote :

Okay, my mistake :)
Double-checked my dropdown usage, should all be fine then…

9115. By GunChleoc

Merged trunk.

9116. By GunChleoc

Unlocalize -

9117. By GunChleoc

Fixed compile errors and replaced emplace with insert.

Revision history for this message
GunChleoc (gunchleoc) wrote :

I have merged trunk and pushed some changes. Feel free to merge this branch if you agree with them :)

review: Approve
Revision history for this message
Benedikt Straub (nordfriese) wrote :

Thanks everyone for the reviews, and thanks for the fixes :)

@bunnybot merge

Revision history for this message
bunnybot (widelandsofficial) wrote :

Continuous integration builds have changed state:

Travis build 5080. State: errored. Details: https://travis-ci.org/widelands/widelands/builds/538595965.
Appveyor build 4860. State: failed. Details: https://ci.appveyor.com/project/widelands-dev/widelands/build/_widelands_dev_widelands_economy_target_profiles-4860.

Revision history for this message
bunnybot (widelandsofficial) wrote :

Refusing to merge, since Travis is not green. Use @bunnybot merge force for merging anyways.

Travis build 5080. State: errored. Details: https://travis-ci.org/widelands/widelands/builds/538595965.

Revision history for this message
GunChleoc (gunchleoc) wrote :

Inputqueues again

@bunnybot merge force

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== added file 'data/images/ui_basic/scrollbar_down_fast.png'
2Binary files data/images/ui_basic/scrollbar_down_fast.png 1970-01-01 00:00:00 +0000 and data/images/ui_basic/scrollbar_down_fast.png 2019-05-29 06:43:44 +0000 differ
3=== added file 'data/images/ui_basic/scrollbar_up_fast.png'
4Binary files data/images/ui_basic/scrollbar_up_fast.png 1970-01-01 00:00:00 +0000 and data/images/ui_basic/scrollbar_up_fast.png 2019-05-29 06:43:44 +0000 differ
5=== added directory 'data/tribes/economy_profiles'
6=== added file 'data/tribes/economy_profiles/atlanteans'
7--- data/tribes/economy_profiles/atlanteans 1970-01-01 00:00:00 +0000
8+++ data/tribes/economy_profiles/atlanteans 2019-05-29 06:43:44 +0000
9@@ -0,0 +1,93 @@
10+# Automatically created by Widelands bzr9094[economy-target-profiles] (Debug)
11+
12+[Default]
13+0=_"Efficiency"
14+1=_"Stockpile"
15+
16+[undeletable]
17+0="true"
18+1="true"
19+
20+[0]
21+blackroot_flour="1"
22+atlanteans_bread="20"
23+bread_paddle="0"
24+buckets="0"
25+coal="5"
26+cornmeal="3"
27+diamond="3"
28+fire_tongs="1"
29+fishing_net="2"
30+gold="1"
31+gold_ore="1"
32+gold_thread="0"
33+granite="10"
34+hammer="0"
35+hook_pole="0"
36+hunting_bow="1"
37+iron="5"
38+iron_ore="1"
39+milking_tongs="0"
40+pick="1"
41+planks="1"
42+quartz="3"
43+saw="0"
44+scythe="0"
45+shield_advanced="0"
46+shield_steel="0"
47+shovel="0"
48+smoked_fish="5"
49+smoked_meat="3"
50+spidercloth="5"
51+spider_silk="5"
52+tabard="1"
53+tabard_golden="0"
54+trident_double="0"
55+trident_heavy_double="0"
56+trident_light="1"
57+trident_long="0"
58+trident_steel="0"
59+atlanteans_horse="1"
60+atlanteans_soldier="10"
61+
62+[1]
63+blackroot_flour="20"
64+atlanteans_bread="30"
65+bread_paddle="1"
66+buckets="2"
67+coal="25"
68+cornmeal="20"
69+diamond="10"
70+fire_tongs="1"
71+fishing_net="2"
72+gold="20"
73+gold_ore="15"
74+gold_thread="5"
75+granite="30"
76+hammer="2"
77+hook_pole="1"
78+hunting_bow="1"
79+iron="25"
80+iron_ore="20"
81+milking_tongs="1"
82+pick="3"
83+planks="40"
84+quartz="10"
85+saw="2"
86+scythe="1"
87+shield_advanced="1"
88+shield_steel="1"
89+shovel="2"
90+smoked_fish="40"
91+smoked_meat="25"
92+spidercloth="20"
93+spider_silk="15"
94+tabard="30"
95+tabard_golden="1"
96+trident_double="1"
97+trident_heavy_double="1"
98+trident_light="30"
99+trident_long="1"
100+trident_steel="1"
101+atlanteans_horse="20"
102+atlanteans_soldier="20"
103
104=== added file 'data/tribes/economy_profiles/barbarians'
105--- data/tribes/economy_profiles/barbarians 1970-01-01 00:00:00 +0000
106+++ data/tribes/economy_profiles/barbarians 2019-05-29 06:43:44 +0000
107@@ -0,0 +1,85 @@
108+# Automatically created by Widelands bzr9094[economy-target-profiles] (Debug)
109+
110+[Default]
111+0=_"Efficiency"
112+1=_"Stockpile"
113+
114+[undeletable]
115+0="true"
116+1="true"
117+
118+[0]
119+ax="1"
120+ax_battle="0"
121+ax_broad="0"
122+ax_bronze="0"
123+ax_sharp="0"
124+ax_warriors="0"
125+beer="0"
126+beer_strong="1"
127+blackwood="40"
128+barbarians_bread="5"
129+bread_paddle="0"
130+cloth="10"
131+coal="20"
132+felling_ax="0"
133+fire_tongs="1"
134+fishing_rod="0"
135+gold="1"
136+gold_ore="1"
137+granite="10"
138+grout="1"
139+hammer="1"
140+helmet="0"
141+helmet_mask="0"
142+helmet_warhelm="0"
143+hunting_spear="0"
144+iron="5"
145+iron_ore="5"
146+kitchen_tools="0"
147+meal="5"
148+pick="1"
149+ration="20"
150+scythe="0"
151+shovel="0"
152+snack="0"
153+barbarians_ox="1"
154+barbarians_soldier="10"
155+
156+[1]
157+ax="30"
158+ax_battle="1"
159+ax_broad="1"
160+ax_bronze="1"
161+ax_sharp="1"
162+ax_warriors="1"
163+beer="15"
164+beer_strong="20"
165+blackwood="45"
166+barbarians_bread="25"
167+bread_paddle="1"
168+cloth="10"
169+coal="25"
170+felling_ax="5"
171+fire_tongs="1"
172+fishing_rod="1"
173+gold="20"
174+gold_ore="15"
175+granite="30"
176+grout="20"
177+hammer="2"
178+helmet="1"
179+helmet_mask="1"
180+helmet_warhelm="1"
181+hunting_spear="1"
182+iron="25"
183+iron_ore="20"
184+kitchen_tools="1"
185+meal="15"
186+pick="2"
187+ration="30"
188+scythe="1"
189+shovel="1"
190+snack="20"
191+barbarians_ox="20"
192+barbarians_soldier="20"
193
194=== added file 'data/tribes/economy_profiles/empire'
195--- data/tribes/economy_profiles/empire 1970-01-01 00:00:00 +0000
196+++ data/tribes/economy_profiles/empire 2019-05-29 06:43:44 +0000
197@@ -0,0 +1,93 @@
198+# Automatically created by Widelands bzr9094[economy-target-profiles] (Debug)
199+
200+[Default]
201+0=_"Efficiency"
202+1=_"Stockpile"
203+
204+[undeletable]
205+0="true"
206+1="true"
207+
208+[0]
209+armor="1"
210+armor_chain="1"
211+armor_gilded="1"
212+armor_helmet="30"
213+basket="1"
214+beer="1"
215+empire_bread="20"
216+bread_paddle="0"
217+cloth="15"
218+coal="5"
219+felling_ax="0"
220+fire_tongs="1"
221+fishing_rod="0"
222+flour="20"
223+gold="1"
224+gold_ore="1"
225+granite="10"
226+hammer="0"
227+hunting_spear="0"
228+iron="5"
229+iron_ore="3"
230+kitchen_tools="0"
231+marble="30"
232+marble_column="10"
233+meal="5"
234+meat="20"
235+pick="1"
236+planks="1"
237+ration="20"
238+saw="0"
239+scythe="0"
240+shovel="0"
241+spear="1"
242+spear_advanced="1"
243+spear_heavy="1"
244+spear_war="1"
245+spear_wooden="30"
246+wool="10"
247+empire_donkey="1"
248+empire_soldier="10"
249+
250+[1]
251+armor="1"
252+armor_chain="1"
253+armor_gilded="1"
254+armor_helmet="30"
255+basket="1"
256+beer="20"
257+empire_bread="30"
258+bread_paddle="1"
259+cloth="15"
260+coal="25"
261+felling_ax="3"
262+fire_tongs="1"
263+fishing_rod="1"
264+flour="25"
265+gold="20"
266+gold_ore="15"
267+granite="30"
268+hammer="2"
269+hunting_spear="1"
270+iron="25"
271+iron_ore="20"
272+kitchen_tools="1"
273+marble="35"
274+marble_column="15"
275+meal="20"
276+meat="30"
277+pick="2"
278+planks="40"
279+ration="25"
280+saw="1"
281+scythe="1"
282+shovel="1"
283+spear="1"
284+spear_advanced="1"
285+spear_heavy="1"
286+spear_war="1"
287+spear_wooden="30"
288+wool="15"
289+empire_donkey="20"
290+empire_soldier="20"
291
292=== added file 'data/tribes/economy_profiles/frisians'
293--- data/tribes/economy_profiles/frisians 1970-01-01 00:00:00 +0000
294+++ data/tribes/economy_profiles/frisians 2019-05-29 06:43:44 +0000
295@@ -0,0 +1,97 @@
296+# Automatically created by Widelands bzr9093[economy-target-profiles] (Debug)
297+
298+[Default]
299+0=_"Efficiency"
300+1=_"Stockpile"
301+
302+[undeletable]
303+0="true"
304+1="true"
305+
306+[0]
307+clay="30"
308+brick="40"
309+bread_frisians="20"
310+honey_bread="20"
311+mead="15"
312+fur="10"
313+fur_garment="30"
314+fur_garment_studded="2"
315+fur_garment_golden="2"
316+helmet_golden="2"
317+sword_short="30"
318+sword_long="2"
319+sword_broad="2"
320+sword_double="2"
321+needles="1"
322+basket="1"
323+beer="1"
324+bread_paddle="1"
325+cloth="10"
326+coal="20"
327+felling_ax="0"
328+fire_tongs="1"
329+fish="20"
330+fishing_net="2"
331+gold="1"
332+gold_ore="1"
333+granite="10"
334+hammer="1"
335+helmet="0"
336+hunting_spear="0"
337+iron="5"
338+iron_ore="3"
339+kitchen_tools="0"
340+meal="1"
341+pick="1"
342+ration="20"
343+scythe="0"
344+shovel="0"
345+smoked_fish="20"
346+smoked_meat="10"
347+frisians_reindeer="1"
348+frisians_soldier="10"
349+
350+[1]
351+clay="35"
352+brick="50"
353+bread_frisians="30"
354+honey_bread="30"
355+mead="30"
356+fur="20"
357+fur_garment="30"
358+fur_garment_studded="2"
359+fur_garment_golden="2"
360+helmet_golden="2"
361+sword_short="30"
362+sword_long="2"
363+sword_broad="2"
364+sword_double="2"
365+needles="1"
366+basket="1"
367+beer="30"
368+bread_paddle="1"
369+cloth="10"
370+coal="35"
371+felling_ax="3"
372+fire_tongs="2"
373+fish="40"
374+fishing_net="2"
375+gold="20"
376+gold_ore="15"
377+granite="35"
378+hammer="3"
379+helmet="2"
380+hunting_spear="1"
381+iron="25"
382+iron_ore="20"
383+kitchen_tools="2"
384+meal="10"
385+pick="3"
386+ration="30"
387+scythe="2"
388+shovel="5"
389+smoked_fish="30"
390+smoked_meat="20"
391+frisians_reindeer="20"
392+frisians_soldier="20"
393
394=== modified file 'src/logic/filesystem_constants.h'
395--- src/logic/filesystem_constants.h 2019-04-18 16:50:35 +0000
396+++ src/logic/filesystem_constants.h 2019-05-29 06:43:44 +0000
397@@ -78,4 +78,6 @@
398 /// Filesystem names for config
399 const std::string kConfigFile = "config";
400
401+const std::string kEconomyProfilesDir = "tribes/economy_profiles";
402+
403 #endif // end of include guard: WL_LOGIC_FILESYSTEM_CONSTANTS_H
404
405=== modified file 'src/logic/map_objects/tribes/tribe_descr.cc'
406--- src/logic/map_objects/tribes/tribe_descr.cc 2019-05-27 21:04:13 +0000
407+++ src/logic/map_objects/tribes/tribe_descr.cc 2019-05-29 06:43:44 +0000
408@@ -95,8 +95,6 @@
409 load_roads("busy", &busy_road_paths_);
410
411 items_table = table.get_table("wares_order");
412- wares_order_coords_.resize(tribes_.nrwares());
413- int columnindex = 0;
414 for (const int key : items_table->keys<int>()) {
415 std::vector<DescriptionIndex> column;
416 std::vector<std::string> warenames =
417@@ -110,7 +108,6 @@
418 }
419 wares_.insert(wareindex);
420 column.push_back(wareindex);
421- wares_order_coords_[wareindex] = std::make_pair(columnindex, rowindex);
422 } catch (const WException& e) {
423 throw GameDataError(
424 "Failed adding ware '%s: %s", warenames[rowindex].c_str(), e.what());
425@@ -118,13 +115,10 @@
426 }
427 if (!column.empty()) {
428 wares_order_.push_back(column);
429- ++columnindex;
430 }
431 }
432
433 items_table = table.get_table("workers_order");
434- workers_order_coords_.resize(tribes_.nrworkers());
435- columnindex = 0;
436 for (const int key : items_table->keys<int>()) {
437 std::vector<DescriptionIndex> column;
438 std::vector<std::string> workernames =
439@@ -138,7 +132,6 @@
440 }
441 workers_.insert(workerindex);
442 column.push_back(workerindex);
443- workers_order_coords_[workerindex] = std::make_pair(columnindex, rowindex);
444
445 const WorkerDescr& worker_descr = *tribes_.get_worker_descr(workerindex);
446 if (worker_descr.is_buildable() && worker_descr.buildcost().empty()) {
447@@ -151,7 +144,6 @@
448 }
449 if (!column.empty()) {
450 workers_order_.push_back(column);
451- ++columnindex;
452 }
453 }
454
455@@ -424,38 +416,6 @@
456 return list->second.find(lowest)->second;
457 }
458
459-void TribeDescr::resize_ware_orders(size_t maxLength) {
460- bool need_resize = false;
461-
462- // Check if we actually need to resize.
463- for (WaresOrder::iterator it = wares_order_.begin(); it != wares_order_.end(); ++it) {
464- if (it->size() > maxLength) {
465- need_resize = true;
466- }
467- }
468-
469- // Build new smaller wares_order.
470- if (need_resize) {
471- WaresOrder new_wares_order;
472- for (WaresOrder::iterator it = wares_order_.begin(); it != wares_order_.end(); ++it) {
473- new_wares_order.push_back(std::vector<Widelands::DescriptionIndex>());
474- for (std::vector<Widelands::DescriptionIndex>::iterator it2 = it->begin();
475- it2 != it->end(); ++it2) {
476- if (new_wares_order.rbegin()->size() >= maxLength) {
477- new_wares_order.push_back(std::vector<Widelands::DescriptionIndex>());
478- }
479- new_wares_order.rbegin()->push_back(*it2);
480- wares_order_coords_[*it2].first = new_wares_order.size() - 1;
481- wares_order_coords_[*it2].second = new_wares_order.rbegin()->size() - 1;
482- }
483- }
484-
485- // Remove old array.
486- wares_order_.clear();
487- wares_order_ = new_wares_order;
488- }
489-}
490-
491 void TribeDescr::add_building(const std::string& buildingname) {
492 try {
493 DescriptionIndex index = tribes_.safe_building_index(buildingname);
494
495=== modified file 'src/logic/map_objects/tribes/tribe_descr.h'
496--- src/logic/map_objects/tribes/tribe_descr.h 2019-05-18 13:33:00 +0000
497+++ src/logic/map_objects/tribes/tribe_descr.h 2019-05-29 06:43:44 +0000
498@@ -148,22 +148,13 @@
499 }
500
501 using WaresOrder = std::vector<std::vector<Widelands::DescriptionIndex>>;
502- using WaresOrderCoords = std::vector<std::pair<uint32_t, uint32_t>>;
503 const WaresOrder& wares_order() const {
504 return wares_order_;
505 }
506- const WaresOrderCoords& wares_order_coords() const {
507- return wares_order_coords_;
508- }
509
510 const WaresOrder& workers_order() const {
511 return workers_order_;
512 }
513- const WaresOrderCoords& workers_order_coords() const {
514- return workers_order_coords_;
515- }
516-
517- void resize_ware_orders(size_t maxLength);
518
519 const std::vector<std::string>& get_ship_names() const {
520 return ship_names_;
521@@ -213,9 +204,7 @@
522 std::vector<DescriptionIndex> trainingsites_;
523 // Order and positioning of wares in the warehouse display
524 WaresOrder wares_order_;
525- WaresOrderCoords wares_order_coords_;
526 WaresOrder workers_order_;
527- WaresOrderCoords workers_order_coords_;
528
529 std::vector<Widelands::TribeBasicInfo::Initialization> initializations_;
530
531
532=== modified file 'src/logic/map_objects/tribes/tribes.cc'
533--- src/logic/map_objects/tribes/tribes.cc 2019-05-22 11:23:14 +0000
534+++ src/logic/map_objects/tribes/tribes.cc 2019-05-29 06:43:44 +0000
535@@ -347,14 +347,9 @@
536 // Calculate the trainingsites proportions.
537 postload_calculate_trainingsites_proportions();
538
539- // Resize the configuration of our wares if they won't fit in the current window (12 = info label
540- // size).
541- // Also, do some final checks on the gamedata
542- int number = (g_gr->get_yres() - 290) / (WARE_MENU_PIC_HEIGHT + WARE_MENU_PIC_PAD_Y + 12);
543+ // Some final checks on the gamedata
544 for (DescriptionIndex i = 0; i < tribes_->size(); ++i) {
545 TribeDescr* tribe_descr = tribes_->get_mutable(i);
546- tribe_descr->resize_ware_orders(number);
547-
548 // Verify that the preciousness has been set for all of the tribe's wares
549 for (const DescriptionIndex wi : tribe_descr->wares()) {
550 if (tribe_descr->get_ware_descr(wi)->ai_hints().preciousness(tribe_descr->name()) ==
551
552=== modified file 'src/logic/map_objects/tribes/ware_descr.h'
553--- src/logic/map_objects/tribes/ware_descr.h 2019-05-22 11:23:14 +0000
554+++ src/logic/map_objects/tribes/ware_descr.h 2019-05-29 06:43:44 +0000
555@@ -35,10 +35,8 @@
556 class Image;
557 class LuaTable;
558
559-#define WARE_MENU_PIC_WIDTH 24 //!< Default width for ware's menu icons
560-#define WARE_MENU_PIC_HEIGHT 24 //!< Default height for ware's menu icons
561-#define WARE_MENU_PIC_PAD_X 3 //!< Default padding between menu icons
562-#define WARE_MENU_PIC_PAD_Y 4 //!< Default padding between menu icons
563+constexpr int kWareMenuPicWidth = 24; //!< Default width for ware's menu icons
564+constexpr int kWareMenuPicHeight = 24; //!< Default height for ware's menu icons
565
566 namespace Widelands {
567
568
569=== modified file 'src/logic/playercommand.h'
570--- src/logic/playercommand.h 2019-05-07 12:14:02 +0000
571+++ src/logic/playercommand.h 2019-05-29 06:43:44 +0000
572@@ -581,6 +581,7 @@
573 uint32_t permanent_;
574 };
575
576+// TODO(Nordfriese): CmdResetWareTargetQuantity can be removed when we next break savegame compatibility
577 struct CmdResetWareTargetQuantity : public CmdChangeTargetQuantity {
578 CmdResetWareTargetQuantity() : CmdChangeTargetQuantity() {
579 }
580@@ -629,6 +630,7 @@
581 uint32_t permanent_;
582 };
583
584+// TODO(Nordfriese): CmdResetWorkerTargetQuantity can be removed when we next break savegame compatibility
585 struct CmdResetWorkerTargetQuantity : public CmdChangeTargetQuantity {
586 CmdResetWorkerTargetQuantity() : CmdChangeTargetQuantity() {
587 }
588
589=== modified file 'src/notifications/note_ids.h'
590--- src/notifications/note_ids.h 2019-02-23 11:00:49 +0000
591+++ src/notifications/note_ids.h 2019-05-29 06:43:44 +0000
592@@ -36,6 +36,7 @@
593 Ship,
594 Building,
595 Economy,
596+ EconomyProfile,
597 GraphicResolutionChanged,
598 NoteExpeditionCanceled,
599 Sound,
600
601=== modified file 'src/wui/CMakeLists.txt'
602--- src/wui/CMakeLists.txt 2019-05-12 07:45:59 +0000
603+++ src/wui/CMakeLists.txt 2019-05-29 06:43:44 +0000
604@@ -46,8 +46,10 @@
605 graphic
606 logic
607 logic_commands
608+ logic_filesystem_constants
609 logic_map_objects
610 notifications
611+ profile
612 ui_basic
613 wui_waresdisplay
614 )
615
616=== modified file 'src/wui/economy_options_window.cc'
617--- src/wui/economy_options_window.cc 2019-05-01 07:20:25 +0000
618+++ src/wui/economy_options_window.cc 2019-05-29 06:43:44 +0000
619@@ -19,36 +19,104 @@
620
621 #include "wui/economy_options_window.h"
622
623+#include <memory>
624+
625 #include <boost/lexical_cast.hpp>
626
627 #include "graphic/graphic.h"
628 #include "logic/editor_game_base.h"
629+#include "logic/filesystem_constants.h"
630 #include "logic/map_objects/tribes/ware_descr.h"
631 #include "logic/map_objects/tribes/worker_descr.h"
632 #include "logic/player.h"
633 #include "logic/playercommand.h"
634-#include "ui_basic/button.h"
635+#include "profile/profile.h"
636+#include "ui_basic/messagebox.h"
637
638 static const char pic_tab_wares[] = "images/wui/buildings/menu_tab_wares.png";
639 static const char pic_tab_workers[] = "images/wui/buildings/menu_tab_workers.png";
640
641+constexpr int kDesiredWidth = 216;
642+
643 EconomyOptionsWindow::EconomyOptionsWindow(UI::Panel* parent,
644 Widelands::Economy* economy,
645 bool can_act)
646 : UI::Window(parent, "economy_options", 0, 0, 0, 0, _("Economy options")),
647+ main_box_(this, 0, 0, UI::Box::Vertical),
648 serial_(economy->serial()),
649 player_(&economy->owner()),
650 tabpanel_(this, UI::TabPanelStyle::kWuiDark),
651- ware_panel_(new EconomyOptionsPanel(&tabpanel_, serial_, player_, can_act, Widelands::wwWARE)),
652+ ware_panel_(new EconomyOptionsPanel(&tabpanel_, this, serial_, player_, can_act, Widelands::wwWARE, kDesiredWidth)),
653 worker_panel_(
654- new EconomyOptionsPanel(&tabpanel_, serial_, player_, can_act, Widelands::wwWORKER)) {
655- set_center_panel(&tabpanel_);
656+ new EconomyOptionsPanel(&tabpanel_, this, serial_, player_, can_act, Widelands::wwWORKER, kDesiredWidth)),
657+ dropdown_box_(this, 0, 0, UI::Box::Horizontal),
658+ dropdown_(&dropdown_box_, 0, 0, 174, 200, 34, "", UI::DropdownType::kTextual, UI::PanelStyle::kWui),
659+ time_last_thought_(0),
660+ save_profile_dialog_(nullptr) {
661+ set_center_panel(&main_box_);
662
663 tabpanel_.add("wares", g_gr->images().get(pic_tab_wares), ware_panel_, _("Wares"));
664 tabpanel_.add("workers", g_gr->images().get(pic_tab_workers), worker_panel_, _("Workers"));
665+
666+ UI::Box* buttons = new UI::Box(this, 0, 0, UI::Box::Horizontal);
667+ UI::Button* b = new UI::Button(buttons, "decrease_target_fast", 0, 0, 44, 28, UI::ButtonStyle::kWuiSecondary,
668+ g_gr->images().get("images/ui_basic/scrollbar_down_fast.png"), _("Decrease target by 10"));
669+ b->sigclicked.connect([this] { change_target(-10); });
670+ buttons->add(b);
671+ b->set_repeating(true);
672+ buttons->add_space(8);
673+ b = new UI::Button(buttons, "decrease_target", 0, 0, 44, 28, UI::ButtonStyle::kWuiSecondary,
674+ g_gr->images().get("images/ui_basic/scrollbar_down.png"), _("Decrease target"));
675+ b->sigclicked.connect([this] { change_target(-1); });
676+ buttons->add(b);
677+ b->set_repeating(true);
678+ buttons->add_space(24);
679+
680+ b = new UI::Button(buttons, "increase_target", 0, 0, 44, 28, UI::ButtonStyle::kWuiSecondary,
681+ g_gr->images().get("images/ui_basic/scrollbar_up.png"), _("Increase target"));
682+ b->sigclicked.connect([this] { change_target(1); });
683+ buttons->add(b);
684+ b->set_repeating(true);
685+ buttons->add_space(8);
686+ b = new UI::Button(buttons, "increase_target_fast", 0, 0, 44, 28, UI::ButtonStyle::kWuiSecondary,
687+ g_gr->images().get("images/ui_basic/scrollbar_up_fast.png"), _("Increase target by 10"));
688+ b->sigclicked.connect([this] { change_target(10); });
689+ buttons->add(b);
690+ b->set_repeating(true);
691+
692+ dropdown_.set_tooltip(_("Profile to apply to the selected items"));
693+ dropdown_box_.set_size(40, 20); // Prevent assert failures
694+ dropdown_box_.add(&dropdown_, UI::Box::Resizing::kFullSize);
695+ dropdown_.selected.connect([this] { reset_target(); });
696+
697+ b = new UI::Button(&dropdown_box_, "save_targets", 0, 0, 34, 34, UI::ButtonStyle::kWuiMenu,
698+ g_gr->images().get("images/wui/menus/menu_save_game.png"), _("Save target settings"));
699+ b->sigclicked.connect([this] { create_target(); });
700+ dropdown_box_.add_space(8);
701+ dropdown_box_.add(b);
702+
703+ main_box_.add(&tabpanel_, UI::Box::Resizing::kAlign, UI::Align::kCenter);
704+ main_box_.add_space(8);
705+ main_box_.add(buttons, UI::Box::Resizing::kAlign, UI::Align::kCenter);
706+ main_box_.add_space(8);
707+ main_box_.add(&dropdown_box_, UI::Box::Resizing::kAlign, UI::Align::kCenter);
708+
709 economy->set_has_window(true);
710 economynotes_subscriber_ = Notifications::subscribe<Widelands::NoteEconomy>(
711 [this](const Widelands::NoteEconomy& note) { on_economy_note(note); });
712+ profilenotes_subscriber_ = Notifications::subscribe<NoteEconomyProfile>(
713+ [this](const NoteEconomyProfile& n) {
714+ if (n.serial == serial_) {
715+ // We already updated ourself before we changed something
716+ return;
717+ }
718+ read_targets();
719+ if (save_profile_dialog_) {
720+ save_profile_dialog_->update_table();
721+ }
722+ });
723+
724+ read_targets();
725 }
726
727 EconomyOptionsWindow::~EconomyOptionsWindow() {
728@@ -56,6 +124,9 @@
729 if (economy != nullptr) {
730 economy->set_has_window(false);
731 }
732+ if (save_profile_dialog_) {
733+ save_profile_dialog_->unset_parent();
734+ }
735 }
736
737 void EconomyOptionsWindow::on_economy_note(const Widelands::NoteEconomy& note) {
738@@ -81,6 +152,20 @@
739 }
740 }
741
742+void EconomyOptionsWindow::layout() {
743+ int w, h;
744+ tabpanel_.get_desired_size(&w, &h);
745+ main_box_.set_desired_size(w, h + 78);
746+ update_desired_size();
747+ UI::Window::layout();
748+}
749+
750+void EconomyOptionsWindow::EconomyOptionsPanel::update_desired_size() {
751+ display_.set_hgap(AbstractWaresDisplay::calc_hgap(display_.get_extent().w, kDesiredWidth));
752+ Box::update_desired_size();
753+ get_parent()->layout();
754+}
755+
756 EconomyOptionsWindow::TargetWaresDisplay::TargetWaresDisplay(UI::Panel* const parent,
757 int32_t const x,
758 int32_t const y,
759@@ -129,42 +214,26 @@
760 * Wraps the wares/workers display together with some buttons
761 */
762 EconomyOptionsWindow::EconomyOptionsPanel::EconomyOptionsPanel(UI::Panel* parent,
763+ EconomyOptionsWindow* eco_window,
764 Widelands::Serial serial,
765 Widelands::Player* player,
766 bool can_act,
767- Widelands::WareWorker type)
768+ Widelands::WareWorker type,
769+ int32_t min_w)
770 : UI::Box(parent, 0, 0, UI::Box::Vertical),
771 serial_(serial),
772 player_(player),
773 type_(type),
774 can_act_(can_act),
775- display_(this, 0, 0, serial_, player_, type_, can_act_) {
776+ display_(this, 0, 0, serial_, player_, type_, can_act_),
777+ economy_options_window_(eco_window) {
778 add(&display_, UI::Box::Resizing::kFullSize);
779
780+ display_.set_hgap(AbstractWaresDisplay::calc_hgap(display_.get_extent().w, min_w));
781+
782 if (!can_act_) {
783 return;
784 }
785- UI::Box* buttons = new UI::Box(this, 0, 0, UI::Box::Horizontal);
786- add(buttons);
787-
788- UI::Button* b = new UI::Button(buttons, "decrease_target", 0, 0, 34, 34,
789- UI::ButtonStyle::kWuiMenu, "-", _("Decrease target"));
790- b->sigclicked.connect(boost::bind(&EconomyOptionsPanel::change_target, this, -1));
791- buttons->add(b);
792- b->set_repeating(true);
793- buttons->add_space(8);
794-
795- b = new UI::Button(buttons, "increase_target", 0, 0, 34, 34, UI::ButtonStyle::kWuiMenu, "+",
796- _("Increase target"));
797- b->sigclicked.connect(boost::bind(&EconomyOptionsPanel::change_target, this, 1));
798- buttons->add(b);
799- b->set_repeating(true);
800- buttons->add_space(8);
801-
802- b = new UI::Button(
803- buttons, "reset_target", 0, 0, 34, 34, UI::ButtonStyle::kWuiMenu, "R", _("Reset to default"));
804- b->sigclicked.connect(boost::bind(&EconomyOptionsPanel::reset_target, this));
805- buttons->add(b);
806 }
807
808 void EconomyOptionsWindow::EconomyOptionsPanel::set_economy(Widelands::Serial serial) {
809@@ -172,7 +241,29 @@
810 display_.set_economy(serial);
811 }
812
813-void EconomyOptionsWindow::EconomyOptionsPanel::change_target(int amount) {
814+void EconomyOptionsWindow::change_target(int amount) {
815+ if (tabpanel_.active() == 0) {
816+ ware_panel_->change_target(amount);
817+ } else {
818+ worker_panel_->change_target(amount);
819+ }
820+}
821+
822+void EconomyOptionsWindow::reset_target() {
823+ if (dropdown_.get_selected().empty()) {
824+ return;
825+ }
826+ if (tabpanel_.active() == 0) {
827+ ware_panel_->reset_target();
828+ } else {
829+ worker_panel_->reset_target();
830+ }
831+}
832+
833+void EconomyOptionsWindow::EconomyOptionsPanel::change_target(int delta) {
834+ if (delta == 0) {
835+ return;
836+ }
837 Widelands::Economy* economy = player_->get_economy(serial_);
838 if (economy == nullptr) {
839 die();
840@@ -186,35 +277,401 @@
841 const Widelands::Economy::TargetQuantity& tq = is_wares ?
842 economy->ware_target_quantity(index) :
843 economy->worker_target_quantity(index);
844- // Don't allow negative new amount.
845- if (amount >= 0 || -amount <= static_cast<int>(tq.permanent)) {
846+ // Don't allow negative new amount
847+ const int old_amount = static_cast<int>(tq.permanent);
848+ const int new_amount = std::max(0, old_amount + delta);
849+ if (new_amount == old_amount) {
850+ continue;
851+ }
852+ if (is_wares) {
853+ game.send_player_command(new Widelands::CmdSetWareTargetQuantity(
854+ game.get_gametime(), player_->player_number(), serial_, index, new_amount));
855+ } else {
856+ game.send_player_command(new Widelands::CmdSetWorkerTargetQuantity(
857+ game.get_gametime(), player_->player_number(), serial_, index, new_amount));
858+ }
859+ }
860+ }
861+}
862+
863+void EconomyOptionsWindow::EconomyOptionsPanel::reset_target() {
864+ Widelands::Game& game = dynamic_cast<Widelands::Game&>(player_->egbase());
865+ const bool is_wares = type_ == Widelands::wwWARE;
866+ const auto& items = is_wares ? player_->tribe().wares() : player_->tribe().workers();
867+ const PredefinedTargets settings = economy_options_window_->get_selected_target();
868+
869+ bool anything_selected = false;
870+ bool second_phase = false;
871+
872+ do {
873+ for (const Widelands::DescriptionIndex& index : items) {
874+ if (display_.ware_selected(index) || (second_phase && !display_.is_ware_hidden(index))) {
875+ anything_selected = true;
876 if (is_wares) {
877 game.send_player_command(new Widelands::CmdSetWareTargetQuantity(
878- game.get_gametime(), player_->player_number(), serial_, index,
879- tq.permanent + amount));
880+ game.get_gametime(), player_->player_number(), serial_, index, settings.wares.at(index)));
881 } else {
882 game.send_player_command(new Widelands::CmdSetWorkerTargetQuantity(
883- game.get_gametime(), player_->player_number(), serial_, index,
884- tq.permanent + amount));
885- }
886- }
887- }
888- }
889-}
890-
891-void EconomyOptionsWindow::EconomyOptionsPanel::reset_target() {
892- Widelands::Game& game = dynamic_cast<Widelands::Game&>(player_->egbase());
893- const bool is_wares = type_ == Widelands::wwWARE;
894- const auto& items = is_wares ? player_->tribe().wares() : player_->tribe().workers();
895- for (const Widelands::DescriptionIndex& index : items) {
896- if (display_.ware_selected(index)) {
897- if (is_wares) {
898- game.send_player_command(new Widelands::CmdResetWareTargetQuantity(
899- game.get_gametime(), player_->player_number(), serial_, index));
900- } else {
901- game.send_player_command(new Widelands::CmdResetWorkerTargetQuantity(
902- game.get_gametime(), player_->player_number(), serial_, index));
903- }
904- }
905- }
906+ game.get_gametime(), player_->player_number(), serial_, index, settings.workers.at(index)));
907+ }
908+ }
909+ }
910+ if (anything_selected) {
911+ return;
912+ }
913+ // Nothing was selected, now go through the loop again and change everything
914+ second_phase = true;
915+ } while (!second_phase);
916+}
917+
918+constexpr unsigned kThinkInterval = 200;
919+
920+void EconomyOptionsWindow::think() {
921+ const uint32_t time = player_->egbase().get_gametime();
922+ if (time - time_last_thought_ < kThinkInterval || !player_->get_economy(serial_)) {
923+ // If our economy has been deleted, die() was already called, no need to do anything
924+ return;
925+ }
926+ time_last_thought_ = time;
927+ update_profiles();
928+}
929+
930+std::string EconomyOptionsWindow::applicable_target() {
931+ const Widelands::Economy* eco = player_->get_economy(serial_);
932+ for (const auto& pair : predefined_targets_) {
933+ bool matches = true;
934+ if (tabpanel_.active() == 0) {
935+ for (const Widelands::DescriptionIndex& index : player_->tribe().wares()) {
936+ const auto it = pair.second.wares.find(index);
937+ if (it != pair.second.wares.end() && eco->ware_target_quantity(index).permanent != it->second) {
938+ matches = false;
939+ break;
940+ }
941+ }
942+ } else {
943+ for (const Widelands::DescriptionIndex& index : player_->tribe().workers()) {
944+ const auto it = pair.second.workers.find(index);
945+ if (it != pair.second.workers.end() && eco->worker_target_quantity(index).permanent != it->second) {
946+ matches = false;
947+ break;
948+ }
949+ }
950+ }
951+ if (matches) {
952+ return pair.first;
953+ }
954+ }
955+ return "";
956+}
957+
958+void EconomyOptionsWindow::update_profiles() {
959+ const std::string current_profile = applicable_target();
960+
961+ for (const auto& pair : predefined_targets_) {
962+ if (last_added_to_dropdown_.count(pair.first) == 0) {
963+ return update_profiles_needed(current_profile);
964+ }
965+ }
966+ for (const auto& string : last_added_to_dropdown_) {
967+ if (!string.empty() && predefined_targets_.find(string) == predefined_targets_.end()) {
968+ return update_profiles_needed(current_profile);
969+ }
970+ }
971+ if (last_added_to_dropdown_.count("") == (current_profile.empty() ? 0 : 1)) {
972+ return update_profiles_needed(current_profile);
973+ }
974+
975+ update_profiles_select(current_profile);
976+}
977+
978+void EconomyOptionsWindow::update_profiles_needed(const std::string& current_profile) {
979+ dropdown_.clear();
980+ last_added_to_dropdown_.clear();
981+ for (const auto& pair : predefined_targets_) {
982+ dropdown_.add(_(pair.first), pair.first);
983+ last_added_to_dropdown_.insert(pair.first);
984+ }
985+ if (current_profile.empty()) {
986+ // Nothing selected
987+ dropdown_.add("–", "");
988+ last_added_to_dropdown_.insert("");
989+ }
990+ update_profiles_select(current_profile);
991+}
992+
993+void EconomyOptionsWindow::update_profiles_select(const std::string& current_profile) {
994+ if (dropdown_.is_expanded()) {
995+ return;
996+ }
997+ if (!dropdown_.has_selection() || dropdown_.get_selected() != current_profile) {
998+ dropdown_.select(current_profile);
999+ }
1000+ assert(dropdown_.has_selection());
1001+}
1002+
1003+void EconomyOptionsWindow::SaveProfileWindow::update_save_enabled() {
1004+ const std::string& text = profile_name_.text();
1005+ if (text.empty() || text == kDefaultEconomyProfile) {
1006+ save_.set_enabled(false);
1007+ save_.set_tooltip(text.empty() ? _("The profile name cannot be empty") :
1008+ _("The default profile cannot be overwritten"));
1009+ } else {
1010+ save_.set_enabled(true);
1011+ save_.set_tooltip(_("Save the profile under this name"));
1012+ }
1013+}
1014+
1015+void EconomyOptionsWindow::SaveProfileWindow::table_selection_changed() {
1016+ if (!table_.has_selection()) {
1017+ delete_.set_enabled(false);
1018+ delete_.set_tooltip("");
1019+ return;
1020+ }
1021+ const std::string& sel = table_[table_.selection_index()];
1022+ if (economy_options_->get_predefined_targets().at(sel).undeletable) {
1023+ delete_.set_tooltip(_("The predefined profiles cannot be deleted"));
1024+ delete_.set_enabled(false);
1025+ } else {
1026+ delete_.set_tooltip(_("Delete the selected profiles"));
1027+ delete_.set_enabled(true);
1028+ }
1029+ profile_name_.set_text(sel);
1030+ update_save_enabled();
1031+}
1032+
1033+void EconomyOptionsWindow::SaveProfileWindow::update_table() {
1034+ table_.clear();
1035+ for (const auto& pair : economy_options_->get_predefined_targets()) {
1036+ table_.add(pair.first).set_string(0, _(pair.first));
1037+ }
1038+ layout();
1039+}
1040+
1041+void EconomyOptionsWindow::SaveProfileWindow::save() {
1042+ const std::string name = profile_name_.text();
1043+ assert(!name.empty());
1044+ assert(name != kDefaultEconomyProfile);
1045+ for (const auto& pair : economy_options_->get_predefined_targets()) {
1046+ if (pair.first == name) {
1047+ UI::WLMessageBox m(this, _("Overwrite?"),
1048+ _("A profile with this name already exists. Do you wish to replace it?"),
1049+ UI::WLMessageBox::MBoxType::kOkCancel);
1050+ if (m.run<UI::Panel::Returncodes>() != UI::Panel::Returncodes::kOk) {
1051+ return;
1052+ }
1053+ break;
1054+ }
1055+ }
1056+ economy_options_->do_create_target(name);
1057+ die();
1058+}
1059+
1060+void EconomyOptionsWindow::SaveProfileWindow::delete_selected() {
1061+ assert(table_.has_selection());
1062+
1063+ auto& map = economy_options_->get_predefined_targets();
1064+ const std::string& name = table_[table_.selection_index()];
1065+
1066+ assert(name != kDefaultEconomyProfile);
1067+ assert(!map.at(name).undeletable);
1068+ auto it = map.find(name);
1069+ assert(it != map.end());
1070+ map.erase(it);
1071+
1072+ economy_options_->save_targets();
1073+ economy_options_->update_profiles();
1074+ update_table();
1075+}
1076+
1077+void EconomyOptionsWindow::SaveProfileWindow::unset_parent() {
1078+ economy_options_ = nullptr;
1079+ die();
1080+}
1081+
1082+void EconomyOptionsWindow::SaveProfileWindow::think() {
1083+ if (!economy_options_) {
1084+ die();
1085+ }
1086+ UI::Window::think();
1087+}
1088+
1089+EconomyOptionsWindow::SaveProfileWindow::SaveProfileWindow(UI::Panel* parent, EconomyOptionsWindow* eco)
1090+ : UI::Window(parent, "save_economy_options_profile", 0, 0, 0, 0, _("Save Profile")),
1091+ economy_options_(eco),
1092+ main_box_(this, 0, 0, UI::Box::Vertical),
1093+ table_box_(&main_box_, 0, 0, UI::Box::Vertical),
1094+ table_(&table_box_, 0, 0, 460, 120, UI::PanelStyle::kWui),
1095+ buttons_box_(&main_box_, 0, 0, UI::Box::Horizontal),
1096+ profile_name_(&buttons_box_, 0, 0, 240, UI::PanelStyle::kWui),
1097+ save_(&buttons_box_, "save", 0, 0, 80, 34, UI::ButtonStyle::kWuiPrimary, _("Save")),
1098+ cancel_(&buttons_box_, "cancel", 0, 0, 80, 34, UI::ButtonStyle::kWuiSecondary, _("Cancel")),
1099+ delete_(&buttons_box_, "delete", 0, 0, 80, 34, UI::ButtonStyle::kWuiSecondary, _("Delete")) {
1100+ table_.add_column(200, _("Existing Profiles"));
1101+ update_table();
1102+
1103+ table_.selected.connect([this](uint32_t) { table_selection_changed(); });
1104+ profile_name_.changed.connect([this] { update_save_enabled(); });
1105+ profile_name_.ok.connect([this] { save(); });
1106+ profile_name_.cancel.connect([this] { die(); });
1107+ save_.sigclicked.connect([this] { save(); });
1108+ cancel_.sigclicked.connect([this] { die(); });
1109+ delete_.sigclicked.connect([this] { delete_selected(); });
1110+
1111+ table_box_.add(&table_, UI::Box::Resizing::kFullSize);
1112+ buttons_box_.add(&profile_name_, UI::Box::Resizing::kFullSize);
1113+ buttons_box_.add(&save_);
1114+ buttons_box_.add(&cancel_);
1115+ buttons_box_.add(&delete_);
1116+ main_box_.add(&table_box_, UI::Box::Resizing::kFullSize);
1117+ main_box_.add(&buttons_box_, UI::Box::Resizing::kFullSize);
1118+ set_center_panel(&main_box_);
1119+
1120+ table_selection_changed();
1121+ update_save_enabled();
1122+}
1123+
1124+EconomyOptionsWindow::SaveProfileWindow::~SaveProfileWindow() {
1125+ if (economy_options_) {
1126+ economy_options_->close_save_profile_window();
1127+ }
1128+}
1129+
1130+void EconomyOptionsWindow::create_target() {
1131+ if (save_profile_dialog_) {
1132+ // Already open
1133+ return;
1134+ }
1135+ save_profile_dialog_ = new SaveProfileWindow(get_parent(), this);
1136+}
1137+
1138+void EconomyOptionsWindow::close_save_profile_window() {
1139+ assert(save_profile_dialog_);
1140+ save_profile_dialog_ = nullptr;
1141+}
1142+
1143+void EconomyOptionsWindow::do_create_target(const std::string& name) {
1144+ assert(!name.empty());
1145+ assert(name != kDefaultEconomyProfile);
1146+ const Widelands::Tribes& tribes = player_->egbase().tribes();
1147+ const Widelands::TribeDescr& tribe = player_->tribe();
1148+ Widelands::Economy* economy = player_->get_economy(serial_);
1149+ PredefinedTargets t;
1150+ for (Widelands::DescriptionIndex di : tribe.wares()) {
1151+ if (tribes.get_ware_descr(di)->has_demand_check(tribe.name())) {
1152+ t.wares[di] = economy->ware_target_quantity(di).permanent;
1153+ }
1154+ }
1155+ for (Widelands::DescriptionIndex di : tribe.workers()) {
1156+ if (tribes.get_worker_descr(di)->has_demand_check()) {
1157+ t.workers[di] = economy->worker_target_quantity(di).permanent;
1158+ }
1159+ }
1160+ predefined_targets_[name] = t;
1161+
1162+ save_targets();
1163+ update_profiles();
1164+}
1165+
1166+void EconomyOptionsWindow::save_targets() {
1167+ const Widelands::Tribes& tribes = player_->egbase().tribes();
1168+ Profile profile;
1169+
1170+ std::map<std::string, uint32_t> serials;
1171+ for (const auto& pair : predefined_targets_) {
1172+ if (pair.first != kDefaultEconomyProfile) {
1173+ serials.insert(std::make_pair(pair.first, serials.size()));
1174+ }
1175+ }
1176+ Section& global_section = profile.create_section(kDefaultEconomyProfile.c_str());
1177+ for (const auto& pair : serials) {
1178+ global_section.set_string(std::to_string(pair.second).c_str(), pair.first);
1179+ }
1180+
1181+ for (const auto& pair : predefined_targets_) {
1182+ if (pair.first == kDefaultEconomyProfile) {
1183+ continue;
1184+ }
1185+ Section& section = profile.create_section(std::to_string(serials.at(pair.first)).c_str());
1186+ for (const auto& setting : pair.second.wares) {
1187+ section.set_natural(tribes.get_ware_descr(setting.first)->name().c_str(), setting.second);
1188+ }
1189+ for (const auto& setting : pair.second.workers) {
1190+ section.set_natural(tribes.get_worker_descr(setting.first)->name().c_str(), setting.second);
1191+ }
1192+ }
1193+
1194+ Section& section = profile.create_section("undeletable");
1195+ for (const auto& pair : predefined_targets_) {
1196+ if (pair.first != kDefaultEconomyProfile) {
1197+ section.set_bool(std::to_string(serials.at(pair.first)).c_str(), pair.second.undeletable);
1198+ }
1199+ }
1200+
1201+ g_fs->ensure_directory_exists(kEconomyProfilesDir);
1202+ std::string complete_filename = kEconomyProfilesDir + g_fs->file_separator() + player_->tribe().name();
1203+ profile.write(complete_filename.c_str(), false);
1204+
1205+ // Inform the windows of other economies of new and deleted profiles
1206+ Notifications::publish(NoteEconomyProfile(serial_));
1207+}
1208+
1209+void EconomyOptionsWindow::read_targets() {
1210+ predefined_targets_.clear();
1211+ const Widelands::Tribes& tribes = player_->egbase().tribes();
1212+ const Widelands::TribeDescr& tribe = player_->tribe();
1213+
1214+ {
1215+ PredefinedTargets t;
1216+ t.undeletable = true;
1217+ for (Widelands::DescriptionIndex di : tribe.wares()) {
1218+ const Widelands::WareDescr* descr = tribes.get_ware_descr(di);
1219+ if (descr->has_demand_check(tribe.name())) {
1220+ t.wares.insert(std::make_pair(di, descr->default_target_quantity(tribe.name())));
1221+ }
1222+ }
1223+ for (Widelands::DescriptionIndex di : tribe.workers()) {
1224+ const Widelands::WorkerDescr* descr = tribes.get_worker_descr(di);
1225+ if (descr->has_demand_check()) {
1226+ t.workers.insert(std::make_pair(di, descr->default_target_quantity()));
1227+ }
1228+ }
1229+ predefined_targets_.insert(std::make_pair(kDefaultEconomyProfile, t));
1230+ }
1231+
1232+ std::string complete_filename = kEconomyProfilesDir + g_fs->file_separator() + player_->tribe().name();
1233+ Profile profile;
1234+ profile.read(complete_filename.c_str());
1235+
1236+ Section* global_section = profile.get_section(kDefaultEconomyProfile);
1237+ if (global_section) {
1238+ std::map<std::string, std::string> serials;
1239+ while (Section::Value* v = global_section->get_next_val()) {
1240+ serials.insert(std::make_pair(v->get_name(), v->get_string()));
1241+ }
1242+
1243+ for (const auto& pair : serials) {
1244+ Section* section = profile.get_section(pair.first);
1245+ PredefinedTargets t;
1246+ while (Section::Value* v = section->get_next_val()) {
1247+ const std::string name(v->get_name());
1248+ Widelands::DescriptionIndex di = tribes.ware_index(name);
1249+ if (di == Widelands::INVALID_INDEX) {
1250+ di = tribes.worker_index(name);
1251+ assert(di != Widelands::INVALID_INDEX);
1252+ t.workers.insert(std::make_pair(di, v->get_natural()));
1253+ } else {
1254+ t.wares.insert(std::make_pair(di, v->get_natural()));
1255+ }
1256+ }
1257+ predefined_targets_.insert(std::make_pair(pair.second, t));
1258+ }
1259+
1260+ if (Section* section = profile.get_section("undeletable")) {
1261+ while (Section::Value* v = section->get_next_val()) {
1262+ predefined_targets_.at(serials.at(std::string(v->get_name()))).undeletable = v->get_bool();
1263+ }
1264+ }
1265+ }
1266+
1267+ update_profiles();
1268 }
1269
1270=== modified file 'src/wui/economy_options_window.h'
1271--- src/wui/economy_options_window.h 2019-02-23 11:00:49 +0000
1272+++ src/wui/economy_options_window.h 2019-05-29 06:43:44 +0000
1273@@ -20,19 +20,61 @@
1274 #ifndef WL_WUI_ECONOMY_OPTIONS_WINDOW_H
1275 #define WL_WUI_ECONOMY_OPTIONS_WINDOW_H
1276
1277+#include <map>
1278 #include <memory>
1279+#include <string>
1280
1281 #include "economy/economy.h"
1282 #include "logic/map_objects/tribes/tribe_descr.h"
1283 #include "notifications/notifications.h"
1284 #include "ui_basic/box.h"
1285+#include "ui_basic/button.h"
1286+#include "ui_basic/dropdown.h"
1287+#include "ui_basic/editbox.h"
1288+#include "ui_basic/table.h"
1289 #include "ui_basic/tabpanel.h"
1290 #include "ui_basic/window.h"
1291 #include "wui/waresdisplay.h"
1292
1293+const std::string kDefaultEconomyProfile = "Default";
1294+
1295+// Used to indicate that a profile has been saved or deleted, so all open windows can update it
1296+struct NoteEconomyProfile {
1297+ NoteEconomyProfile(Widelands::Serial s) : serial(s) {
1298+ }
1299+ Widelands::Serial serial;
1300+ CAN_BE_SENT_AS_NOTE(NoteId::EconomyProfile)
1301+};
1302+
1303 struct EconomyOptionsWindow : public UI::Window {
1304 EconomyOptionsWindow(UI::Panel* parent, Widelands::Economy* economy, bool can_act);
1305- ~EconomyOptionsWindow();
1306+ ~EconomyOptionsWindow() override;
1307+
1308+ struct PredefinedTargets {
1309+ using Targets = std::map<Widelands::DescriptionIndex, uint32_t>;
1310+ Targets wares;
1311+ Targets workers;
1312+ bool undeletable = false;
1313+ };
1314+
1315+ void create_target();
1316+ void do_create_target(const std::string&);
1317+ void save_targets();
1318+ void read_targets();
1319+ void update_profiles();
1320+ std::map<std::string, PredefinedTargets>& get_predefined_targets() {
1321+ return predefined_targets_;
1322+ }
1323+ const PredefinedTargets& get_selected_target() const {
1324+ return predefined_targets_.at(dropdown_.get_selected());
1325+ }
1326+
1327+ void change_target(int amount);
1328+ void reset_target();
1329+
1330+ void layout() override;
1331+
1332+ void close_save_profile_window();
1333
1334 private:
1335 struct TargetWaresDisplay : public AbstractWaresDisplay {
1336@@ -59,14 +101,17 @@
1337 */
1338 struct EconomyOptionsPanel : UI::Box {
1339 EconomyOptionsPanel(UI::Panel* parent,
1340+ EconomyOptionsWindow* eco_window,
1341 Widelands::Serial serial,
1342 Widelands::Player* player,
1343 bool can_act,
1344- Widelands::WareWorker type);
1345+ Widelands::WareWorker type,
1346+ int32_t min_w);
1347
1348 void set_economy(Widelands::Serial serial);
1349 void change_target(int amount);
1350 void reset_target();
1351+ void update_desired_size() override;
1352
1353 private:
1354 Widelands::Serial serial_;
1355@@ -74,17 +119,61 @@
1356 Widelands::WareWorker type_;
1357 bool can_act_;
1358 TargetWaresDisplay display_;
1359+ EconomyOptionsWindow* economy_options_window_;
1360 };
1361
1362 /// Actions performed when a NoteEconomyWindow is received.
1363 void on_economy_note(const Widelands::NoteEconomy& note);
1364
1365+ UI::Box main_box_;
1366 Widelands::Serial serial_;
1367 Widelands::Player* player_;
1368 UI::TabPanel tabpanel_;
1369 EconomyOptionsPanel* ware_panel_;
1370 EconomyOptionsPanel* worker_panel_;
1371 std::unique_ptr<Notifications::Subscriber<Widelands::NoteEconomy>> economynotes_subscriber_;
1372+ std::unique_ptr<Notifications::Subscriber<NoteEconomyProfile>> profilenotes_subscriber_;
1373+
1374+ std::map<std::string, PredefinedTargets> predefined_targets_;
1375+ UI::Box dropdown_box_;
1376+ UI::Dropdown<std::string> dropdown_;
1377+
1378+ std::string applicable_target();
1379+ std::set<std::string> last_added_to_dropdown_;
1380+ void think() override;
1381+ uint32_t time_last_thought_;
1382+
1383+ struct SaveProfileWindow : public UI::Window {
1384+ SaveProfileWindow(UI::Panel* parent, EconomyOptionsWindow* eco);
1385+ ~SaveProfileWindow() override;
1386+
1387+ void update_save_enabled();
1388+ void table_selection_changed();
1389+ void update_table();
1390+ void save();
1391+ void delete_selected();
1392+
1393+ void unset_parent();
1394+
1395+ void think() override;
1396+
1397+ private:
1398+ EconomyOptionsWindow* economy_options_;
1399+ UI::Box main_box_;
1400+ UI::Box table_box_;
1401+ UI::Table<const std::string&> table_;
1402+ UI::Box buttons_box_;
1403+ UI::EditBox profile_name_;
1404+ UI::Button save_;
1405+ UI::Button cancel_;
1406+ UI::Button delete_;
1407+ };
1408+
1409+ // Helper functions for update_profiles()
1410+ void update_profiles_needed(const std::string&);
1411+ void update_profiles_select(const std::string&);
1412+
1413+ SaveProfileWindow* save_profile_dialog_;
1414 };
1415
1416 #endif // end of include guard: WL_WUI_ECONOMY_OPTIONS_WINDOW_H
1417
1418=== modified file 'src/wui/inputqueuedisplay.cc'
1419--- src/wui/inputqueuedisplay.cc 2019-05-27 05:37:03 +0000
1420+++ src/wui/inputqueuedisplay.cc 2019-05-29 06:43:44 +0000
1421@@ -72,7 +72,7 @@
1422
1423 uint32_t priority_button_height = show_only ? 0 : 3 * PriorityButtonSize;
1424 uint32_t image_height =
1425- show_only ? WARE_MENU_PIC_HEIGHT : std::max<int32_t>(WARE_MENU_PIC_HEIGHT, ph);
1426+ show_only ? kWareMenuPicHeight : std::max<int32_t>(kWareMenuPicHeight, ph);
1427
1428 total_height_ = std::max(priority_button_height, image_height) + 2 * Border;
1429
1430@@ -92,7 +92,7 @@
1431 */
1432 void InputQueueDisplay::max_size_changed() {
1433 uint32_t pbs = show_only_ ? 0 : PriorityButtonSize;
1434- uint32_t ctrl_b_size = show_only_ ? 0 : 2 * WARE_MENU_PIC_WIDTH;
1435+ uint32_t ctrl_b_size = show_only_ ? 0 : 2 * kWareMenuPicWidth;
1436
1437 cache_size_ = queue_.get_max_size();
1438
1439@@ -141,7 +141,7 @@
1440
1441 Vector2i point = Vector2i::zero();
1442 point.x = Border + (show_only_ ? 0 : CellWidth + CellSpacing);
1443- point.y = Border + (total_height_ - 2 * Border - WARE_MENU_PIC_HEIGHT) / 2;
1444+ point.y = Border + (total_height_ - 2 * Border - kWareMenuPicHeight) / 2;
1445
1446 for (; nr_inputs_to_draw; --nr_inputs_to_draw, point.x += CellWidth + CellSpacing) {
1447 dst.blitrect(Vector2i(point.x, point.y), icon_, Recti(0, 0, icon_->width(), icon_->height()),
1448@@ -241,12 +241,12 @@
1449 return;
1450
1451 uint32_t x = Border;
1452- uint32_t y = Border + (total_height_ - 2 * Border - WARE_MENU_PIC_WIDTH) / 2;
1453+ uint32_t y = Border + (total_height_ - 2 * Border - kWareMenuPicWidth) / 2;
1454
1455 boost::format tooltip_format("<p>%s%s%s</p>");
1456
1457 decrease_max_fill_ = new UI::Button(
1458- this, "decrease_max_fill", x, y, WARE_MENU_PIC_WIDTH, WARE_MENU_PIC_HEIGHT,
1459+ this, "decrease_max_fill", x, y, kWareMenuPicWidth, kWareMenuPicHeight,
1460 UI::ButtonStyle::kWuiMenu, g_gr->images().get("images/ui_basic/scrollbar_left.png"),
1461 (tooltip_format %
1462 g_gr->styles()
1463@@ -272,7 +272,7 @@
1464 x = Border + (cache_size_ + 1) * (CellWidth + CellSpacing);
1465
1466 increase_max_fill_ = new UI::Button(
1467- this, "increase_max_fill", x, y, WARE_MENU_PIC_WIDTH, WARE_MENU_PIC_HEIGHT,
1468+ this, "increase_max_fill", x, y, kWareMenuPicWidth, kWareMenuPicHeight,
1469 UI::ButtonStyle::kWuiMenu, g_gr->images().get("images/ui_basic/scrollbar_right.png"),
1470 (tooltip_format
1471
1472
1473=== modified file 'src/wui/inputqueuedisplay.h'
1474--- src/wui/inputqueuedisplay.h 2019-04-23 14:53:35 +0000
1475+++ src/wui/inputqueuedisplay.h 2019-05-29 06:43:44 +0000
1476@@ -49,7 +49,7 @@
1477 */
1478 class InputQueueDisplay : public UI::Panel {
1479 public:
1480- enum { CellWidth = WARE_MENU_PIC_WIDTH, CellSpacing = 2, Border = 4, PriorityButtonSize = 10 };
1481+ enum { CellWidth = kWareMenuPicWidth, CellSpacing = 2, Border = 4, PriorityButtonSize = 10 };
1482
1483 InputQueueDisplay(UI::Panel* parent,
1484 int32_t x,
1485
1486=== modified file 'src/wui/ware_statistics_menu.cc'
1487--- src/wui/ware_statistics_menu.cc 2019-03-26 22:15:39 +0000
1488+++ src/wui/ware_statistics_menu.cc 2019-05-29 06:43:44 +0000
1489@@ -22,6 +22,7 @@
1490 #include "base/i18n.h"
1491 #include "graphic/graphic.h"
1492 #include "graphic/rendertarget.h"
1493+#include "graphic/text_layout.h"
1494 #include "logic/map_objects/tribes/tribe_descr.h"
1495 #include "logic/map_objects/tribes/warelist.h"
1496 #include "logic/player.h"
1497@@ -101,7 +102,11 @@
1498 &registry,
1499 kPlotWidth + 2 * kSpacing,
1500 270,
1501- _("Ware Statistics")) {
1502+ _("Ware Statistics")),
1503+ main_box_(nullptr),
1504+ tab_panel_(nullptr),
1505+ display_(nullptr),
1506+ slider_(nullptr) {
1507 uint8_t const nr_wares = parent.get_player()->egbase().tribes().nrwares();
1508
1509 // Init color sets
1510@@ -111,45 +116,45 @@
1511 std::fill(active_colors_.begin(), active_colors_.end(), 0);
1512
1513 // First, we must decide about the size.
1514- UI::Box* box = new UI::Box(this, 0, 0, UI::Box::Vertical, 0, 0, 5);
1515- box->set_border(kSpacing, kSpacing, kSpacing, kSpacing);
1516- set_center_panel(box);
1517+ main_box_ = new UI::Box(this, 0, 0, UI::Box::Vertical, 0, 0, 5);
1518+ main_box_->set_border(kSpacing, kSpacing, kSpacing, kSpacing);
1519+ set_center_panel(main_box_);
1520
1521 // Setup plot widgets
1522 // Create a tabbed environment for the different plots
1523- UI::TabPanel* tabs = new UI::TabPanel(box, UI::TabPanelStyle::kWuiDark);
1524+ tab_panel_ = new UI::TabPanel(main_box_, UI::TabPanelStyle::kWuiDark);
1525
1526 plot_production_ =
1527- new WuiPlotArea(tabs, 0, 0, kPlotWidth, kPlotHeight + kSpacing,
1528+ new WuiPlotArea(tab_panel_, 0, 0, kPlotWidth, kPlotHeight + kSpacing,
1529 Widelands::kStatisticsSampleTime, WuiPlotArea::Plotmode::kRelative);
1530
1531- tabs->add(
1532+ tab_panel_->add(
1533 "production", g_gr->images().get(pic_tab_production), plot_production_, _("Production"));
1534
1535 plot_consumption_ =
1536- new WuiPlotArea(tabs, 0, 0, kPlotWidth, kPlotHeight + kSpacing,
1537+ new WuiPlotArea(tab_panel_, 0, 0, kPlotWidth, kPlotHeight + kSpacing,
1538 Widelands::kStatisticsSampleTime, WuiPlotArea::Plotmode::kRelative);
1539
1540- tabs->add(
1541+ tab_panel_->add(
1542 "consumption", g_gr->images().get(pic_tab_consumption), plot_consumption_, _("Consumption"));
1543
1544 plot_economy_ =
1545- new DifferentialPlotArea(tabs, 0, 0, kPlotWidth, kPlotHeight + kSpacing,
1546+ new DifferentialPlotArea(tab_panel_, 0, 0, kPlotWidth, kPlotHeight + kSpacing,
1547 Widelands::kStatisticsSampleTime, WuiPlotArea::Plotmode::kRelative);
1548
1549- tabs->add(
1550+ tab_panel_->add(
1551 "economy_health", g_gr->images().get(pic_tab_economy), plot_economy_, _("Economy health"));
1552
1553 plot_stock_ =
1554- new WuiPlotArea(tabs, 0, 0, kPlotWidth, kPlotHeight + kSpacing,
1555+ new WuiPlotArea(tab_panel_, 0, 0, kPlotWidth, kPlotHeight + kSpacing,
1556 Widelands::kStatisticsSampleTime, WuiPlotArea::Plotmode::kAbsolute);
1557
1558- tabs->add("stock", g_gr->images().get(pic_tab_stock), plot_stock_, _("Stock"));
1559+ tab_panel_->add("stock", g_gr->images().get(pic_tab_stock), plot_stock_, _("Stock"));
1560
1561- tabs->activate(0);
1562+ tab_panel_->activate(0);
1563
1564 // Add tabbed environment to box
1565- box->add(tabs, UI::Box::Resizing::kFullSize);
1566+ main_box_->add(tab_panel_, UI::Box::Resizing::kFullSize);
1567
1568 // Register statistics data
1569 for (Widelands::DescriptionIndex cur_ware = 0; cur_ware < nr_wares; ++cur_ware) {
1570@@ -178,15 +183,16 @@
1571 colors[kInactiveColorIndex]);
1572 }
1573
1574- box->add(new StatisticWaresDisplay(
1575- box, 0, 0, parent.get_player()->tribe(),
1576+ display_ = new StatisticWaresDisplay(
1577+ main_box_, 0, 0, parent.get_player()->tribe(),
1578 [this](const int ware_index, const bool what) { cb_changed_to(ware_index, what); },
1579- color_map_),
1580- UI::Box::Resizing::kFullSize);
1581+ color_map_);
1582+ display_->set_min_free_vertical_space(400);
1583+ main_box_->add(display_, UI::Box::Resizing::kFullSize);
1584
1585- WuiPlotAreaSlider* slider = new WuiPlotAreaSlider(this, *plot_production_, 0, 0, kPlotWidth, 45);
1586- slider->changedto.connect([this](const int32_t timescale) { set_time(timescale); });
1587- box->add(slider, UI::Box::Resizing::kFullSize);
1588+ slider_ = new WuiPlotAreaSlider(this, *plot_production_, 0, 0, kPlotWidth, 45);
1589+ slider_->changedto.connect([this](const int32_t timescale) { set_time(timescale); });
1590+ main_box_->add(slider_, UI::Box::Resizing::kFullSize);
1591 }
1592
1593 /**
1594@@ -228,6 +234,27 @@
1595 plot_stock_->show_plot(static_cast<size_t>(id), what);
1596 }
1597
1598+static bool layouting = false;
1599+void WareStatisticsMenu::layout() {
1600+ if (layouting || !tab_panel_ || !display_ || !slider_ || !main_box_) {
1601+ return;
1602+ }
1603+ layouting = true;
1604+
1605+ display_->set_hgap(3, false);
1606+ int w1, h1, w2, h2, w3, h3;
1607+ tab_panel_->get_desired_size(&w1, &h1);
1608+ display_->get_desired_size(&w2, &h2);
1609+ slider_->get_desired_size(&w3, &h3);
1610+
1611+ display_->set_hgap(std::max(3, AbstractWaresDisplay::calc_hgap(display_->get_extent().w, kPlotWidth)), false);
1612+ display_->get_desired_size(&w2, &h2);
1613+
1614+ main_box_->set_desired_size(std::max(w2, kPlotWidth) + 2 * kSpacing, h1 + h2 + h3 + text_height(UI::FontStyle::kLabel));
1615+ UI::UniqueWindow::layout();
1616+ layouting = false;
1617+}
1618+
1619 /**
1620 * Callback for the time buttons. Change the time axis of all ware
1621 * statistics simultaneously.
1622
1623=== modified file 'src/wui/ware_statistics_menu.h'
1624--- src/wui/ware_statistics_menu.h 2019-02-23 11:00:49 +0000
1625+++ src/wui/ware_statistics_menu.h 2019-05-29 06:43:44 +0000
1626@@ -29,13 +29,27 @@
1627 struct DifferentialPlotArea;
1628 class InteractivePlayer;
1629 struct WuiPlotArea;
1630+struct StatisticWaresDisplay;
1631+struct WuiPlotAreaSlider;
1632+namespace UI {
1633+ struct Box;
1634+ struct TabPanel;
1635+}
1636
1637 struct WareStatisticsMenu : public UI::UniqueWindow {
1638 public:
1639 WareStatisticsMenu(InteractivePlayer&, UI::UniqueWindow::Registry&);
1640 void set_time(int32_t);
1641
1642+protected:
1643+ void layout() override;
1644+
1645 private:
1646+ UI::Box* main_box_;
1647+ UI::TabPanel* tab_panel_;
1648+ StatisticWaresDisplay* display_;
1649+ WuiPlotAreaSlider* slider_;
1650+
1651 WuiPlotArea* plot_production_;
1652 WuiPlotArea* plot_consumption_;
1653 WuiPlotArea* plot_stock_;
1654
1655=== modified file 'src/wui/waresdisplay.cc'
1656--- src/wui/waresdisplay.cc 2019-05-26 17:21:15 +0000
1657+++ src/wui/waresdisplay.cc 2019-05-29 06:43:44 +0000
1658@@ -35,8 +35,9 @@
1659 #include "logic/map_objects/tribes/ware_descr.h"
1660 #include "logic/map_objects/tribes/worker.h"
1661 #include "logic/player.h"
1662+#include "ui_basic/window.h"
1663
1664-const int WARE_MENU_INFO_SIZE = 12;
1665+constexpr int kWareMenuInfoSize = 12;
1666
1667 AbstractWaresDisplay::AbstractWaresDisplay(
1668 UI::Panel* const parent,
1669@@ -46,7 +47,9 @@
1670 Widelands::WareWorker type,
1671 bool selectable,
1672 boost::function<void(Widelands::DescriptionIndex, bool)> callback_function,
1673- bool horizontal)
1674+ bool horizontal,
1675+ int32_t hgap,
1676+ int32_t vgap)
1677 : // Size is set when add_warelist is called, as it depends on the type_.
1678 UI::Panel(parent, x, y, 0, 0),
1679 tribe_(tribe),
1680@@ -57,8 +60,11 @@
1681
1682 selectable_(selectable),
1683 horizontal_(horizontal),
1684+ hgap_(hgap),
1685+ vgap_(vgap),
1686 selection_anchor_(Widelands::INVALID_INDEX),
1687- callback_function_(callback_function) {
1688+ callback_function_(callback_function),
1689+ min_free_vertical_space_(290) {
1690 for (const Widelands::DescriptionIndex& index : indices_) {
1691 selected_.insert(std::make_pair(index, false));
1692 hidden_.insert(std::make_pair(index, false));
1693@@ -67,22 +73,65 @@
1694
1695 curware_.set_text(_("Stock"));
1696
1697- // Find out geometry from icons_order
1698- unsigned int columns = icons_order().size();
1699- unsigned int rows = 0;
1700- for (unsigned int i = 0; i < icons_order().size(); i++)
1701- if (icons_order()[i].size() > rows)
1702- rows = icons_order()[i].size();
1703+ graphic_resolution_changed_subscriber_ = Notifications::subscribe<GraphicResolutionChanged>(
1704+ [this](const GraphicResolutionChanged&) {
1705+ recalc_desired_size(true);
1706+ });
1707+
1708+ recalc_desired_size(false);
1709+}
1710+
1711+Widelands::Extent AbstractWaresDisplay::get_extent() const {
1712+ int16_t columns = 0;
1713+ int16_t rows = 0;
1714+ for (const auto& pair : icons_order_coords()) {
1715+ columns = std::max(columns, pair.second.x);
1716+ rows = std::max(rows, pair.second.y);
1717+ }
1718+ // We cound from 0 up
1719+ ++columns;
1720+ ++rows;
1721+
1722 if (horizontal_) {
1723- unsigned int s = columns;
1724+ const int16_t s = columns;
1725 columns = rows;
1726 rows = s;
1727 }
1728+ return Widelands::Extent(columns, rows);
1729+}
1730+
1731+void AbstractWaresDisplay::set_hgap(int32_t gap, bool relayout) {
1732+ hgap_ = gap;
1733+ recalc_desired_size(relayout);
1734+}
1735+
1736+void AbstractWaresDisplay::set_vgap(int32_t gap, bool relayout) {
1737+ vgap_ = gap;
1738+ recalc_desired_size(relayout);
1739+}
1740+
1741+void AbstractWaresDisplay::recalc_desired_size(bool relayout) {
1742+ relayout_icons_order_coords();
1743+
1744+ // Find out geometry from icons_order
1745+ const Widelands::Extent size = get_extent();
1746
1747 // 25 is height of curware_ text
1748 set_desired_size(
1749- columns * (WARE_MENU_PIC_WIDTH + WARE_MENU_PIC_PAD_X) + 1,
1750- rows * (WARE_MENU_PIC_HEIGHT + WARE_MENU_INFO_SIZE + WARE_MENU_PIC_PAD_Y) + 1 + 25);
1751+ size.w * (kWareMenuPicWidth + hgap_) - hgap_ + 5,
1752+ size.h * (kWareMenuPicHeight + kWareMenuInfoSize + vgap_) - vgap_ + 1 + 25);
1753+
1754+ if (relayout) {
1755+ // Since we are usually stacked deep within other panels, we need to tell our highest parent window to relayout
1756+ UI::Panel* p = this;
1757+ while (p->get_parent()) {
1758+ p = p->get_parent();
1759+ if (dynamic_cast<UI::Window*>(p)) {
1760+ p->layout();
1761+ return;
1762+ }
1763+ }
1764+ }
1765 }
1766
1767 bool AbstractWaresDisplay::handle_mousemove(uint8_t state, int32_t x, int32_t y, int32_t, int32_t) {
1768@@ -162,25 +211,56 @@
1769 * DescriptionIndex::null() if the given point is outside the range.
1770 */
1771 Widelands::DescriptionIndex AbstractWaresDisplay::ware_at_point(int32_t x, int32_t y) const {
1772- if (x < 0 || y < 0)
1773- return Widelands::INVALID_INDEX;
1774-
1775- unsigned int i = x / (WARE_MENU_PIC_WIDTH + WARE_MENU_PIC_PAD_X);
1776- unsigned int j = y / (WARE_MENU_PIC_HEIGHT + WARE_MENU_INFO_SIZE + WARE_MENU_PIC_PAD_Y);
1777+ // Graphical offset
1778+ x -= 2;
1779+ y -= 2;
1780+
1781+ if (x < 0 || y < 0) {
1782+ return Widelands::INVALID_INDEX;
1783+ }
1784+
1785+ int i = x / (kWareMenuPicWidth + hgap_);
1786+ int j = y / (kWareMenuPicHeight + kWareMenuInfoSize + vgap_);
1787+ if (kWareMenuPicWidth * (i + 1) + hgap_ * i < x ||
1788+ (kWareMenuPicHeight + kWareMenuInfoSize) * (j + 1) + vgap_ * j < y) {
1789+ // Not on the ware, but on the space between
1790+ return Widelands::INVALID_INDEX;
1791+ }
1792 if (horizontal_) {
1793- unsigned int s = i;
1794+ int s = i;
1795 i = j;
1796 j = s;
1797 }
1798- if (i < icons_order().size() && j < icons_order()[i].size()) {
1799- const Widelands::DescriptionIndex& ware = icons_order()[i][j];
1800- assert(hidden_.count(ware) == 1);
1801- if (!(hidden_.find(ware)->second)) {
1802- return ware;
1803- }
1804- }
1805-
1806- return Widelands::INVALID_INDEX;
1807+ for (const auto& pair : icons_order_coords()) {
1808+ if (pair.second.x == i && pair.second.y == j) {
1809+ assert(hidden_.count(pair.first) == 1);
1810+ if (!(hidden_.find(pair.first)->second)) {
1811+ return pair.first;
1812+ }
1813+ break;
1814+ }
1815+ }
1816+
1817+ return Widelands::INVALID_INDEX;
1818+}
1819+
1820+Widelands::DescriptionIndex AbstractWaresDisplay::ware_at_coords(int16_t x, int16_t y) const {
1821+ for (const auto& pair : icons_order_coords()) {
1822+ if (pair.second.x == x && pair.second.y == y) {
1823+ return pair.first;
1824+ }
1825+ }
1826+ return Widelands::INVALID_INDEX;
1827+}
1828+
1829+uint16_t AbstractWaresDisplay::column_length(int16_t x) const {
1830+ uint16_t l = 0;
1831+ for (const auto& pair : icons_order_coords()) {
1832+ if (pair.second.x == x) {
1833+ l = std::max(l, static_cast<uint16_t>(pair.second.y + 1));
1834+ }
1835+ }
1836+ return l;
1837 }
1838
1839 // Update the anchored selection. An anchor has been created by mouse
1840@@ -199,15 +279,13 @@
1841 Vector2i anchor_pos = ware_position(selection_anchor_);
1842 // Add an offset to make sure the anchor line and column will be
1843 // selected when selecting in topleft direction
1844- int32_t anchor_x = anchor_pos.x + WARE_MENU_PIC_WIDTH / 2;
1845- int32_t anchor_y = anchor_pos.y + WARE_MENU_PIC_HEIGHT / 2;
1846+ int32_t anchor_x = anchor_pos.x + kWareMenuPicWidth / 2;
1847+ int32_t anchor_y = anchor_pos.y + kWareMenuPicHeight / 2;
1848
1849- unsigned int left_ware_idx = anchor_x / (WARE_MENU_PIC_WIDTH + WARE_MENU_PIC_PAD_X);
1850- unsigned int top_ware_idx =
1851- anchor_y / (WARE_MENU_PIC_HEIGHT + WARE_MENU_INFO_SIZE + WARE_MENU_PIC_PAD_Y);
1852- unsigned int right_ware_idx = x / (WARE_MENU_PIC_WIDTH + WARE_MENU_PIC_PAD_X);
1853- unsigned int bottoware_idx_ =
1854- y / (WARE_MENU_PIC_HEIGHT + WARE_MENU_INFO_SIZE + WARE_MENU_PIC_PAD_Y);
1855+ unsigned int left_ware_idx = anchor_x / (kWareMenuPicWidth + hgap_);
1856+ unsigned int top_ware_idx = anchor_y / (kWareMenuPicHeight + kWareMenuInfoSize + vgap_);
1857+ unsigned int right_ware_idx = x / (kWareMenuPicWidth + hgap_);
1858+ unsigned int bottoware_idx_ = y / (kWareMenuPicHeight + kWareMenuInfoSize + vgap_);
1859 unsigned int tmp;
1860
1861 // Reverse col/row and anchor/endpoint if needed
1862@@ -231,10 +309,10 @@
1863 }
1864
1865 for (unsigned int cur_ware_x = left_ware_idx; cur_ware_x <= right_ware_idx; cur_ware_x++) {
1866- if (cur_ware_x < icons_order().size()) {
1867+ if (cur_ware_x < icons_order_coords().size()) {
1868 for (unsigned cur_ware_y = top_ware_idx; cur_ware_y <= bottoware_idx_; cur_ware_y++) {
1869- if (cur_ware_y < icons_order()[cur_ware_x].size()) {
1870- Widelands::DescriptionIndex ware = icons_order()[cur_ware_x][cur_ware_y];
1871+ if (cur_ware_y < static_cast<unsigned>(column_length(cur_ware_x))) {
1872+ Widelands::DescriptionIndex ware = ware_at_coords(cur_ware_x, cur_ware_y);
1873 if (!hidden_[ware]) {
1874 in_selection_[ware] = true;
1875 }
1876@@ -271,26 +349,44 @@
1877 NEVER_HERE();
1878 }
1879
1880-const Widelands::TribeDescr::WaresOrderCoords& AbstractWaresDisplay::icons_order_coords() const {
1881- switch (type_) {
1882- case Widelands::wwWARE:
1883- return tribe_.wares_order_coords();
1884- case Widelands::wwWORKER:
1885- return tribe_.workers_order_coords();
1886+const WaresOrderCoords& AbstractWaresDisplay::icons_order_coords() const {
1887+ assert(!order_coords_.empty());
1888+ return order_coords_;
1889+}
1890+
1891+void AbstractWaresDisplay::relayout_icons_order_coords() {
1892+ order_coords_.clear();
1893+ const int column_number = icons_order().size();
1894+ const int column_max_size = std::max(1, (g_gr->get_yres() - min_free_vertical_space_) /
1895+ (kWareMenuPicHeight + vgap_ + kWareMenuInfoSize));
1896+
1897+ int16_t column_index_to_apply = 0;
1898+ for (int16_t column_index = 0; column_index < column_number; ++column_index) {
1899+ const std::vector<Widelands::DescriptionIndex>& column = icons_order().at(column_index);
1900+ const int row_number = column.size();
1901+ int16_t row_index_to_apply = 0;
1902+ for (int16_t row_index = 0; row_index < row_number; ++row_index) {
1903+ order_coords_.emplace(column.at(row_index), Widelands::Coords(column_index_to_apply, row_index_to_apply));
1904+ ++row_index_to_apply;
1905+ if (row_index_to_apply >= column_max_size) {
1906+ row_index_to_apply = 0;
1907+ ++column_index_to_apply;
1908+ }
1909+ }
1910+ if (row_index_to_apply > 0) {
1911+ ++column_index_to_apply;
1912+ }
1913 }
1914- NEVER_HERE();
1915 }
1916
1917 Vector2i AbstractWaresDisplay::ware_position(Widelands::DescriptionIndex id) const {
1918 Vector2i p(2, 2);
1919 if (horizontal_) {
1920- p.x += icons_order_coords()[id].second * (WARE_MENU_PIC_WIDTH + WARE_MENU_PIC_PAD_X);
1921- p.y += icons_order_coords()[id].first *
1922- (WARE_MENU_PIC_HEIGHT + WARE_MENU_PIC_PAD_Y + WARE_MENU_INFO_SIZE);
1923+ p.x += icons_order_coords().at(id).y * (kWareMenuPicWidth + hgap_);
1924+ p.y += icons_order_coords().at(id).x * (kWareMenuPicHeight + vgap_ + kWareMenuInfoSize);
1925 } else {
1926- p.x += icons_order_coords()[id].first * (WARE_MENU_PIC_WIDTH + WARE_MENU_PIC_PAD_X);
1927- p.y += icons_order_coords()[id].second *
1928- (WARE_MENU_PIC_HEIGHT + WARE_MENU_PIC_PAD_Y + WARE_MENU_INFO_SIZE);
1929+ p.x += icons_order_coords().at(id).x * (kWareMenuPicWidth + hgap_);
1930+ p.y += icons_order_coords().at(id).y * (kWareMenuPicHeight + vgap_ + kWareMenuInfoSize);
1931 }
1932 return p;
1933 }
1934@@ -328,15 +424,15 @@
1935 const Image* icon = type_ == Widelands::wwWORKER ? tribe_.get_worker_descr(id)->icon() :
1936 tribe_.get_ware_descr(id)->icon();
1937
1938- dst.blit(p + Vector2i((w - WARE_MENU_PIC_WIDTH) / 2, 1), icon);
1939+ dst.blit(p + Vector2i((w - kWareMenuPicWidth) / 2, 1), icon);
1940
1941- dst.fill_rect(Recti(p + Vector2i(0, WARE_MENU_PIC_HEIGHT), w, WARE_MENU_INFO_SIZE),
1942+ dst.fill_rect(Recti(p + Vector2i(0, kWareMenuPicHeight), w, kWareMenuInfoSize),
1943 info_color_for_ware(id));
1944
1945 std::shared_ptr<const UI::RenderedText> rendered_text =
1946 UI::g_fh->render(as_richtext_paragraph(info_for_ware(id), style.info_font()));
1947 rendered_text->draw(dst, Vector2i(p.x + w - rendered_text->width() - 1,
1948- p.y + WARE_MENU_PIC_HEIGHT + WARE_MENU_INFO_SIZE + 1 -
1949+ p.y + kWareMenuPicHeight + kWareMenuInfoSize + 1 -
1950 rendered_text->height()));
1951 }
1952
1953@@ -370,6 +466,10 @@
1954 hidden_[ware] = true;
1955 }
1956
1957+bool AbstractWaresDisplay::is_ware_hidden(Widelands::DescriptionIndex ware) const {
1958+ return hidden_.at(ware);
1959+}
1960+
1961 WaresDisplay::WaresDisplay(UI::Panel* const parent,
1962 int32_t x,
1963 int32_t y,
1964
1965=== modified file 'src/wui/waresdisplay.h'
1966--- src/wui/waresdisplay.h 2019-02-23 11:00:49 +0000
1967+++ src/wui/waresdisplay.h 2019-05-29 06:43:44 +0000
1968@@ -20,6 +20,7 @@
1969 #ifndef WL_WUI_WARESDISPLAY_H
1970 #define WL_WUI_WARESDISPLAY_H
1971
1972+#include <memory>
1973 #include <vector>
1974
1975 #include "logic/map_objects/tribes/tribe_descr.h"
1976@@ -36,6 +37,8 @@
1977 struct WareList;
1978 } // namespace Widelands
1979
1980+using WaresOrderCoords = std::map<Widelands::DescriptionIndex, Widelands::Coords>;
1981+
1982 /**
1983 * Display wares or workers together with some string (typically a number)
1984 * in the style of the @ref WarehouseWindow.
1985@@ -54,7 +57,9 @@
1986 CLANG_DIAG_OFF("-Wunknown-pragmas") CLANG_DIAG_OFF("-Wzero-as-null-pointer-constant")
1987 boost::function<void(Widelands::DescriptionIndex, bool)> callback_function = 0,
1988 CLANG_DIAG_ON("-Wzero-as-null-pointer-constant")
1989- CLANG_DIAG_ON("-Wunknown-pragmas") bool horizontal = false);
1990+ CLANG_DIAG_ON("-Wunknown-pragmas") bool horizontal = false,
1991+ int32_t hgap = 3,
1992+ int32_t vgap = 4);
1993
1994 bool
1995 handle_mousemove(uint8_t state, int32_t x, int32_t y, int32_t xdiff, int32_t ydiff) override;
1996@@ -68,12 +73,39 @@
1997
1998 // Wares may be hidden
1999 void hide_ware(Widelands::DescriptionIndex);
2000+ bool is_ware_hidden(Widelands::DescriptionIndex) const;
2001
2002 Widelands::DescriptionIndex ware_at_point(int32_t x, int32_t y) const;
2003 Widelands::WareWorker get_type() const {
2004 return type_;
2005 }
2006
2007+ int32_t get_hgap() {
2008+ return hgap_;
2009+ }
2010+ int32_t get_vgap() {
2011+ return vgap_;
2012+ }
2013+ void set_hgap(int32_t, bool = true);
2014+ void set_vgap(int32_t, bool = true);
2015+
2016+ Widelands::Extent get_extent() const;
2017+
2018+ const WaresOrderCoords& icons_order_coords() const;
2019+ Widelands::DescriptionIndex ware_at_coords(int16_t x, int16_t y) const;
2020+ uint16_t column_length(int16_t) const;
2021+
2022+ void set_min_free_vertical_space(int32_t s) {
2023+ min_free_vertical_space_ = s;
2024+ }
2025+ int32_t get_min_free_vertical_space() const {
2026+ return min_free_vertical_space_;
2027+ }
2028+
2029+ static inline int32_t calc_hgap(int32_t columns, int32_t total_w, int32_t min = 3) {
2030+ return std::max(min, (total_w - columns * kWareMenuPicWidth) / (columns - 1));
2031+ }
2032+
2033 protected:
2034 void layout() override;
2035
2036@@ -82,7 +114,6 @@
2037 virtual RGBColor info_color_for_ware(Widelands::DescriptionIndex);
2038
2039 const Widelands::TribeDescr::WaresOrder& icons_order() const;
2040- const Widelands::TribeDescr::WaresOrderCoords& icons_order_coords() const;
2041 virtual Vector2i ware_position(Widelands::DescriptionIndex) const;
2042 void draw(RenderTarget&) override;
2043 virtual void draw_ware(RenderTarget&, Widelands::DescriptionIndex);
2044@@ -110,6 +141,13 @@
2045 WareListSelectionType in_selection_; // Wares in temporary anchored selection
2046 bool selectable_;
2047 bool horizontal_;
2048+ int32_t hgap_;
2049+ int32_t vgap_;
2050+
2051+ WaresOrderCoords order_coords_;
2052+
2053+ void relayout_icons_order_coords();
2054+ void recalc_desired_size(bool);
2055
2056 /**
2057 * The ware on which the mouse press has been performed.
2058@@ -117,6 +155,9 @@
2059 */
2060 Widelands::DescriptionIndex selection_anchor_;
2061 boost::function<void(Widelands::DescriptionIndex, bool)> callback_function_;
2062+
2063+ std::unique_ptr<Notifications::Subscriber<GraphicResolutionChanged>> graphic_resolution_changed_subscriber_;
2064+ int32_t min_free_vertical_space_;
2065 };
2066
2067 /*

Subscribers

People subscribed via source and target branches

to status/vote changes: