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.

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.

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

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…

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) :
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

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

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.

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

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…

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…

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
=== added file 'data/images/ui_basic/scrollbar_down_fast.png'
0Binary 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 differ0Binary 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
=== added file 'data/images/ui_basic/scrollbar_up_fast.png'
1Binary 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 differ1Binary 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
=== added directory 'data/tribes/economy_profiles'
=== added file 'data/tribes/economy_profiles/atlanteans'
--- data/tribes/economy_profiles/atlanteans 1970-01-01 00:00:00 +0000
+++ data/tribes/economy_profiles/atlanteans 2019-05-29 06:43:44 +0000
@@ -0,0 +1,93 @@
1# Automatically created by Widelands bzr9094[economy-target-profiles] (Debug)
2
3[Default]
40=_"Efficiency"
51=_"Stockpile"
6
7[undeletable]
80="true"
91="true"
10
11[0]
12blackroot_flour="1"
13atlanteans_bread="20"
14bread_paddle="0"
15buckets="0"
16coal="5"
17cornmeal="3"
18diamond="3"
19fire_tongs="1"
20fishing_net="2"
21gold="1"
22gold_ore="1"
23gold_thread="0"
24granite="10"
25hammer="0"
26hook_pole="0"
27hunting_bow="1"
28iron="5"
29iron_ore="1"
30milking_tongs="0"
31pick="1"
32planks="1"
33quartz="3"
34saw="0"
35scythe="0"
36shield_advanced="0"
37shield_steel="0"
38shovel="0"
39smoked_fish="5"
40smoked_meat="3"
41spidercloth="5"
42spider_silk="5"
43tabard="1"
44tabard_golden="0"
45trident_double="0"
46trident_heavy_double="0"
47trident_light="1"
48trident_long="0"
49trident_steel="0"
50atlanteans_horse="1"
51atlanteans_soldier="10"
52
53[1]
54blackroot_flour="20"
55atlanteans_bread="30"
56bread_paddle="1"
57buckets="2"
58coal="25"
59cornmeal="20"
60diamond="10"
61fire_tongs="1"
62fishing_net="2"
63gold="20"
64gold_ore="15"
65gold_thread="5"
66granite="30"
67hammer="2"
68hook_pole="1"
69hunting_bow="1"
70iron="25"
71iron_ore="20"
72milking_tongs="1"
73pick="3"
74planks="40"
75quartz="10"
76saw="2"
77scythe="1"
78shield_advanced="1"
79shield_steel="1"
80shovel="2"
81smoked_fish="40"
82smoked_meat="25"
83spidercloth="20"
84spider_silk="15"
85tabard="30"
86tabard_golden="1"
87trident_double="1"
88trident_heavy_double="1"
89trident_light="30"
90trident_long="1"
91trident_steel="1"
92atlanteans_horse="20"
93atlanteans_soldier="20"
094
=== added file 'data/tribes/economy_profiles/barbarians'
--- data/tribes/economy_profiles/barbarians 1970-01-01 00:00:00 +0000
+++ data/tribes/economy_profiles/barbarians 2019-05-29 06:43:44 +0000
@@ -0,0 +1,85 @@
1# Automatically created by Widelands bzr9094[economy-target-profiles] (Debug)
2
3[Default]
40=_"Efficiency"
51=_"Stockpile"
6
7[undeletable]
80="true"
91="true"
10
11[0]
12ax="1"
13ax_battle="0"
14ax_broad="0"
15ax_bronze="0"
16ax_sharp="0"
17ax_warriors="0"
18beer="0"
19beer_strong="1"
20blackwood="40"
21barbarians_bread="5"
22bread_paddle="0"
23cloth="10"
24coal="20"
25felling_ax="0"
26fire_tongs="1"
27fishing_rod="0"
28gold="1"
29gold_ore="1"
30granite="10"
31grout="1"
32hammer="1"
33helmet="0"
34helmet_mask="0"
35helmet_warhelm="0"
36hunting_spear="0"
37iron="5"
38iron_ore="5"
39kitchen_tools="0"
40meal="5"
41pick="1"
42ration="20"
43scythe="0"
44shovel="0"
45snack="0"
46barbarians_ox="1"
47barbarians_soldier="10"
48
49[1]
50ax="30"
51ax_battle="1"
52ax_broad="1"
53ax_bronze="1"
54ax_sharp="1"
55ax_warriors="1"
56beer="15"
57beer_strong="20"
58blackwood="45"
59barbarians_bread="25"
60bread_paddle="1"
61cloth="10"
62coal="25"
63felling_ax="5"
64fire_tongs="1"
65fishing_rod="1"
66gold="20"
67gold_ore="15"
68granite="30"
69grout="20"
70hammer="2"
71helmet="1"
72helmet_mask="1"
73helmet_warhelm="1"
74hunting_spear="1"
75iron="25"
76iron_ore="20"
77kitchen_tools="1"
78meal="15"
79pick="2"
80ration="30"
81scythe="1"
82shovel="1"
83snack="20"
84barbarians_ox="20"
85barbarians_soldier="20"
086
=== added file 'data/tribes/economy_profiles/empire'
--- data/tribes/economy_profiles/empire 1970-01-01 00:00:00 +0000
+++ data/tribes/economy_profiles/empire 2019-05-29 06:43:44 +0000
@@ -0,0 +1,93 @@
1# Automatically created by Widelands bzr9094[economy-target-profiles] (Debug)
2
3[Default]
40=_"Efficiency"
51=_"Stockpile"
6
7[undeletable]
80="true"
91="true"
10
11[0]
12armor="1"
13armor_chain="1"
14armor_gilded="1"
15armor_helmet="30"
16basket="1"
17beer="1"
18empire_bread="20"
19bread_paddle="0"
20cloth="15"
21coal="5"
22felling_ax="0"
23fire_tongs="1"
24fishing_rod="0"
25flour="20"
26gold="1"
27gold_ore="1"
28granite="10"
29hammer="0"
30hunting_spear="0"
31iron="5"
32iron_ore="3"
33kitchen_tools="0"
34marble="30"
35marble_column="10"
36meal="5"
37meat="20"
38pick="1"
39planks="1"
40ration="20"
41saw="0"
42scythe="0"
43shovel="0"
44spear="1"
45spear_advanced="1"
46spear_heavy="1"
47spear_war="1"
48spear_wooden="30"
49wool="10"
50empire_donkey="1"
51empire_soldier="10"
52
53[1]
54armor="1"
55armor_chain="1"
56armor_gilded="1"
57armor_helmet="30"
58basket="1"
59beer="20"
60empire_bread="30"
61bread_paddle="1"
62cloth="15"
63coal="25"
64felling_ax="3"
65fire_tongs="1"
66fishing_rod="1"
67flour="25"
68gold="20"
69gold_ore="15"
70granite="30"
71hammer="2"
72hunting_spear="1"
73iron="25"
74iron_ore="20"
75kitchen_tools="1"
76marble="35"
77marble_column="15"
78meal="20"
79meat="30"
80pick="2"
81planks="40"
82ration="25"
83saw="1"
84scythe="1"
85shovel="1"
86spear="1"
87spear_advanced="1"
88spear_heavy="1"
89spear_war="1"
90spear_wooden="30"
91wool="15"
92empire_donkey="20"
93empire_soldier="20"
094
=== added file 'data/tribes/economy_profiles/frisians'
--- data/tribes/economy_profiles/frisians 1970-01-01 00:00:00 +0000
+++ data/tribes/economy_profiles/frisians 2019-05-29 06:43:44 +0000
@@ -0,0 +1,97 @@
1# Automatically created by Widelands bzr9093[economy-target-profiles] (Debug)
2
3[Default]
40=_"Efficiency"
51=_"Stockpile"
6
7[undeletable]
80="true"
91="true"
10
11[0]
12clay="30"
13brick="40"
14bread_frisians="20"
15honey_bread="20"
16mead="15"
17fur="10"
18fur_garment="30"
19fur_garment_studded="2"
20fur_garment_golden="2"
21helmet_golden="2"
22sword_short="30"
23sword_long="2"
24sword_broad="2"
25sword_double="2"
26needles="1"
27basket="1"
28beer="1"
29bread_paddle="1"
30cloth="10"
31coal="20"
32felling_ax="0"
33fire_tongs="1"
34fish="20"
35fishing_net="2"
36gold="1"
37gold_ore="1"
38granite="10"
39hammer="1"
40helmet="0"
41hunting_spear="0"
42iron="5"
43iron_ore="3"
44kitchen_tools="0"
45meal="1"
46pick="1"
47ration="20"
48scythe="0"
49shovel="0"
50smoked_fish="20"
51smoked_meat="10"
52frisians_reindeer="1"
53frisians_soldier="10"
54
55[1]
56clay="35"
57brick="50"
58bread_frisians="30"
59honey_bread="30"
60mead="30"
61fur="20"
62fur_garment="30"
63fur_garment_studded="2"
64fur_garment_golden="2"
65helmet_golden="2"
66sword_short="30"
67sword_long="2"
68sword_broad="2"
69sword_double="2"
70needles="1"
71basket="1"
72beer="30"
73bread_paddle="1"
74cloth="10"
75coal="35"
76felling_ax="3"
77fire_tongs="2"
78fish="40"
79fishing_net="2"
80gold="20"
81gold_ore="15"
82granite="35"
83hammer="3"
84helmet="2"
85hunting_spear="1"
86iron="25"
87iron_ore="20"
88kitchen_tools="2"
89meal="10"
90pick="3"
91ration="30"
92scythe="2"
93shovel="5"
94smoked_fish="30"
95smoked_meat="20"
96frisians_reindeer="20"
97frisians_soldier="20"
098
=== modified file 'src/logic/filesystem_constants.h'
--- src/logic/filesystem_constants.h 2019-04-18 16:50:35 +0000
+++ src/logic/filesystem_constants.h 2019-05-29 06:43:44 +0000
@@ -78,4 +78,6 @@
78/// Filesystem names for config78/// Filesystem names for config
79const std::string kConfigFile = "config";79const std::string kConfigFile = "config";
8080
81const std::string kEconomyProfilesDir = "tribes/economy_profiles";
82
81#endif // end of include guard: WL_LOGIC_FILESYSTEM_CONSTANTS_H83#endif // end of include guard: WL_LOGIC_FILESYSTEM_CONSTANTS_H
8284
=== modified file 'src/logic/map_objects/tribes/tribe_descr.cc'
--- src/logic/map_objects/tribes/tribe_descr.cc 2019-05-27 21:04:13 +0000
+++ src/logic/map_objects/tribes/tribe_descr.cc 2019-05-29 06:43:44 +0000
@@ -95,8 +95,6 @@
95 load_roads("busy", &busy_road_paths_);95 load_roads("busy", &busy_road_paths_);
9696
97 items_table = table.get_table("wares_order");97 items_table = table.get_table("wares_order");
98 wares_order_coords_.resize(tribes_.nrwares());
99 int columnindex = 0;
100 for (const int key : items_table->keys<int>()) {98 for (const int key : items_table->keys<int>()) {
101 std::vector<DescriptionIndex> column;99 std::vector<DescriptionIndex> column;
102 std::vector<std::string> warenames =100 std::vector<std::string> warenames =
@@ -110,7 +108,6 @@
110 }108 }
111 wares_.insert(wareindex);109 wares_.insert(wareindex);
112 column.push_back(wareindex);110 column.push_back(wareindex);
113 wares_order_coords_[wareindex] = std::make_pair(columnindex, rowindex);
114 } catch (const WException& e) {111 } catch (const WException& e) {
115 throw GameDataError(112 throw GameDataError(
116 "Failed adding ware '%s: %s", warenames[rowindex].c_str(), e.what());113 "Failed adding ware '%s: %s", warenames[rowindex].c_str(), e.what());
@@ -118,13 +115,10 @@
118 }115 }
119 if (!column.empty()) {116 if (!column.empty()) {
120 wares_order_.push_back(column);117 wares_order_.push_back(column);
121 ++columnindex;
122 }118 }
123 }119 }
124120
125 items_table = table.get_table("workers_order");121 items_table = table.get_table("workers_order");
126 workers_order_coords_.resize(tribes_.nrworkers());
127 columnindex = 0;
128 for (const int key : items_table->keys<int>()) {122 for (const int key : items_table->keys<int>()) {
129 std::vector<DescriptionIndex> column;123 std::vector<DescriptionIndex> column;
130 std::vector<std::string> workernames =124 std::vector<std::string> workernames =
@@ -138,7 +132,6 @@
138 }132 }
139 workers_.insert(workerindex);133 workers_.insert(workerindex);
140 column.push_back(workerindex);134 column.push_back(workerindex);
141 workers_order_coords_[workerindex] = std::make_pair(columnindex, rowindex);
142135
143 const WorkerDescr& worker_descr = *tribes_.get_worker_descr(workerindex);136 const WorkerDescr& worker_descr = *tribes_.get_worker_descr(workerindex);
144 if (worker_descr.is_buildable() && worker_descr.buildcost().empty()) {137 if (worker_descr.is_buildable() && worker_descr.buildcost().empty()) {
@@ -151,7 +144,6 @@
151 }144 }
152 if (!column.empty()) {145 if (!column.empty()) {
153 workers_order_.push_back(column);146 workers_order_.push_back(column);
154 ++columnindex;
155 }147 }
156 }148 }
157149
@@ -424,38 +416,6 @@
424 return list->second.find(lowest)->second;416 return list->second.find(lowest)->second;
425}417}
426418
427void TribeDescr::resize_ware_orders(size_t maxLength) {
428 bool need_resize = false;
429
430 // Check if we actually need to resize.
431 for (WaresOrder::iterator it = wares_order_.begin(); it != wares_order_.end(); ++it) {
432 if (it->size() > maxLength) {
433 need_resize = true;
434 }
435 }
436
437 // Build new smaller wares_order.
438 if (need_resize) {
439 WaresOrder new_wares_order;
440 for (WaresOrder::iterator it = wares_order_.begin(); it != wares_order_.end(); ++it) {
441 new_wares_order.push_back(std::vector<Widelands::DescriptionIndex>());
442 for (std::vector<Widelands::DescriptionIndex>::iterator it2 = it->begin();
443 it2 != it->end(); ++it2) {
444 if (new_wares_order.rbegin()->size() >= maxLength) {
445 new_wares_order.push_back(std::vector<Widelands::DescriptionIndex>());
446 }
447 new_wares_order.rbegin()->push_back(*it2);
448 wares_order_coords_[*it2].first = new_wares_order.size() - 1;
449 wares_order_coords_[*it2].second = new_wares_order.rbegin()->size() - 1;
450 }
451 }
452
453 // Remove old array.
454 wares_order_.clear();
455 wares_order_ = new_wares_order;
456 }
457}
458
459void TribeDescr::add_building(const std::string& buildingname) {419void TribeDescr::add_building(const std::string& buildingname) {
460 try {420 try {
461 DescriptionIndex index = tribes_.safe_building_index(buildingname);421 DescriptionIndex index = tribes_.safe_building_index(buildingname);
462422
=== modified file 'src/logic/map_objects/tribes/tribe_descr.h'
--- src/logic/map_objects/tribes/tribe_descr.h 2019-05-18 13:33:00 +0000
+++ src/logic/map_objects/tribes/tribe_descr.h 2019-05-29 06:43:44 +0000
@@ -148,22 +148,13 @@
148 }148 }
149149
150 using WaresOrder = std::vector<std::vector<Widelands::DescriptionIndex>>;150 using WaresOrder = std::vector<std::vector<Widelands::DescriptionIndex>>;
151 using WaresOrderCoords = std::vector<std::pair<uint32_t, uint32_t>>;
152 const WaresOrder& wares_order() const {151 const WaresOrder& wares_order() const {
153 return wares_order_;152 return wares_order_;
154 }153 }
155 const WaresOrderCoords& wares_order_coords() const {
156 return wares_order_coords_;
157 }
158154
159 const WaresOrder& workers_order() const {155 const WaresOrder& workers_order() const {
160 return workers_order_;156 return workers_order_;
161 }157 }
162 const WaresOrderCoords& workers_order_coords() const {
163 return workers_order_coords_;
164 }
165
166 void resize_ware_orders(size_t maxLength);
167158
168 const std::vector<std::string>& get_ship_names() const {159 const std::vector<std::string>& get_ship_names() const {
169 return ship_names_;160 return ship_names_;
@@ -213,9 +204,7 @@
213 std::vector<DescriptionIndex> trainingsites_;204 std::vector<DescriptionIndex> trainingsites_;
214 // Order and positioning of wares in the warehouse display205 // Order and positioning of wares in the warehouse display
215 WaresOrder wares_order_;206 WaresOrder wares_order_;
216 WaresOrderCoords wares_order_coords_;
217 WaresOrder workers_order_;207 WaresOrder workers_order_;
218 WaresOrderCoords workers_order_coords_;
219208
220 std::vector<Widelands::TribeBasicInfo::Initialization> initializations_;209 std::vector<Widelands::TribeBasicInfo::Initialization> initializations_;
221210
222211
=== modified file 'src/logic/map_objects/tribes/tribes.cc'
--- src/logic/map_objects/tribes/tribes.cc 2019-05-22 11:23:14 +0000
+++ src/logic/map_objects/tribes/tribes.cc 2019-05-29 06:43:44 +0000
@@ -347,14 +347,9 @@
347 // Calculate the trainingsites proportions.347 // Calculate the trainingsites proportions.
348 postload_calculate_trainingsites_proportions();348 postload_calculate_trainingsites_proportions();
349349
350 // Resize the configuration of our wares if they won't fit in the current window (12 = info label350 // Some final checks on the gamedata
351 // size).
352 // Also, do some final checks on the gamedata
353 int number = (g_gr->get_yres() - 290) / (WARE_MENU_PIC_HEIGHT + WARE_MENU_PIC_PAD_Y + 12);
354 for (DescriptionIndex i = 0; i < tribes_->size(); ++i) {351 for (DescriptionIndex i = 0; i < tribes_->size(); ++i) {
355 TribeDescr* tribe_descr = tribes_->get_mutable(i);352 TribeDescr* tribe_descr = tribes_->get_mutable(i);
356 tribe_descr->resize_ware_orders(number);
357
358 // Verify that the preciousness has been set for all of the tribe's wares353 // Verify that the preciousness has been set for all of the tribe's wares
359 for (const DescriptionIndex wi : tribe_descr->wares()) {354 for (const DescriptionIndex wi : tribe_descr->wares()) {
360 if (tribe_descr->get_ware_descr(wi)->ai_hints().preciousness(tribe_descr->name()) ==355 if (tribe_descr->get_ware_descr(wi)->ai_hints().preciousness(tribe_descr->name()) ==
361356
=== modified file 'src/logic/map_objects/tribes/ware_descr.h'
--- src/logic/map_objects/tribes/ware_descr.h 2019-05-22 11:23:14 +0000
+++ src/logic/map_objects/tribes/ware_descr.h 2019-05-29 06:43:44 +0000
@@ -35,10 +35,8 @@
35class Image;35class Image;
36class LuaTable;36class LuaTable;
3737
38#define WARE_MENU_PIC_WIDTH 24 //!< Default width for ware's menu icons38constexpr int kWareMenuPicWidth = 24; //!< Default width for ware's menu icons
39#define WARE_MENU_PIC_HEIGHT 24 //!< Default height for ware's menu icons39constexpr int kWareMenuPicHeight = 24; //!< Default height for ware's menu icons
40#define WARE_MENU_PIC_PAD_X 3 //!< Default padding between menu icons
41#define WARE_MENU_PIC_PAD_Y 4 //!< Default padding between menu icons
4240
43namespace Widelands {41namespace Widelands {
4442
4543
=== modified file 'src/logic/playercommand.h'
--- src/logic/playercommand.h 2019-05-07 12:14:02 +0000
+++ src/logic/playercommand.h 2019-05-29 06:43:44 +0000
@@ -581,6 +581,7 @@
581 uint32_t permanent_;581 uint32_t permanent_;
582};582};
583583
584// TODO(Nordfriese): CmdResetWareTargetQuantity can be removed when we next break savegame compatibility
584struct CmdResetWareTargetQuantity : public CmdChangeTargetQuantity {585struct CmdResetWareTargetQuantity : public CmdChangeTargetQuantity {
585 CmdResetWareTargetQuantity() : CmdChangeTargetQuantity() {586 CmdResetWareTargetQuantity() : CmdChangeTargetQuantity() {
586 }587 }
@@ -629,6 +630,7 @@
629 uint32_t permanent_;630 uint32_t permanent_;
630};631};
631632
633// TODO(Nordfriese): CmdResetWorkerTargetQuantity can be removed when we next break savegame compatibility
632struct CmdResetWorkerTargetQuantity : public CmdChangeTargetQuantity {634struct CmdResetWorkerTargetQuantity : public CmdChangeTargetQuantity {
633 CmdResetWorkerTargetQuantity() : CmdChangeTargetQuantity() {635 CmdResetWorkerTargetQuantity() : CmdChangeTargetQuantity() {
634 }636 }
635637
=== modified file 'src/notifications/note_ids.h'
--- src/notifications/note_ids.h 2019-02-23 11:00:49 +0000
+++ src/notifications/note_ids.h 2019-05-29 06:43:44 +0000
@@ -36,6 +36,7 @@
36 Ship,36 Ship,
37 Building,37 Building,
38 Economy,38 Economy,
39 EconomyProfile,
39 GraphicResolutionChanged,40 GraphicResolutionChanged,
40 NoteExpeditionCanceled,41 NoteExpeditionCanceled,
41 Sound,42 Sound,
4243
=== modified file 'src/wui/CMakeLists.txt'
--- src/wui/CMakeLists.txt 2019-05-12 07:45:59 +0000
+++ src/wui/CMakeLists.txt 2019-05-29 06:43:44 +0000
@@ -46,8 +46,10 @@
46 graphic46 graphic
47 logic47 logic
48 logic_commands48 logic_commands
49 logic_filesystem_constants
49 logic_map_objects50 logic_map_objects
50 notifications51 notifications
52 profile
51 ui_basic53 ui_basic
52 wui_waresdisplay54 wui_waresdisplay
53)55)
5456
=== modified file 'src/wui/economy_options_window.cc'
--- src/wui/economy_options_window.cc 2019-05-01 07:20:25 +0000
+++ src/wui/economy_options_window.cc 2019-05-29 06:43:44 +0000
@@ -19,36 +19,104 @@
1919
20#include "wui/economy_options_window.h"20#include "wui/economy_options_window.h"
2121
22#include <memory>
23
22#include <boost/lexical_cast.hpp>24#include <boost/lexical_cast.hpp>
2325
24#include "graphic/graphic.h"26#include "graphic/graphic.h"
25#include "logic/editor_game_base.h"27#include "logic/editor_game_base.h"
28#include "logic/filesystem_constants.h"
26#include "logic/map_objects/tribes/ware_descr.h"29#include "logic/map_objects/tribes/ware_descr.h"
27#include "logic/map_objects/tribes/worker_descr.h"30#include "logic/map_objects/tribes/worker_descr.h"
28#include "logic/player.h"31#include "logic/player.h"
29#include "logic/playercommand.h"32#include "logic/playercommand.h"
30#include "ui_basic/button.h"33#include "profile/profile.h"
34#include "ui_basic/messagebox.h"
3135
32static const char pic_tab_wares[] = "images/wui/buildings/menu_tab_wares.png";36static const char pic_tab_wares[] = "images/wui/buildings/menu_tab_wares.png";
33static const char pic_tab_workers[] = "images/wui/buildings/menu_tab_workers.png";37static const char pic_tab_workers[] = "images/wui/buildings/menu_tab_workers.png";
3438
39constexpr int kDesiredWidth = 216;
40
35EconomyOptionsWindow::EconomyOptionsWindow(UI::Panel* parent,41EconomyOptionsWindow::EconomyOptionsWindow(UI::Panel* parent,
36 Widelands::Economy* economy,42 Widelands::Economy* economy,
37 bool can_act)43 bool can_act)
38 : UI::Window(parent, "economy_options", 0, 0, 0, 0, _("Economy options")),44 : UI::Window(parent, "economy_options", 0, 0, 0, 0, _("Economy options")),
45 main_box_(this, 0, 0, UI::Box::Vertical),
39 serial_(economy->serial()),46 serial_(economy->serial()),
40 player_(&economy->owner()),47 player_(&economy->owner()),
41 tabpanel_(this, UI::TabPanelStyle::kWuiDark),48 tabpanel_(this, UI::TabPanelStyle::kWuiDark),
42 ware_panel_(new EconomyOptionsPanel(&tabpanel_, serial_, player_, can_act, Widelands::wwWARE)),49 ware_panel_(new EconomyOptionsPanel(&tabpanel_, this, serial_, player_, can_act, Widelands::wwWARE, kDesiredWidth)),
43 worker_panel_(50 worker_panel_(
44 new EconomyOptionsPanel(&tabpanel_, serial_, player_, can_act, Widelands::wwWORKER)) {51 new EconomyOptionsPanel(&tabpanel_, this, serial_, player_, can_act, Widelands::wwWORKER, kDesiredWidth)),
45 set_center_panel(&tabpanel_);52 dropdown_box_(this, 0, 0, UI::Box::Horizontal),
53 dropdown_(&dropdown_box_, 0, 0, 174, 200, 34, "", UI::DropdownType::kTextual, UI::PanelStyle::kWui),
54 time_last_thought_(0),
55 save_profile_dialog_(nullptr) {
56 set_center_panel(&main_box_);
4657
47 tabpanel_.add("wares", g_gr->images().get(pic_tab_wares), ware_panel_, _("Wares"));58 tabpanel_.add("wares", g_gr->images().get(pic_tab_wares), ware_panel_, _("Wares"));
48 tabpanel_.add("workers", g_gr->images().get(pic_tab_workers), worker_panel_, _("Workers"));59 tabpanel_.add("workers", g_gr->images().get(pic_tab_workers), worker_panel_, _("Workers"));
60
61 UI::Box* buttons = new UI::Box(this, 0, 0, UI::Box::Horizontal);
62 UI::Button* b = new UI::Button(buttons, "decrease_target_fast", 0, 0, 44, 28, UI::ButtonStyle::kWuiSecondary,
63 g_gr->images().get("images/ui_basic/scrollbar_down_fast.png"), _("Decrease target by 10"));
64 b->sigclicked.connect([this] { change_target(-10); });
65 buttons->add(b);
66 b->set_repeating(true);
67 buttons->add_space(8);
68 b = new UI::Button(buttons, "decrease_target", 0, 0, 44, 28, UI::ButtonStyle::kWuiSecondary,
69 g_gr->images().get("images/ui_basic/scrollbar_down.png"), _("Decrease target"));
70 b->sigclicked.connect([this] { change_target(-1); });
71 buttons->add(b);
72 b->set_repeating(true);
73 buttons->add_space(24);
74
75 b = new UI::Button(buttons, "increase_target", 0, 0, 44, 28, UI::ButtonStyle::kWuiSecondary,
76 g_gr->images().get("images/ui_basic/scrollbar_up.png"), _("Increase target"));
77 b->sigclicked.connect([this] { change_target(1); });
78 buttons->add(b);
79 b->set_repeating(true);
80 buttons->add_space(8);
81 b = new UI::Button(buttons, "increase_target_fast", 0, 0, 44, 28, UI::ButtonStyle::kWuiSecondary,
82 g_gr->images().get("images/ui_basic/scrollbar_up_fast.png"), _("Increase target by 10"));
83 b->sigclicked.connect([this] { change_target(10); });
84 buttons->add(b);
85 b->set_repeating(true);
86
87 dropdown_.set_tooltip(_("Profile to apply to the selected items"));
88 dropdown_box_.set_size(40, 20); // Prevent assert failures
89 dropdown_box_.add(&dropdown_, UI::Box::Resizing::kFullSize);
90 dropdown_.selected.connect([this] { reset_target(); });
91
92 b = new UI::Button(&dropdown_box_, "save_targets", 0, 0, 34, 34, UI::ButtonStyle::kWuiMenu,
93 g_gr->images().get("images/wui/menus/menu_save_game.png"), _("Save target settings"));
94 b->sigclicked.connect([this] { create_target(); });
95 dropdown_box_.add_space(8);
96 dropdown_box_.add(b);
97
98 main_box_.add(&tabpanel_, UI::Box::Resizing::kAlign, UI::Align::kCenter);
99 main_box_.add_space(8);
100 main_box_.add(buttons, UI::Box::Resizing::kAlign, UI::Align::kCenter);
101 main_box_.add_space(8);
102 main_box_.add(&dropdown_box_, UI::Box::Resizing::kAlign, UI::Align::kCenter);
103
49 economy->set_has_window(true);104 economy->set_has_window(true);
50 economynotes_subscriber_ = Notifications::subscribe<Widelands::NoteEconomy>(105 economynotes_subscriber_ = Notifications::subscribe<Widelands::NoteEconomy>(
51 [this](const Widelands::NoteEconomy& note) { on_economy_note(note); });106 [this](const Widelands::NoteEconomy& note) { on_economy_note(note); });
107 profilenotes_subscriber_ = Notifications::subscribe<NoteEconomyProfile>(
108 [this](const NoteEconomyProfile& n) {
109 if (n.serial == serial_) {
110 // We already updated ourself before we changed something
111 return;
112 }
113 read_targets();
114 if (save_profile_dialog_) {
115 save_profile_dialog_->update_table();
116 }
117 });
118
119 read_targets();
52}120}
53121
54EconomyOptionsWindow::~EconomyOptionsWindow() {122EconomyOptionsWindow::~EconomyOptionsWindow() {
@@ -56,6 +124,9 @@
56 if (economy != nullptr) {124 if (economy != nullptr) {
57 economy->set_has_window(false);125 economy->set_has_window(false);
58 }126 }
127 if (save_profile_dialog_) {
128 save_profile_dialog_->unset_parent();
129 }
59}130}
60131
61void EconomyOptionsWindow::on_economy_note(const Widelands::NoteEconomy& note) {132void EconomyOptionsWindow::on_economy_note(const Widelands::NoteEconomy& note) {
@@ -81,6 +152,20 @@
81 }152 }
82}153}
83154
155void EconomyOptionsWindow::layout() {
156 int w, h;
157 tabpanel_.get_desired_size(&w, &h);
158 main_box_.set_desired_size(w, h + 78);
159 update_desired_size();
160 UI::Window::layout();
161}
162
163void EconomyOptionsWindow::EconomyOptionsPanel::update_desired_size() {
164 display_.set_hgap(AbstractWaresDisplay::calc_hgap(display_.get_extent().w, kDesiredWidth));
165 Box::update_desired_size();
166 get_parent()->layout();
167}
168
84EconomyOptionsWindow::TargetWaresDisplay::TargetWaresDisplay(UI::Panel* const parent,169EconomyOptionsWindow::TargetWaresDisplay::TargetWaresDisplay(UI::Panel* const parent,
85 int32_t const x,170 int32_t const x,
86 int32_t const y,171 int32_t const y,
@@ -129,42 +214,26 @@
129 * Wraps the wares/workers display together with some buttons214 * Wraps the wares/workers display together with some buttons
130 */215 */
131EconomyOptionsWindow::EconomyOptionsPanel::EconomyOptionsPanel(UI::Panel* parent,216EconomyOptionsWindow::EconomyOptionsPanel::EconomyOptionsPanel(UI::Panel* parent,
217 EconomyOptionsWindow* eco_window,
132 Widelands::Serial serial,218 Widelands::Serial serial,
133 Widelands::Player* player,219 Widelands::Player* player,
134 bool can_act,220 bool can_act,
135 Widelands::WareWorker type)221 Widelands::WareWorker type,
222 int32_t min_w)
136 : UI::Box(parent, 0, 0, UI::Box::Vertical),223 : UI::Box(parent, 0, 0, UI::Box::Vertical),
137 serial_(serial),224 serial_(serial),
138 player_(player),225 player_(player),
139 type_(type),226 type_(type),
140 can_act_(can_act),227 can_act_(can_act),
141 display_(this, 0, 0, serial_, player_, type_, can_act_) {228 display_(this, 0, 0, serial_, player_, type_, can_act_),
229 economy_options_window_(eco_window) {
142 add(&display_, UI::Box::Resizing::kFullSize);230 add(&display_, UI::Box::Resizing::kFullSize);
143231
232 display_.set_hgap(AbstractWaresDisplay::calc_hgap(display_.get_extent().w, min_w));
233
144 if (!can_act_) {234 if (!can_act_) {
145 return;235 return;
146 }236 }
147 UI::Box* buttons = new UI::Box(this, 0, 0, UI::Box::Horizontal);
148 add(buttons);
149
150 UI::Button* b = new UI::Button(buttons, "decrease_target", 0, 0, 34, 34,
151 UI::ButtonStyle::kWuiMenu, "-", _("Decrease target"));
152 b->sigclicked.connect(boost::bind(&EconomyOptionsPanel::change_target, this, -1));
153 buttons->add(b);
154 b->set_repeating(true);
155 buttons->add_space(8);
156
157 b = new UI::Button(buttons, "increase_target", 0, 0, 34, 34, UI::ButtonStyle::kWuiMenu, "+",
158 _("Increase target"));
159 b->sigclicked.connect(boost::bind(&EconomyOptionsPanel::change_target, this, 1));
160 buttons->add(b);
161 b->set_repeating(true);
162 buttons->add_space(8);
163
164 b = new UI::Button(
165 buttons, "reset_target", 0, 0, 34, 34, UI::ButtonStyle::kWuiMenu, "R", _("Reset to default"));
166 b->sigclicked.connect(boost::bind(&EconomyOptionsPanel::reset_target, this));
167 buttons->add(b);
168}237}
169238
170void EconomyOptionsWindow::EconomyOptionsPanel::set_economy(Widelands::Serial serial) {239void EconomyOptionsWindow::EconomyOptionsPanel::set_economy(Widelands::Serial serial) {
@@ -172,7 +241,29 @@
172 display_.set_economy(serial);241 display_.set_economy(serial);
173}242}
174243
175void EconomyOptionsWindow::EconomyOptionsPanel::change_target(int amount) {244void EconomyOptionsWindow::change_target(int amount) {
245 if (tabpanel_.active() == 0) {
246 ware_panel_->change_target(amount);
247 } else {
248 worker_panel_->change_target(amount);
249 }
250}
251
252void EconomyOptionsWindow::reset_target() {
253 if (dropdown_.get_selected().empty()) {
254 return;
255 }
256 if (tabpanel_.active() == 0) {
257 ware_panel_->reset_target();
258 } else {
259 worker_panel_->reset_target();
260 }
261}
262
263void EconomyOptionsWindow::EconomyOptionsPanel::change_target(int delta) {
264 if (delta == 0) {
265 return;
266 }
176 Widelands::Economy* economy = player_->get_economy(serial_);267 Widelands::Economy* economy = player_->get_economy(serial_);
177 if (economy == nullptr) {268 if (economy == nullptr) {
178 die();269 die();
@@ -186,35 +277,401 @@
186 const Widelands::Economy::TargetQuantity& tq = is_wares ?277 const Widelands::Economy::TargetQuantity& tq = is_wares ?
187 economy->ware_target_quantity(index) :278 economy->ware_target_quantity(index) :
188 economy->worker_target_quantity(index);279 economy->worker_target_quantity(index);
189 // Don't allow negative new amount.280 // Don't allow negative new amount
190 if (amount >= 0 || -amount <= static_cast<int>(tq.permanent)) {281 const int old_amount = static_cast<int>(tq.permanent);
282 const int new_amount = std::max(0, old_amount + delta);
283 if (new_amount == old_amount) {
284 continue;
285 }
286 if (is_wares) {
287 game.send_player_command(new Widelands::CmdSetWareTargetQuantity(
288 game.get_gametime(), player_->player_number(), serial_, index, new_amount));
289 } else {
290 game.send_player_command(new Widelands::CmdSetWorkerTargetQuantity(
291 game.get_gametime(), player_->player_number(), serial_, index, new_amount));
292 }
293 }
294 }
295}
296
297void EconomyOptionsWindow::EconomyOptionsPanel::reset_target() {
298 Widelands::Game& game = dynamic_cast<Widelands::Game&>(player_->egbase());
299 const bool is_wares = type_ == Widelands::wwWARE;
300 const auto& items = is_wares ? player_->tribe().wares() : player_->tribe().workers();
301 const PredefinedTargets settings = economy_options_window_->get_selected_target();
302
303 bool anything_selected = false;
304 bool second_phase = false;
305
306 do {
307 for (const Widelands::DescriptionIndex& index : items) {
308 if (display_.ware_selected(index) || (second_phase && !display_.is_ware_hidden(index))) {
309 anything_selected = true;
191 if (is_wares) {310 if (is_wares) {
192 game.send_player_command(new Widelands::CmdSetWareTargetQuantity(311 game.send_player_command(new Widelands::CmdSetWareTargetQuantity(
193 game.get_gametime(), player_->player_number(), serial_, index,312 game.get_gametime(), player_->player_number(), serial_, index, settings.wares.at(index)));
194 tq.permanent + amount));
195 } else {313 } else {
196 game.send_player_command(new Widelands::CmdSetWorkerTargetQuantity(314 game.send_player_command(new Widelands::CmdSetWorkerTargetQuantity(
197 game.get_gametime(), player_->player_number(), serial_, index,315 game.get_gametime(), player_->player_number(), serial_, index, settings.workers.at(index)));
198 tq.permanent + amount));316 }
199 }317 }
200 }318 }
201 }319 if (anything_selected) {
202 }320 return;
203}321 }
204322 // Nothing was selected, now go through the loop again and change everything
205void EconomyOptionsWindow::EconomyOptionsPanel::reset_target() {323 second_phase = true;
206 Widelands::Game& game = dynamic_cast<Widelands::Game&>(player_->egbase());324 } while (!second_phase);
207 const bool is_wares = type_ == Widelands::wwWARE;325}
208 const auto& items = is_wares ? player_->tribe().wares() : player_->tribe().workers();326
209 for (const Widelands::DescriptionIndex& index : items) {327constexpr unsigned kThinkInterval = 200;
210 if (display_.ware_selected(index)) {328
211 if (is_wares) {329void EconomyOptionsWindow::think() {
212 game.send_player_command(new Widelands::CmdResetWareTargetQuantity(330 const uint32_t time = player_->egbase().get_gametime();
213 game.get_gametime(), player_->player_number(), serial_, index));331 if (time - time_last_thought_ < kThinkInterval || !player_->get_economy(serial_)) {
214 } else {332 // If our economy has been deleted, die() was already called, no need to do anything
215 game.send_player_command(new Widelands::CmdResetWorkerTargetQuantity(333 return;
216 game.get_gametime(), player_->player_number(), serial_, index));334 }
217 }335 time_last_thought_ = time;
218 }336 update_profiles();
219 }337}
338
339std::string EconomyOptionsWindow::applicable_target() {
340 const Widelands::Economy* eco = player_->get_economy(serial_);
341 for (const auto& pair : predefined_targets_) {
342 bool matches = true;
343 if (tabpanel_.active() == 0) {
344 for (const Widelands::DescriptionIndex& index : player_->tribe().wares()) {
345 const auto it = pair.second.wares.find(index);
346 if (it != pair.second.wares.end() && eco->ware_target_quantity(index).permanent != it->second) {
347 matches = false;
348 break;
349 }
350 }
351 } else {
352 for (const Widelands::DescriptionIndex& index : player_->tribe().workers()) {
353 const auto it = pair.second.workers.find(index);
354 if (it != pair.second.workers.end() && eco->worker_target_quantity(index).permanent != it->second) {
355 matches = false;
356 break;
357 }
358 }
359 }
360 if (matches) {
361 return pair.first;
362 }
363 }
364 return "";
365}
366
367void EconomyOptionsWindow::update_profiles() {
368 const std::string current_profile = applicable_target();
369
370 for (const auto& pair : predefined_targets_) {
371 if (last_added_to_dropdown_.count(pair.first) == 0) {
372 return update_profiles_needed(current_profile);
373 }
374 }
375 for (const auto& string : last_added_to_dropdown_) {
376 if (!string.empty() && predefined_targets_.find(string) == predefined_targets_.end()) {
377 return update_profiles_needed(current_profile);
378 }
379 }
380 if (last_added_to_dropdown_.count("") == (current_profile.empty() ? 0 : 1)) {
381 return update_profiles_needed(current_profile);
382 }
383
384 update_profiles_select(current_profile);
385}
386
387void EconomyOptionsWindow::update_profiles_needed(const std::string& current_profile) {
388 dropdown_.clear();
389 last_added_to_dropdown_.clear();
390 for (const auto& pair : predefined_targets_) {
391 dropdown_.add(_(pair.first), pair.first);
392 last_added_to_dropdown_.insert(pair.first);
393 }
394 if (current_profile.empty()) {
395 // Nothing selected
396 dropdown_.add("–", "");
397 last_added_to_dropdown_.insert("");
398 }
399 update_profiles_select(current_profile);
400}
401
402void EconomyOptionsWindow::update_profiles_select(const std::string& current_profile) {
403 if (dropdown_.is_expanded()) {
404 return;
405 }
406 if (!dropdown_.has_selection() || dropdown_.get_selected() != current_profile) {
407 dropdown_.select(current_profile);
408 }
409 assert(dropdown_.has_selection());
410}
411
412void EconomyOptionsWindow::SaveProfileWindow::update_save_enabled() {
413 const std::string& text = profile_name_.text();
414 if (text.empty() || text == kDefaultEconomyProfile) {
415 save_.set_enabled(false);
416 save_.set_tooltip(text.empty() ? _("The profile name cannot be empty") :
417 _("The default profile cannot be overwritten"));
418 } else {
419 save_.set_enabled(true);
420 save_.set_tooltip(_("Save the profile under this name"));
421 }
422}
423
424void EconomyOptionsWindow::SaveProfileWindow::table_selection_changed() {
425 if (!table_.has_selection()) {
426 delete_.set_enabled(false);
427 delete_.set_tooltip("");
428 return;
429 }
430 const std::string& sel = table_[table_.selection_index()];
431 if (economy_options_->get_predefined_targets().at(sel).undeletable) {
432 delete_.set_tooltip(_("The predefined profiles cannot be deleted"));
433 delete_.set_enabled(false);
434 } else {
435 delete_.set_tooltip(_("Delete the selected profiles"));
436 delete_.set_enabled(true);
437 }
438 profile_name_.set_text(sel);
439 update_save_enabled();
440}
441
442void EconomyOptionsWindow::SaveProfileWindow::update_table() {
443 table_.clear();
444 for (const auto& pair : economy_options_->get_predefined_targets()) {
445 table_.add(pair.first).set_string(0, _(pair.first));
446 }
447 layout();
448}
449
450void EconomyOptionsWindow::SaveProfileWindow::save() {
451 const std::string name = profile_name_.text();
452 assert(!name.empty());
453 assert(name != kDefaultEconomyProfile);
454 for (const auto& pair : economy_options_->get_predefined_targets()) {
455 if (pair.first == name) {
456 UI::WLMessageBox m(this, _("Overwrite?"),
457 _("A profile with this name already exists. Do you wish to replace it?"),
458 UI::WLMessageBox::MBoxType::kOkCancel);
459 if (m.run<UI::Panel::Returncodes>() != UI::Panel::Returncodes::kOk) {
460 return;
461 }
462 break;
463 }
464 }
465 economy_options_->do_create_target(name);
466 die();
467}
468
469void EconomyOptionsWindow::SaveProfileWindow::delete_selected() {
470 assert(table_.has_selection());
471
472 auto& map = economy_options_->get_predefined_targets();
473 const std::string& name = table_[table_.selection_index()];
474
475 assert(name != kDefaultEconomyProfile);
476 assert(!map.at(name).undeletable);
477 auto it = map.find(name);
478 assert(it != map.end());
479 map.erase(it);
480
481 economy_options_->save_targets();
482 economy_options_->update_profiles();
483 update_table();
484}
485
486void EconomyOptionsWindow::SaveProfileWindow::unset_parent() {
487 economy_options_ = nullptr;
488 die();
489}
490
491void EconomyOptionsWindow::SaveProfileWindow::think() {
492 if (!economy_options_) {
493 die();
494 }
495 UI::Window::think();
496}
497
498EconomyOptionsWindow::SaveProfileWindow::SaveProfileWindow(UI::Panel* parent, EconomyOptionsWindow* eco)
499 : UI::Window(parent, "save_economy_options_profile", 0, 0, 0, 0, _("Save Profile")),
500 economy_options_(eco),
501 main_box_(this, 0, 0, UI::Box::Vertical),
502 table_box_(&main_box_, 0, 0, UI::Box::Vertical),
503 table_(&table_box_, 0, 0, 460, 120, UI::PanelStyle::kWui),
504 buttons_box_(&main_box_, 0, 0, UI::Box::Horizontal),
505 profile_name_(&buttons_box_, 0, 0, 240, UI::PanelStyle::kWui),
506 save_(&buttons_box_, "save", 0, 0, 80, 34, UI::ButtonStyle::kWuiPrimary, _("Save")),
507 cancel_(&buttons_box_, "cancel", 0, 0, 80, 34, UI::ButtonStyle::kWuiSecondary, _("Cancel")),
508 delete_(&buttons_box_, "delete", 0, 0, 80, 34, UI::ButtonStyle::kWuiSecondary, _("Delete")) {
509 table_.add_column(200, _("Existing Profiles"));
510 update_table();
511
512 table_.selected.connect([this](uint32_t) { table_selection_changed(); });
513 profile_name_.changed.connect([this] { update_save_enabled(); });
514 profile_name_.ok.connect([this] { save(); });
515 profile_name_.cancel.connect([this] { die(); });
516 save_.sigclicked.connect([this] { save(); });
517 cancel_.sigclicked.connect([this] { die(); });
518 delete_.sigclicked.connect([this] { delete_selected(); });
519
520 table_box_.add(&table_, UI::Box::Resizing::kFullSize);
521 buttons_box_.add(&profile_name_, UI::Box::Resizing::kFullSize);
522 buttons_box_.add(&save_);
523 buttons_box_.add(&cancel_);
524 buttons_box_.add(&delete_);
525 main_box_.add(&table_box_, UI::Box::Resizing::kFullSize);
526 main_box_.add(&buttons_box_, UI::Box::Resizing::kFullSize);
527 set_center_panel(&main_box_);
528
529 table_selection_changed();
530 update_save_enabled();
531}
532
533EconomyOptionsWindow::SaveProfileWindow::~SaveProfileWindow() {
534 if (economy_options_) {
535 economy_options_->close_save_profile_window();
536 }
537}
538
539void EconomyOptionsWindow::create_target() {
540 if (save_profile_dialog_) {
541 // Already open
542 return;
543 }
544 save_profile_dialog_ = new SaveProfileWindow(get_parent(), this);
545}
546
547void EconomyOptionsWindow::close_save_profile_window() {
548 assert(save_profile_dialog_);
549 save_profile_dialog_ = nullptr;
550}
551
552void EconomyOptionsWindow::do_create_target(const std::string& name) {
553 assert(!name.empty());
554 assert(name != kDefaultEconomyProfile);
555 const Widelands::Tribes& tribes = player_->egbase().tribes();
556 const Widelands::TribeDescr& tribe = player_->tribe();
557 Widelands::Economy* economy = player_->get_economy(serial_);
558 PredefinedTargets t;
559 for (Widelands::DescriptionIndex di : tribe.wares()) {
560 if (tribes.get_ware_descr(di)->has_demand_check(tribe.name())) {
561 t.wares[di] = economy->ware_target_quantity(di).permanent;
562 }
563 }
564 for (Widelands::DescriptionIndex di : tribe.workers()) {
565 if (tribes.get_worker_descr(di)->has_demand_check()) {
566 t.workers[di] = economy->worker_target_quantity(di).permanent;
567 }
568 }
569 predefined_targets_[name] = t;
570
571 save_targets();
572 update_profiles();
573}
574
575void EconomyOptionsWindow::save_targets() {
576 const Widelands::Tribes& tribes = player_->egbase().tribes();
577 Profile profile;
578
579 std::map<std::string, uint32_t> serials;
580 for (const auto& pair : predefined_targets_) {
581 if (pair.first != kDefaultEconomyProfile) {
582 serials.insert(std::make_pair(pair.first, serials.size()));
583 }
584 }
585 Section& global_section = profile.create_section(kDefaultEconomyProfile.c_str());
586 for (const auto& pair : serials) {
587 global_section.set_string(std::to_string(pair.second).c_str(), pair.first);
588 }
589
590 for (const auto& pair : predefined_targets_) {
591 if (pair.first == kDefaultEconomyProfile) {
592 continue;
593 }
594 Section& section = profile.create_section(std::to_string(serials.at(pair.first)).c_str());
595 for (const auto& setting : pair.second.wares) {
596 section.set_natural(tribes.get_ware_descr(setting.first)->name().c_str(), setting.second);
597 }
598 for (const auto& setting : pair.second.workers) {
599 section.set_natural(tribes.get_worker_descr(setting.first)->name().c_str(), setting.second);
600 }
601 }
602
603 Section& section = profile.create_section("undeletable");
604 for (const auto& pair : predefined_targets_) {
605 if (pair.first != kDefaultEconomyProfile) {
606 section.set_bool(std::to_string(serials.at(pair.first)).c_str(), pair.second.undeletable);
607 }
608 }
609
610 g_fs->ensure_directory_exists(kEconomyProfilesDir);
611 std::string complete_filename = kEconomyProfilesDir + g_fs->file_separator() + player_->tribe().name();
612 profile.write(complete_filename.c_str(), false);
613
614 // Inform the windows of other economies of new and deleted profiles
615 Notifications::publish(NoteEconomyProfile(serial_));
616}
617
618void EconomyOptionsWindow::read_targets() {
619 predefined_targets_.clear();
620 const Widelands::Tribes& tribes = player_->egbase().tribes();
621 const Widelands::TribeDescr& tribe = player_->tribe();
622
623 {
624 PredefinedTargets t;
625 t.undeletable = true;
626 for (Widelands::DescriptionIndex di : tribe.wares()) {
627 const Widelands::WareDescr* descr = tribes.get_ware_descr(di);
628 if (descr->has_demand_check(tribe.name())) {
629 t.wares.insert(std::make_pair(di, descr->default_target_quantity(tribe.name())));
630 }
631 }
632 for (Widelands::DescriptionIndex di : tribe.workers()) {
633 const Widelands::WorkerDescr* descr = tribes.get_worker_descr(di);
634 if (descr->has_demand_check()) {
635 t.workers.insert(std::make_pair(di, descr->default_target_quantity()));
636 }
637 }
638 predefined_targets_.insert(std::make_pair(kDefaultEconomyProfile, t));
639 }
640
641 std::string complete_filename = kEconomyProfilesDir + g_fs->file_separator() + player_->tribe().name();
642 Profile profile;
643 profile.read(complete_filename.c_str());
644
645 Section* global_section = profile.get_section(kDefaultEconomyProfile);
646 if (global_section) {
647 std::map<std::string, std::string> serials;
648 while (Section::Value* v = global_section->get_next_val()) {
649 serials.insert(std::make_pair(v->get_name(), v->get_string()));
650 }
651
652 for (const auto& pair : serials) {
653 Section* section = profile.get_section(pair.first);
654 PredefinedTargets t;
655 while (Section::Value* v = section->get_next_val()) {
656 const std::string name(v->get_name());
657 Widelands::DescriptionIndex di = tribes.ware_index(name);
658 if (di == Widelands::INVALID_INDEX) {
659 di = tribes.worker_index(name);
660 assert(di != Widelands::INVALID_INDEX);
661 t.workers.insert(std::make_pair(di, v->get_natural()));
662 } else {
663 t.wares.insert(std::make_pair(di, v->get_natural()));
664 }
665 }
666 predefined_targets_.insert(std::make_pair(pair.second, t));
667 }
668
669 if (Section* section = profile.get_section("undeletable")) {
670 while (Section::Value* v = section->get_next_val()) {
671 predefined_targets_.at(serials.at(std::string(v->get_name()))).undeletable = v->get_bool();
672 }
673 }
674 }
675
676 update_profiles();
220}677}
221678
=== modified file 'src/wui/economy_options_window.h'
--- src/wui/economy_options_window.h 2019-02-23 11:00:49 +0000
+++ src/wui/economy_options_window.h 2019-05-29 06:43:44 +0000
@@ -20,19 +20,61 @@
20#ifndef WL_WUI_ECONOMY_OPTIONS_WINDOW_H20#ifndef WL_WUI_ECONOMY_OPTIONS_WINDOW_H
21#define WL_WUI_ECONOMY_OPTIONS_WINDOW_H21#define WL_WUI_ECONOMY_OPTIONS_WINDOW_H
2222
23#include <map>
23#include <memory>24#include <memory>
25#include <string>
2426
25#include "economy/economy.h"27#include "economy/economy.h"
26#include "logic/map_objects/tribes/tribe_descr.h"28#include "logic/map_objects/tribes/tribe_descr.h"
27#include "notifications/notifications.h"29#include "notifications/notifications.h"
28#include "ui_basic/box.h"30#include "ui_basic/box.h"
31#include "ui_basic/button.h"
32#include "ui_basic/dropdown.h"
33#include "ui_basic/editbox.h"
34#include "ui_basic/table.h"
29#include "ui_basic/tabpanel.h"35#include "ui_basic/tabpanel.h"
30#include "ui_basic/window.h"36#include "ui_basic/window.h"
31#include "wui/waresdisplay.h"37#include "wui/waresdisplay.h"
3238
39const std::string kDefaultEconomyProfile = "Default";
40
41// Used to indicate that a profile has been saved or deleted, so all open windows can update it
42struct NoteEconomyProfile {
43 NoteEconomyProfile(Widelands::Serial s) : serial(s) {
44 }
45 Widelands::Serial serial;
46 CAN_BE_SENT_AS_NOTE(NoteId::EconomyProfile)
47};
48
33struct EconomyOptionsWindow : public UI::Window {49struct EconomyOptionsWindow : public UI::Window {
34 EconomyOptionsWindow(UI::Panel* parent, Widelands::Economy* economy, bool can_act);50 EconomyOptionsWindow(UI::Panel* parent, Widelands::Economy* economy, bool can_act);
35 ~EconomyOptionsWindow();51 ~EconomyOptionsWindow() override;
52
53 struct PredefinedTargets {
54 using Targets = std::map<Widelands::DescriptionIndex, uint32_t>;
55 Targets wares;
56 Targets workers;
57 bool undeletable = false;
58 };
59
60 void create_target();
61 void do_create_target(const std::string&);
62 void save_targets();
63 void read_targets();
64 void update_profiles();
65 std::map<std::string, PredefinedTargets>& get_predefined_targets() {
66 return predefined_targets_;
67 }
68 const PredefinedTargets& get_selected_target() const {
69 return predefined_targets_.at(dropdown_.get_selected());
70 }
71
72 void change_target(int amount);
73 void reset_target();
74
75 void layout() override;
76
77 void close_save_profile_window();
3678
37private:79private:
38 struct TargetWaresDisplay : public AbstractWaresDisplay {80 struct TargetWaresDisplay : public AbstractWaresDisplay {
@@ -59,14 +101,17 @@
59 */101 */
60 struct EconomyOptionsPanel : UI::Box {102 struct EconomyOptionsPanel : UI::Box {
61 EconomyOptionsPanel(UI::Panel* parent,103 EconomyOptionsPanel(UI::Panel* parent,
104 EconomyOptionsWindow* eco_window,
62 Widelands::Serial serial,105 Widelands::Serial serial,
63 Widelands::Player* player,106 Widelands::Player* player,
64 bool can_act,107 bool can_act,
65 Widelands::WareWorker type);108 Widelands::WareWorker type,
109 int32_t min_w);
66110
67 void set_economy(Widelands::Serial serial);111 void set_economy(Widelands::Serial serial);
68 void change_target(int amount);112 void change_target(int amount);
69 void reset_target();113 void reset_target();
114 void update_desired_size() override;
70115
71 private:116 private:
72 Widelands::Serial serial_;117 Widelands::Serial serial_;
@@ -74,17 +119,61 @@
74 Widelands::WareWorker type_;119 Widelands::WareWorker type_;
75 bool can_act_;120 bool can_act_;
76 TargetWaresDisplay display_;121 TargetWaresDisplay display_;
122 EconomyOptionsWindow* economy_options_window_;
77 };123 };
78124
79 /// Actions performed when a NoteEconomyWindow is received.125 /// Actions performed when a NoteEconomyWindow is received.
80 void on_economy_note(const Widelands::NoteEconomy& note);126 void on_economy_note(const Widelands::NoteEconomy& note);
81127
128 UI::Box main_box_;
82 Widelands::Serial serial_;129 Widelands::Serial serial_;
83 Widelands::Player* player_;130 Widelands::Player* player_;
84 UI::TabPanel tabpanel_;131 UI::TabPanel tabpanel_;
85 EconomyOptionsPanel* ware_panel_;132 EconomyOptionsPanel* ware_panel_;
86 EconomyOptionsPanel* worker_panel_;133 EconomyOptionsPanel* worker_panel_;
87 std::unique_ptr<Notifications::Subscriber<Widelands::NoteEconomy>> economynotes_subscriber_;134 std::unique_ptr<Notifications::Subscriber<Widelands::NoteEconomy>> economynotes_subscriber_;
135 std::unique_ptr<Notifications::Subscriber<NoteEconomyProfile>> profilenotes_subscriber_;
136
137 std::map<std::string, PredefinedTargets> predefined_targets_;
138 UI::Box dropdown_box_;
139 UI::Dropdown<std::string> dropdown_;
140
141 std::string applicable_target();
142 std::set<std::string> last_added_to_dropdown_;
143 void think() override;
144 uint32_t time_last_thought_;
145
146 struct SaveProfileWindow : public UI::Window {
147 SaveProfileWindow(UI::Panel* parent, EconomyOptionsWindow* eco);
148 ~SaveProfileWindow() override;
149
150 void update_save_enabled();
151 void table_selection_changed();
152 void update_table();
153 void save();
154 void delete_selected();
155
156 void unset_parent();
157
158 void think() override;
159
160 private:
161 EconomyOptionsWindow* economy_options_;
162 UI::Box main_box_;
163 UI::Box table_box_;
164 UI::Table<const std::string&> table_;
165 UI::Box buttons_box_;
166 UI::EditBox profile_name_;
167 UI::Button save_;
168 UI::Button cancel_;
169 UI::Button delete_;
170 };
171
172 // Helper functions for update_profiles()
173 void update_profiles_needed(const std::string&);
174 void update_profiles_select(const std::string&);
175
176 SaveProfileWindow* save_profile_dialog_;
88};177};
89178
90#endif // end of include guard: WL_WUI_ECONOMY_OPTIONS_WINDOW_H179#endif // end of include guard: WL_WUI_ECONOMY_OPTIONS_WINDOW_H
91180
=== modified file 'src/wui/inputqueuedisplay.cc'
--- src/wui/inputqueuedisplay.cc 2019-05-27 05:37:03 +0000
+++ src/wui/inputqueuedisplay.cc 2019-05-29 06:43:44 +0000
@@ -72,7 +72,7 @@
7272
73 uint32_t priority_button_height = show_only ? 0 : 3 * PriorityButtonSize;73 uint32_t priority_button_height = show_only ? 0 : 3 * PriorityButtonSize;
74 uint32_t image_height =74 uint32_t image_height =
75 show_only ? WARE_MENU_PIC_HEIGHT : std::max<int32_t>(WARE_MENU_PIC_HEIGHT, ph);75 show_only ? kWareMenuPicHeight : std::max<int32_t>(kWareMenuPicHeight, ph);
7676
77 total_height_ = std::max(priority_button_height, image_height) + 2 * Border;77 total_height_ = std::max(priority_button_height, image_height) + 2 * Border;
7878
@@ -92,7 +92,7 @@
92 */92 */
93void InputQueueDisplay::max_size_changed() {93void InputQueueDisplay::max_size_changed() {
94 uint32_t pbs = show_only_ ? 0 : PriorityButtonSize;94 uint32_t pbs = show_only_ ? 0 : PriorityButtonSize;
95 uint32_t ctrl_b_size = show_only_ ? 0 : 2 * WARE_MENU_PIC_WIDTH;95 uint32_t ctrl_b_size = show_only_ ? 0 : 2 * kWareMenuPicWidth;
9696
97 cache_size_ = queue_.get_max_size();97 cache_size_ = queue_.get_max_size();
9898
@@ -141,7 +141,7 @@
141141
142 Vector2i point = Vector2i::zero();142 Vector2i point = Vector2i::zero();
143 point.x = Border + (show_only_ ? 0 : CellWidth + CellSpacing);143 point.x = Border + (show_only_ ? 0 : CellWidth + CellSpacing);
144 point.y = Border + (total_height_ - 2 * Border - WARE_MENU_PIC_HEIGHT) / 2;144 point.y = Border + (total_height_ - 2 * Border - kWareMenuPicHeight) / 2;
145145
146 for (; nr_inputs_to_draw; --nr_inputs_to_draw, point.x += CellWidth + CellSpacing) {146 for (; nr_inputs_to_draw; --nr_inputs_to_draw, point.x += CellWidth + CellSpacing) {
147 dst.blitrect(Vector2i(point.x, point.y), icon_, Recti(0, 0, icon_->width(), icon_->height()),147 dst.blitrect(Vector2i(point.x, point.y), icon_, Recti(0, 0, icon_->width(), icon_->height()),
@@ -241,12 +241,12 @@
241 return;241 return;
242242
243 uint32_t x = Border;243 uint32_t x = Border;
244 uint32_t y = Border + (total_height_ - 2 * Border - WARE_MENU_PIC_WIDTH) / 2;244 uint32_t y = Border + (total_height_ - 2 * Border - kWareMenuPicWidth) / 2;
245245
246 boost::format tooltip_format("<p>%s%s%s</p>");246 boost::format tooltip_format("<p>%s%s%s</p>");
247247
248 decrease_max_fill_ = new UI::Button(248 decrease_max_fill_ = new UI::Button(
249 this, "decrease_max_fill", x, y, WARE_MENU_PIC_WIDTH, WARE_MENU_PIC_HEIGHT,249 this, "decrease_max_fill", x, y, kWareMenuPicWidth, kWareMenuPicHeight,
250 UI::ButtonStyle::kWuiMenu, g_gr->images().get("images/ui_basic/scrollbar_left.png"),250 UI::ButtonStyle::kWuiMenu, g_gr->images().get("images/ui_basic/scrollbar_left.png"),
251 (tooltip_format %251 (tooltip_format %
252 g_gr->styles()252 g_gr->styles()
@@ -272,7 +272,7 @@
272 x = Border + (cache_size_ + 1) * (CellWidth + CellSpacing);272 x = Border + (cache_size_ + 1) * (CellWidth + CellSpacing);
273273
274 increase_max_fill_ = new UI::Button(274 increase_max_fill_ = new UI::Button(
275 this, "increase_max_fill", x, y, WARE_MENU_PIC_WIDTH, WARE_MENU_PIC_HEIGHT,275 this, "increase_max_fill", x, y, kWareMenuPicWidth, kWareMenuPicHeight,
276 UI::ButtonStyle::kWuiMenu, g_gr->images().get("images/ui_basic/scrollbar_right.png"),276 UI::ButtonStyle::kWuiMenu, g_gr->images().get("images/ui_basic/scrollbar_right.png"),
277 (tooltip_format277 (tooltip_format
278278
279279
=== modified file 'src/wui/inputqueuedisplay.h'
--- src/wui/inputqueuedisplay.h 2019-04-23 14:53:35 +0000
+++ src/wui/inputqueuedisplay.h 2019-05-29 06:43:44 +0000
@@ -49,7 +49,7 @@
49 */49 */
50class InputQueueDisplay : public UI::Panel {50class InputQueueDisplay : public UI::Panel {
51public:51public:
52 enum { CellWidth = WARE_MENU_PIC_WIDTH, CellSpacing = 2, Border = 4, PriorityButtonSize = 10 };52 enum { CellWidth = kWareMenuPicWidth, CellSpacing = 2, Border = 4, PriorityButtonSize = 10 };
5353
54 InputQueueDisplay(UI::Panel* parent,54 InputQueueDisplay(UI::Panel* parent,
55 int32_t x,55 int32_t x,
5656
=== modified file 'src/wui/ware_statistics_menu.cc'
--- src/wui/ware_statistics_menu.cc 2019-03-26 22:15:39 +0000
+++ src/wui/ware_statistics_menu.cc 2019-05-29 06:43:44 +0000
@@ -22,6 +22,7 @@
22#include "base/i18n.h"22#include "base/i18n.h"
23#include "graphic/graphic.h"23#include "graphic/graphic.h"
24#include "graphic/rendertarget.h"24#include "graphic/rendertarget.h"
25#include "graphic/text_layout.h"
25#include "logic/map_objects/tribes/tribe_descr.h"26#include "logic/map_objects/tribes/tribe_descr.h"
26#include "logic/map_objects/tribes/warelist.h"27#include "logic/map_objects/tribes/warelist.h"
27#include "logic/player.h"28#include "logic/player.h"
@@ -101,7 +102,11 @@
101 &registry,102 &registry,
102 kPlotWidth + 2 * kSpacing,103 kPlotWidth + 2 * kSpacing,
103 270,104 270,
104 _("Ware Statistics")) {105 _("Ware Statistics")),
106 main_box_(nullptr),
107 tab_panel_(nullptr),
108 display_(nullptr),
109 slider_(nullptr) {
105 uint8_t const nr_wares = parent.get_player()->egbase().tribes().nrwares();110 uint8_t const nr_wares = parent.get_player()->egbase().tribes().nrwares();
106111
107 // Init color sets112 // Init color sets
@@ -111,45 +116,45 @@
111 std::fill(active_colors_.begin(), active_colors_.end(), 0);116 std::fill(active_colors_.begin(), active_colors_.end(), 0);
112117
113 // First, we must decide about the size.118 // First, we must decide about the size.
114 UI::Box* box = new UI::Box(this, 0, 0, UI::Box::Vertical, 0, 0, 5);119 main_box_ = new UI::Box(this, 0, 0, UI::Box::Vertical, 0, 0, 5);
115 box->set_border(kSpacing, kSpacing, kSpacing, kSpacing);120 main_box_->set_border(kSpacing, kSpacing, kSpacing, kSpacing);
116 set_center_panel(box);121 set_center_panel(main_box_);
117122
118 // Setup plot widgets123 // Setup plot widgets
119 // Create a tabbed environment for the different plots124 // Create a tabbed environment for the different plots
120 UI::TabPanel* tabs = new UI::TabPanel(box, UI::TabPanelStyle::kWuiDark);125 tab_panel_ = new UI::TabPanel(main_box_, UI::TabPanelStyle::kWuiDark);
121126
122 plot_production_ =127 plot_production_ =
123 new WuiPlotArea(tabs, 0, 0, kPlotWidth, kPlotHeight + kSpacing,128 new WuiPlotArea(tab_panel_, 0, 0, kPlotWidth, kPlotHeight + kSpacing,
124 Widelands::kStatisticsSampleTime, WuiPlotArea::Plotmode::kRelative);129 Widelands::kStatisticsSampleTime, WuiPlotArea::Plotmode::kRelative);
125130
126 tabs->add(131 tab_panel_->add(
127 "production", g_gr->images().get(pic_tab_production), plot_production_, _("Production"));132 "production", g_gr->images().get(pic_tab_production), plot_production_, _("Production"));
128133
129 plot_consumption_ =134 plot_consumption_ =
130 new WuiPlotArea(tabs, 0, 0, kPlotWidth, kPlotHeight + kSpacing,135 new WuiPlotArea(tab_panel_, 0, 0, kPlotWidth, kPlotHeight + kSpacing,
131 Widelands::kStatisticsSampleTime, WuiPlotArea::Plotmode::kRelative);136 Widelands::kStatisticsSampleTime, WuiPlotArea::Plotmode::kRelative);
132137
133 tabs->add(138 tab_panel_->add(
134 "consumption", g_gr->images().get(pic_tab_consumption), plot_consumption_, _("Consumption"));139 "consumption", g_gr->images().get(pic_tab_consumption), plot_consumption_, _("Consumption"));
135140
136 plot_economy_ =141 plot_economy_ =
137 new DifferentialPlotArea(tabs, 0, 0, kPlotWidth, kPlotHeight + kSpacing,142 new DifferentialPlotArea(tab_panel_, 0, 0, kPlotWidth, kPlotHeight + kSpacing,
138 Widelands::kStatisticsSampleTime, WuiPlotArea::Plotmode::kRelative);143 Widelands::kStatisticsSampleTime, WuiPlotArea::Plotmode::kRelative);
139144
140 tabs->add(145 tab_panel_->add(
141 "economy_health", g_gr->images().get(pic_tab_economy), plot_economy_, _("Economy health"));146 "economy_health", g_gr->images().get(pic_tab_economy), plot_economy_, _("Economy health"));
142147
143 plot_stock_ =148 plot_stock_ =
144 new WuiPlotArea(tabs, 0, 0, kPlotWidth, kPlotHeight + kSpacing,149 new WuiPlotArea(tab_panel_, 0, 0, kPlotWidth, kPlotHeight + kSpacing,
145 Widelands::kStatisticsSampleTime, WuiPlotArea::Plotmode::kAbsolute);150 Widelands::kStatisticsSampleTime, WuiPlotArea::Plotmode::kAbsolute);
146151
147 tabs->add("stock", g_gr->images().get(pic_tab_stock), plot_stock_, _("Stock"));152 tab_panel_->add("stock", g_gr->images().get(pic_tab_stock), plot_stock_, _("Stock"));
148153
149 tabs->activate(0);154 tab_panel_->activate(0);
150155
151 // Add tabbed environment to box156 // Add tabbed environment to box
152 box->add(tabs, UI::Box::Resizing::kFullSize);157 main_box_->add(tab_panel_, UI::Box::Resizing::kFullSize);
153158
154 // Register statistics data159 // Register statistics data
155 for (Widelands::DescriptionIndex cur_ware = 0; cur_ware < nr_wares; ++cur_ware) {160 for (Widelands::DescriptionIndex cur_ware = 0; cur_ware < nr_wares; ++cur_ware) {
@@ -178,15 +183,16 @@
178 colors[kInactiveColorIndex]);183 colors[kInactiveColorIndex]);
179 }184 }
180185
181 box->add(new StatisticWaresDisplay(186 display_ = new StatisticWaresDisplay(
182 box, 0, 0, parent.get_player()->tribe(),187 main_box_, 0, 0, parent.get_player()->tribe(),
183 [this](const int ware_index, const bool what) { cb_changed_to(ware_index, what); },188 [this](const int ware_index, const bool what) { cb_changed_to(ware_index, what); },
184 color_map_),189 color_map_);
185 UI::Box::Resizing::kFullSize);190 display_->set_min_free_vertical_space(400);
191 main_box_->add(display_, UI::Box::Resizing::kFullSize);
186192
187 WuiPlotAreaSlider* slider = new WuiPlotAreaSlider(this, *plot_production_, 0, 0, kPlotWidth, 45);193 slider_ = new WuiPlotAreaSlider(this, *plot_production_, 0, 0, kPlotWidth, 45);
188 slider->changedto.connect([this](const int32_t timescale) { set_time(timescale); });194 slider_->changedto.connect([this](const int32_t timescale) { set_time(timescale); });
189 box->add(slider, UI::Box::Resizing::kFullSize);195 main_box_->add(slider_, UI::Box::Resizing::kFullSize);
190}196}
191197
192/**198/**
@@ -228,6 +234,27 @@
228 plot_stock_->show_plot(static_cast<size_t>(id), what);234 plot_stock_->show_plot(static_cast<size_t>(id), what);
229}235}
230236
237static bool layouting = false;
238void WareStatisticsMenu::layout() {
239 if (layouting || !tab_panel_ || !display_ || !slider_ || !main_box_) {
240 return;
241 }
242 layouting = true;
243
244 display_->set_hgap(3, false);
245 int w1, h1, w2, h2, w3, h3;
246 tab_panel_->get_desired_size(&w1, &h1);
247 display_->get_desired_size(&w2, &h2);
248 slider_->get_desired_size(&w3, &h3);
249
250 display_->set_hgap(std::max(3, AbstractWaresDisplay::calc_hgap(display_->get_extent().w, kPlotWidth)), false);
251 display_->get_desired_size(&w2, &h2);
252
253 main_box_->set_desired_size(std::max(w2, kPlotWidth) + 2 * kSpacing, h1 + h2 + h3 + text_height(UI::FontStyle::kLabel));
254 UI::UniqueWindow::layout();
255 layouting = false;
256}
257
231/**258/**
232 * Callback for the time buttons. Change the time axis of all ware259 * Callback for the time buttons. Change the time axis of all ware
233 * statistics simultaneously.260 * statistics simultaneously.
234261
=== modified file 'src/wui/ware_statistics_menu.h'
--- src/wui/ware_statistics_menu.h 2019-02-23 11:00:49 +0000
+++ src/wui/ware_statistics_menu.h 2019-05-29 06:43:44 +0000
@@ -29,13 +29,27 @@
29struct DifferentialPlotArea;29struct DifferentialPlotArea;
30class InteractivePlayer;30class InteractivePlayer;
31struct WuiPlotArea;31struct WuiPlotArea;
32struct StatisticWaresDisplay;
33struct WuiPlotAreaSlider;
34namespace UI {
35 struct Box;
36 struct TabPanel;
37}
3238
33struct WareStatisticsMenu : public UI::UniqueWindow {39struct WareStatisticsMenu : public UI::UniqueWindow {
34public:40public:
35 WareStatisticsMenu(InteractivePlayer&, UI::UniqueWindow::Registry&);41 WareStatisticsMenu(InteractivePlayer&, UI::UniqueWindow::Registry&);
36 void set_time(int32_t);42 void set_time(int32_t);
3743
44protected:
45 void layout() override;
46
38private:47private:
48 UI::Box* main_box_;
49 UI::TabPanel* tab_panel_;
50 StatisticWaresDisplay* display_;
51 WuiPlotAreaSlider* slider_;
52
39 WuiPlotArea* plot_production_;53 WuiPlotArea* plot_production_;
40 WuiPlotArea* plot_consumption_;54 WuiPlotArea* plot_consumption_;
41 WuiPlotArea* plot_stock_;55 WuiPlotArea* plot_stock_;
4256
=== modified file 'src/wui/waresdisplay.cc'
--- src/wui/waresdisplay.cc 2019-05-26 17:21:15 +0000
+++ src/wui/waresdisplay.cc 2019-05-29 06:43:44 +0000
@@ -35,8 +35,9 @@
35#include "logic/map_objects/tribes/ware_descr.h"35#include "logic/map_objects/tribes/ware_descr.h"
36#include "logic/map_objects/tribes/worker.h"36#include "logic/map_objects/tribes/worker.h"
37#include "logic/player.h"37#include "logic/player.h"
38#include "ui_basic/window.h"
3839
39const int WARE_MENU_INFO_SIZE = 12;40constexpr int kWareMenuInfoSize = 12;
4041
41AbstractWaresDisplay::AbstractWaresDisplay(42AbstractWaresDisplay::AbstractWaresDisplay(
42 UI::Panel* const parent,43 UI::Panel* const parent,
@@ -46,7 +47,9 @@
46 Widelands::WareWorker type,47 Widelands::WareWorker type,
47 bool selectable,48 bool selectable,
48 boost::function<void(Widelands::DescriptionIndex, bool)> callback_function,49 boost::function<void(Widelands::DescriptionIndex, bool)> callback_function,
49 bool horizontal)50 bool horizontal,
51 int32_t hgap,
52 int32_t vgap)
50 : // Size is set when add_warelist is called, as it depends on the type_.53 : // Size is set when add_warelist is called, as it depends on the type_.
51 UI::Panel(parent, x, y, 0, 0),54 UI::Panel(parent, x, y, 0, 0),
52 tribe_(tribe),55 tribe_(tribe),
@@ -57,8 +60,11 @@
5760
58 selectable_(selectable),61 selectable_(selectable),
59 horizontal_(horizontal),62 horizontal_(horizontal),
63 hgap_(hgap),
64 vgap_(vgap),
60 selection_anchor_(Widelands::INVALID_INDEX),65 selection_anchor_(Widelands::INVALID_INDEX),
61 callback_function_(callback_function) {66 callback_function_(callback_function),
67 min_free_vertical_space_(290) {
62 for (const Widelands::DescriptionIndex& index : indices_) {68 for (const Widelands::DescriptionIndex& index : indices_) {
63 selected_.insert(std::make_pair(index, false));69 selected_.insert(std::make_pair(index, false));
64 hidden_.insert(std::make_pair(index, false));70 hidden_.insert(std::make_pair(index, false));
@@ -67,22 +73,65 @@
6773
68 curware_.set_text(_("Stock"));74 curware_.set_text(_("Stock"));
6975
70 // Find out geometry from icons_order76 graphic_resolution_changed_subscriber_ = Notifications::subscribe<GraphicResolutionChanged>(
71 unsigned int columns = icons_order().size();77 [this](const GraphicResolutionChanged&) {
72 unsigned int rows = 0;78 recalc_desired_size(true);
73 for (unsigned int i = 0; i < icons_order().size(); i++)79 });
74 if (icons_order()[i].size() > rows)80
75 rows = icons_order()[i].size();81 recalc_desired_size(false);
82}
83
84Widelands::Extent AbstractWaresDisplay::get_extent() const {
85 int16_t columns = 0;
86 int16_t rows = 0;
87 for (const auto& pair : icons_order_coords()) {
88 columns = std::max(columns, pair.second.x);
89 rows = std::max(rows, pair.second.y);
90 }
91 // We cound from 0 up
92 ++columns;
93 ++rows;
94
76 if (horizontal_) {95 if (horizontal_) {
77 unsigned int s = columns;96 const int16_t s = columns;
78 columns = rows;97 columns = rows;
79 rows = s;98 rows = s;
80 }99 }
100 return Widelands::Extent(columns, rows);
101}
102
103void AbstractWaresDisplay::set_hgap(int32_t gap, bool relayout) {
104 hgap_ = gap;
105 recalc_desired_size(relayout);
106}
107
108void AbstractWaresDisplay::set_vgap(int32_t gap, bool relayout) {
109 vgap_ = gap;
110 recalc_desired_size(relayout);
111}
112
113void AbstractWaresDisplay::recalc_desired_size(bool relayout) {
114 relayout_icons_order_coords();
115
116 // Find out geometry from icons_order
117 const Widelands::Extent size = get_extent();
81118
82 // 25 is height of curware_ text119 // 25 is height of curware_ text
83 set_desired_size(120 set_desired_size(
84 columns * (WARE_MENU_PIC_WIDTH + WARE_MENU_PIC_PAD_X) + 1,121 size.w * (kWareMenuPicWidth + hgap_) - hgap_ + 5,
85 rows * (WARE_MENU_PIC_HEIGHT + WARE_MENU_INFO_SIZE + WARE_MENU_PIC_PAD_Y) + 1 + 25);122 size.h * (kWareMenuPicHeight + kWareMenuInfoSize + vgap_) - vgap_ + 1 + 25);
123
124 if (relayout) {
125 // Since we are usually stacked deep within other panels, we need to tell our highest parent window to relayout
126 UI::Panel* p = this;
127 while (p->get_parent()) {
128 p = p->get_parent();
129 if (dynamic_cast<UI::Window*>(p)) {
130 p->layout();
131 return;
132 }
133 }
134 }
86}135}
87136
88bool AbstractWaresDisplay::handle_mousemove(uint8_t state, int32_t x, int32_t y, int32_t, int32_t) {137bool AbstractWaresDisplay::handle_mousemove(uint8_t state, int32_t x, int32_t y, int32_t, int32_t) {
@@ -162,25 +211,56 @@
162 * DescriptionIndex::null() if the given point is outside the range.211 * DescriptionIndex::null() if the given point is outside the range.
163 */212 */
164Widelands::DescriptionIndex AbstractWaresDisplay::ware_at_point(int32_t x, int32_t y) const {213Widelands::DescriptionIndex AbstractWaresDisplay::ware_at_point(int32_t x, int32_t y) const {
165 if (x < 0 || y < 0)214 // Graphical offset
166 return Widelands::INVALID_INDEX;215 x -= 2;
167216 y -= 2;
168 unsigned int i = x / (WARE_MENU_PIC_WIDTH + WARE_MENU_PIC_PAD_X);217
169 unsigned int j = y / (WARE_MENU_PIC_HEIGHT + WARE_MENU_INFO_SIZE + WARE_MENU_PIC_PAD_Y);218 if (x < 0 || y < 0) {
219 return Widelands::INVALID_INDEX;
220 }
221
222 int i = x / (kWareMenuPicWidth + hgap_);
223 int j = y / (kWareMenuPicHeight + kWareMenuInfoSize + vgap_);
224 if (kWareMenuPicWidth * (i + 1) + hgap_ * i < x ||
225 (kWareMenuPicHeight + kWareMenuInfoSize) * (j + 1) + vgap_ * j < y) {
226 // Not on the ware, but on the space between
227 return Widelands::INVALID_INDEX;
228 }
170 if (horizontal_) {229 if (horizontal_) {
171 unsigned int s = i;230 int s = i;
172 i = j;231 i = j;
173 j = s;232 j = s;
174 }233 }
175 if (i < icons_order().size() && j < icons_order()[i].size()) {234 for (const auto& pair : icons_order_coords()) {
176 const Widelands::DescriptionIndex& ware = icons_order()[i][j];235 if (pair.second.x == i && pair.second.y == j) {
177 assert(hidden_.count(ware) == 1);236 assert(hidden_.count(pair.first) == 1);
178 if (!(hidden_.find(ware)->second)) {237 if (!(hidden_.find(pair.first)->second)) {
179 return ware;238 return pair.first;
180 }239 }
181 }240 break;
182241 }
183 return Widelands::INVALID_INDEX;242 }
243
244 return Widelands::INVALID_INDEX;
245}
246
247Widelands::DescriptionIndex AbstractWaresDisplay::ware_at_coords(int16_t x, int16_t y) const {
248 for (const auto& pair : icons_order_coords()) {
249 if (pair.second.x == x && pair.second.y == y) {
250 return pair.first;
251 }
252 }
253 return Widelands::INVALID_INDEX;
254}
255
256uint16_t AbstractWaresDisplay::column_length(int16_t x) const {
257 uint16_t l = 0;
258 for (const auto& pair : icons_order_coords()) {
259 if (pair.second.x == x) {
260 l = std::max(l, static_cast<uint16_t>(pair.second.y + 1));
261 }
262 }
263 return l;
184}264}
185265
186// Update the anchored selection. An anchor has been created by mouse266// Update the anchored selection. An anchor has been created by mouse
@@ -199,15 +279,13 @@
199 Vector2i anchor_pos = ware_position(selection_anchor_);279 Vector2i anchor_pos = ware_position(selection_anchor_);
200 // Add an offset to make sure the anchor line and column will be280 // Add an offset to make sure the anchor line and column will be
201 // selected when selecting in topleft direction281 // selected when selecting in topleft direction
202 int32_t anchor_x = anchor_pos.x + WARE_MENU_PIC_WIDTH / 2;282 int32_t anchor_x = anchor_pos.x + kWareMenuPicWidth / 2;
203 int32_t anchor_y = anchor_pos.y + WARE_MENU_PIC_HEIGHT / 2;283 int32_t anchor_y = anchor_pos.y + kWareMenuPicHeight / 2;
204284
205 unsigned int left_ware_idx = anchor_x / (WARE_MENU_PIC_WIDTH + WARE_MENU_PIC_PAD_X);285 unsigned int left_ware_idx = anchor_x / (kWareMenuPicWidth + hgap_);
206 unsigned int top_ware_idx =286 unsigned int top_ware_idx = anchor_y / (kWareMenuPicHeight + kWareMenuInfoSize + vgap_);
207 anchor_y / (WARE_MENU_PIC_HEIGHT + WARE_MENU_INFO_SIZE + WARE_MENU_PIC_PAD_Y);287 unsigned int right_ware_idx = x / (kWareMenuPicWidth + hgap_);
208 unsigned int right_ware_idx = x / (WARE_MENU_PIC_WIDTH + WARE_MENU_PIC_PAD_X);288 unsigned int bottoware_idx_ = y / (kWareMenuPicHeight + kWareMenuInfoSize + vgap_);
209 unsigned int bottoware_idx_ =
210 y / (WARE_MENU_PIC_HEIGHT + WARE_MENU_INFO_SIZE + WARE_MENU_PIC_PAD_Y);
211 unsigned int tmp;289 unsigned int tmp;
212290
213 // Reverse col/row and anchor/endpoint if needed291 // Reverse col/row and anchor/endpoint if needed
@@ -231,10 +309,10 @@
231 }309 }
232310
233 for (unsigned int cur_ware_x = left_ware_idx; cur_ware_x <= right_ware_idx; cur_ware_x++) {311 for (unsigned int cur_ware_x = left_ware_idx; cur_ware_x <= right_ware_idx; cur_ware_x++) {
234 if (cur_ware_x < icons_order().size()) {312 if (cur_ware_x < icons_order_coords().size()) {
235 for (unsigned cur_ware_y = top_ware_idx; cur_ware_y <= bottoware_idx_; cur_ware_y++) {313 for (unsigned cur_ware_y = top_ware_idx; cur_ware_y <= bottoware_idx_; cur_ware_y++) {
236 if (cur_ware_y < icons_order()[cur_ware_x].size()) {314 if (cur_ware_y < static_cast<unsigned>(column_length(cur_ware_x))) {
237 Widelands::DescriptionIndex ware = icons_order()[cur_ware_x][cur_ware_y];315 Widelands::DescriptionIndex ware = ware_at_coords(cur_ware_x, cur_ware_y);
238 if (!hidden_[ware]) {316 if (!hidden_[ware]) {
239 in_selection_[ware] = true;317 in_selection_[ware] = true;
240 }318 }
@@ -271,26 +349,44 @@
271 NEVER_HERE();349 NEVER_HERE();
272}350}
273351
274const Widelands::TribeDescr::WaresOrderCoords& AbstractWaresDisplay::icons_order_coords() const {352const WaresOrderCoords& AbstractWaresDisplay::icons_order_coords() const {
275 switch (type_) {353 assert(!order_coords_.empty());
276 case Widelands::wwWARE:354 return order_coords_;
277 return tribe_.wares_order_coords();355}
278 case Widelands::wwWORKER:356
279 return tribe_.workers_order_coords();357void AbstractWaresDisplay::relayout_icons_order_coords() {
358 order_coords_.clear();
359 const int column_number = icons_order().size();
360 const int column_max_size = std::max(1, (g_gr->get_yres() - min_free_vertical_space_) /
361 (kWareMenuPicHeight + vgap_ + kWareMenuInfoSize));
362
363 int16_t column_index_to_apply = 0;
364 for (int16_t column_index = 0; column_index < column_number; ++column_index) {
365 const std::vector<Widelands::DescriptionIndex>& column = icons_order().at(column_index);
366 const int row_number = column.size();
367 int16_t row_index_to_apply = 0;
368 for (int16_t row_index = 0; row_index < row_number; ++row_index) {
369 order_coords_.emplace(column.at(row_index), Widelands::Coords(column_index_to_apply, row_index_to_apply));
370 ++row_index_to_apply;
371 if (row_index_to_apply >= column_max_size) {
372 row_index_to_apply = 0;
373 ++column_index_to_apply;
374 }
375 }
376 if (row_index_to_apply > 0) {
377 ++column_index_to_apply;
378 }
280 }379 }
281 NEVER_HERE();
282}380}
283381
284Vector2i AbstractWaresDisplay::ware_position(Widelands::DescriptionIndex id) const {382Vector2i AbstractWaresDisplay::ware_position(Widelands::DescriptionIndex id) const {
285 Vector2i p(2, 2);383 Vector2i p(2, 2);
286 if (horizontal_) {384 if (horizontal_) {
287 p.x += icons_order_coords()[id].second * (WARE_MENU_PIC_WIDTH + WARE_MENU_PIC_PAD_X);385 p.x += icons_order_coords().at(id).y * (kWareMenuPicWidth + hgap_);
288 p.y += icons_order_coords()[id].first *386 p.y += icons_order_coords().at(id).x * (kWareMenuPicHeight + vgap_ + kWareMenuInfoSize);
289 (WARE_MENU_PIC_HEIGHT + WARE_MENU_PIC_PAD_Y + WARE_MENU_INFO_SIZE);
290 } else {387 } else {
291 p.x += icons_order_coords()[id].first * (WARE_MENU_PIC_WIDTH + WARE_MENU_PIC_PAD_X);388 p.x += icons_order_coords().at(id).x * (kWareMenuPicWidth + hgap_);
292 p.y += icons_order_coords()[id].second *389 p.y += icons_order_coords().at(id).y * (kWareMenuPicHeight + vgap_ + kWareMenuInfoSize);
293 (WARE_MENU_PIC_HEIGHT + WARE_MENU_PIC_PAD_Y + WARE_MENU_INFO_SIZE);
294 }390 }
295 return p;391 return p;
296}392}
@@ -328,15 +424,15 @@
328 const Image* icon = type_ == Widelands::wwWORKER ? tribe_.get_worker_descr(id)->icon() :424 const Image* icon = type_ == Widelands::wwWORKER ? tribe_.get_worker_descr(id)->icon() :
329 tribe_.get_ware_descr(id)->icon();425 tribe_.get_ware_descr(id)->icon();
330426
331 dst.blit(p + Vector2i((w - WARE_MENU_PIC_WIDTH) / 2, 1), icon);427 dst.blit(p + Vector2i((w - kWareMenuPicWidth) / 2, 1), icon);
332428
333 dst.fill_rect(Recti(p + Vector2i(0, WARE_MENU_PIC_HEIGHT), w, WARE_MENU_INFO_SIZE),429 dst.fill_rect(Recti(p + Vector2i(0, kWareMenuPicHeight), w, kWareMenuInfoSize),
334 info_color_for_ware(id));430 info_color_for_ware(id));
335431
336 std::shared_ptr<const UI::RenderedText> rendered_text =432 std::shared_ptr<const UI::RenderedText> rendered_text =
337 UI::g_fh->render(as_richtext_paragraph(info_for_ware(id), style.info_font()));433 UI::g_fh->render(as_richtext_paragraph(info_for_ware(id), style.info_font()));
338 rendered_text->draw(dst, Vector2i(p.x + w - rendered_text->width() - 1,434 rendered_text->draw(dst, Vector2i(p.x + w - rendered_text->width() - 1,
339 p.y + WARE_MENU_PIC_HEIGHT + WARE_MENU_INFO_SIZE + 1 -435 p.y + kWareMenuPicHeight + kWareMenuInfoSize + 1 -
340 rendered_text->height()));436 rendered_text->height()));
341}437}
342438
@@ -370,6 +466,10 @@
370 hidden_[ware] = true;466 hidden_[ware] = true;
371}467}
372468
469bool AbstractWaresDisplay::is_ware_hidden(Widelands::DescriptionIndex ware) const {
470 return hidden_.at(ware);
471}
472
373WaresDisplay::WaresDisplay(UI::Panel* const parent,473WaresDisplay::WaresDisplay(UI::Panel* const parent,
374 int32_t x,474 int32_t x,
375 int32_t y,475 int32_t y,
376476
=== modified file 'src/wui/waresdisplay.h'
--- src/wui/waresdisplay.h 2019-02-23 11:00:49 +0000
+++ src/wui/waresdisplay.h 2019-05-29 06:43:44 +0000
@@ -20,6 +20,7 @@
20#ifndef WL_WUI_WARESDISPLAY_H20#ifndef WL_WUI_WARESDISPLAY_H
21#define WL_WUI_WARESDISPLAY_H21#define WL_WUI_WARESDISPLAY_H
2222
23#include <memory>
23#include <vector>24#include <vector>
2425
25#include "logic/map_objects/tribes/tribe_descr.h"26#include "logic/map_objects/tribes/tribe_descr.h"
@@ -36,6 +37,8 @@
36struct WareList;37struct WareList;
37} // namespace Widelands38} // namespace Widelands
3839
40using WaresOrderCoords = std::map<Widelands::DescriptionIndex, Widelands::Coords>;
41
39/**42/**
40 * Display wares or workers together with some string (typically a number)43 * Display wares or workers together with some string (typically a number)
41 * in the style of the @ref WarehouseWindow.44 * in the style of the @ref WarehouseWindow.
@@ -54,7 +57,9 @@
54 CLANG_DIAG_OFF("-Wunknown-pragmas") CLANG_DIAG_OFF("-Wzero-as-null-pointer-constant")57 CLANG_DIAG_OFF("-Wunknown-pragmas") CLANG_DIAG_OFF("-Wzero-as-null-pointer-constant")
55 boost::function<void(Widelands::DescriptionIndex, bool)> callback_function = 0,58 boost::function<void(Widelands::DescriptionIndex, bool)> callback_function = 0,
56 CLANG_DIAG_ON("-Wzero-as-null-pointer-constant")59 CLANG_DIAG_ON("-Wzero-as-null-pointer-constant")
57 CLANG_DIAG_ON("-Wunknown-pragmas") bool horizontal = false);60 CLANG_DIAG_ON("-Wunknown-pragmas") bool horizontal = false,
61 int32_t hgap = 3,
62 int32_t vgap = 4);
5863
59 bool64 bool
60 handle_mousemove(uint8_t state, int32_t x, int32_t y, int32_t xdiff, int32_t ydiff) override;65 handle_mousemove(uint8_t state, int32_t x, int32_t y, int32_t xdiff, int32_t ydiff) override;
@@ -68,12 +73,39 @@
6873
69 // Wares may be hidden74 // Wares may be hidden
70 void hide_ware(Widelands::DescriptionIndex);75 void hide_ware(Widelands::DescriptionIndex);
76 bool is_ware_hidden(Widelands::DescriptionIndex) const;
7177
72 Widelands::DescriptionIndex ware_at_point(int32_t x, int32_t y) const;78 Widelands::DescriptionIndex ware_at_point(int32_t x, int32_t y) const;
73 Widelands::WareWorker get_type() const {79 Widelands::WareWorker get_type() const {
74 return type_;80 return type_;
75 }81 }
7682
83 int32_t get_hgap() {
84 return hgap_;
85 }
86 int32_t get_vgap() {
87 return vgap_;
88 }
89 void set_hgap(int32_t, bool = true);
90 void set_vgap(int32_t, bool = true);
91
92 Widelands::Extent get_extent() const;
93
94 const WaresOrderCoords& icons_order_coords() const;
95 Widelands::DescriptionIndex ware_at_coords(int16_t x, int16_t y) const;
96 uint16_t column_length(int16_t) const;
97
98 void set_min_free_vertical_space(int32_t s) {
99 min_free_vertical_space_ = s;
100 }
101 int32_t get_min_free_vertical_space() const {
102 return min_free_vertical_space_;
103 }
104
105 static inline int32_t calc_hgap(int32_t columns, int32_t total_w, int32_t min = 3) {
106 return std::max(min, (total_w - columns * kWareMenuPicWidth) / (columns - 1));
107 }
108
77protected:109protected:
78 void layout() override;110 void layout() override;
79111
@@ -82,7 +114,6 @@
82 virtual RGBColor info_color_for_ware(Widelands::DescriptionIndex);114 virtual RGBColor info_color_for_ware(Widelands::DescriptionIndex);
83115
84 const Widelands::TribeDescr::WaresOrder& icons_order() const;116 const Widelands::TribeDescr::WaresOrder& icons_order() const;
85 const Widelands::TribeDescr::WaresOrderCoords& icons_order_coords() const;
86 virtual Vector2i ware_position(Widelands::DescriptionIndex) const;117 virtual Vector2i ware_position(Widelands::DescriptionIndex) const;
87 void draw(RenderTarget&) override;118 void draw(RenderTarget&) override;
88 virtual void draw_ware(RenderTarget&, Widelands::DescriptionIndex);119 virtual void draw_ware(RenderTarget&, Widelands::DescriptionIndex);
@@ -110,6 +141,13 @@
110 WareListSelectionType in_selection_; // Wares in temporary anchored selection141 WareListSelectionType in_selection_; // Wares in temporary anchored selection
111 bool selectable_;142 bool selectable_;
112 bool horizontal_;143 bool horizontal_;
144 int32_t hgap_;
145 int32_t vgap_;
146
147 WaresOrderCoords order_coords_;
148
149 void relayout_icons_order_coords();
150 void recalc_desired_size(bool);
113151
114 /**152 /**
115 * The ware on which the mouse press has been performed.153 * The ware on which the mouse press has been performed.
@@ -117,6 +155,9 @@
117 */155 */
118 Widelands::DescriptionIndex selection_anchor_;156 Widelands::DescriptionIndex selection_anchor_;
119 boost::function<void(Widelands::DescriptionIndex, bool)> callback_function_;157 boost::function<void(Widelands::DescriptionIndex, bool)> callback_function_;
158
159 std::unique_ptr<Notifications::Subscriber<GraphicResolutionChanged>> graphic_resolution_changed_subscriber_;
160 int32_t min_free_vertical_space_;
120};161};
121162
122/*163/*

Subscribers

People subscribed via source and target branches

to status/vote changes: