widelands-dev team mailing list archive
-
widelands-dev team
-
Mailing list archive
-
Message #08401
[Merge] lp:~widelands-dev/widelands/bug-536489-dropdown into lp:widelands
GunChleoc has proposed merging lp:~widelands-dev/widelands/bug-536489-dropdown into lp:widelands.
Commit message:
Implemented a textual dropdown menu. It is used in:
- Options: Screen resolution
- Options: Language. Got rid of the extra Language tab and moved it to the Interface tab.
- Launch Single Player Game: Win Condition
Requested reviews:
Klaus Halfmann (klaus-halfmann): compile, test
Related bugs:
Bug #536489 in widelands: "Dropdown button"
https://bugs.launchpad.net/widelands/+bug/536489
For more details, see:
https://code.launchpad.net/~widelands-dev/widelands/bug-536489-dropdown/+merge/306303
My first branch for Build 20 ;)
Implemented a textual dropdown menu. This is already quite a bit of code, so I'll do the win condition for multiplayer in a separate branch. We will also want pictorial dropdowns.
--
Your team Widelands Developers is subscribed to branch lp:~widelands-dev/widelands/bug-536489-dropdown.
=== modified file 'src/ui_basic/CMakeLists.txt'
--- src/ui_basic/CMakeLists.txt 2016-04-02 16:45:53 +0000
+++ src/ui_basic/CMakeLists.txt 2016-09-27 08:44:17 +0000
@@ -6,6 +6,8 @@
button.h
checkbox.cc
checkbox.h
+ dropdown.cc
+ dropdown.h
editbox.cc
editbox.h
fileview_panel.cc
=== modified file 'src/ui_basic/button.cc'
--- src/ui_basic/button.cc 2016-08-04 15:49:05 +0000
+++ src/ui_basic/button.cc 2016-09-27 08:44:17 +0000
@@ -68,6 +68,7 @@
set_size(w, new_height);
}
set_thinks(false);
+ set_can_focus(true);
}
Button::Button // for pictorial buttons
@@ -97,6 +98,7 @@
pic_custom_(fg_pic),
clr_down_(229, 161, 2) {
set_thinks(false);
+ set_can_focus(true);
}
Button::~Button() {
@@ -133,6 +135,8 @@
if (enabled_ == on)
return;
+ set_can_focus(on);
+
// disabled buttons should look different...
if (on)
enabled_ = true;
@@ -306,6 +310,7 @@
return false;
if (enabled_) {
+ focus();
grab_mouse(true);
pressed_ = true;
if (repeating_) {
=== modified file 'src/ui_basic/checkbox.cc'
--- src/ui_basic/checkbox.cc 2016-08-04 15:49:05 +0000
+++ src/ui_basic/checkbox.cc 2016-09-27 08:44:17 +0000
@@ -43,7 +43,7 @@
uint16_t h = pic->height();
set_desired_size(w, h);
set_size(w, h);
-
+ set_can_focus(true);
set_flags(Has_Custom_Picture, true);
pic_graphics_ = pic;
}
@@ -79,6 +79,7 @@
* Args: enabled true if the checkbox should be enabled, false otherwise
*/
void Statebox::set_enabled(bool const enabled) {
+ set_can_focus(enabled);
if (((flags_ & Is_Enabled) > 1) && enabled)
return;
@@ -157,6 +158,7 @@
*/
bool Statebox::handle_mousepress(const uint8_t btn, int32_t, int32_t) {
if (btn == SDL_BUTTON_LEFT && (flags_ & Is_Enabled)) {
+ focus();
clicked();
return true;
}
=== added file 'src/ui_basic/dropdown.cc'
--- src/ui_basic/dropdown.cc 1970-01-01 00:00:00 +0000
+++ src/ui_basic/dropdown.cc 2016-09-27 08:44:17 +0000
@@ -0,0 +1,208 @@
+/*
+ * Copyright (C) 2016 by the Widelands Development Team
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#include "ui_basic/dropdown.h"
+
+#include <algorithm>
+
+#include <boost/format.hpp>
+
+#include "base/i18n.h"
+#include "graphic/align.h"
+#include "graphic/font_handler1.h"
+#include "graphic/graphic.h"
+#include "graphic/image.h"
+
+namespace UI {
+
+BaseDropdown::BaseDropdown(UI::Panel* parent,
+ int32_t x,
+ int32_t y,
+ uint32_t w,
+ uint32_t h,
+ const std::string& label,
+ bool show_tick)
+ : UI::Panel(
+ parent,
+ x,
+ y,
+ w,
+ std::max(24,
+ UI::g_fh1->render(as_uifont(UI::g_fh1->fontset()->representative_character()))
+ ->height() +
+ 2)), // Height only to fit the button, so we can use this in Box layout.
+ max_list_height_(h - 2 * get_h()),
+ mouse_tolerance_(50),
+ button_box_(this, 0, 0, UI::Box::Horizontal, w, h),
+ push_button_(&button_box_,
+ "dropdown_select",
+ 0,
+ 0,
+ 24,
+ get_h(),
+ g_gr->images().get("images/ui_basic/but3.png"),
+ g_gr->images().get("images/ui_basic/scrollbar_down.png"),
+ pgettext("dropdown", "Select Item")),
+ display_button_(&button_box_,
+ "dropdown_label",
+ 0,
+ 0,
+ w - 24,
+ get_h(),
+ g_gr->images().get("images/ui_basic/but1.png"),
+ label,
+ "",
+ true,
+ false),
+ list_(parent,
+ x,
+ y + get_h(),
+ w,
+ 0,
+ show_tick), // Hook into parent so we can drop down outside the panel
+ label_(label) {
+ list_.set_visible(false);
+ list_.set_background(g_gr->images().get("images/ui_basic/but1.png"));
+ display_button_.set_perm_pressed(true);
+ button_box_.add(&display_button_, UI::Align::kLeft);
+ button_box_.add(&push_button_, UI::Align::kLeft);
+ button_box_.set_size(w, get_h());
+
+ display_button_.sigclicked.connect(boost::bind(&BaseDropdown::toggle_list, this));
+ push_button_.sigclicked.connect(boost::bind(&BaseDropdown::toggle_list, this));
+ list_.selected.connect(boost::bind(&BaseDropdown::set_value, this));
+ list_.clicked.connect(boost::bind(&BaseDropdown::toggle_list, this));
+}
+
+void BaseDropdown::add(const std::string& name,
+ const uint32_t value,
+ const Image* pic,
+ const bool select_this,
+ const std::string& tooltip_text) {
+ list_.set_size(
+ list_.get_w(), std::min(list_.get_h() + list_.get_lineheight(), max_list_height_));
+ list_.add(name, value, pic, select_this, tooltip_text);
+}
+
+bool BaseDropdown::has_selection() const {
+ return list_.has_selection();
+}
+
+uint32_t BaseDropdown::get_selected() const {
+ return list_.get_selected();
+}
+
+void BaseDropdown::set_label(const std::string& text) {
+ label_ = text;
+ display_button_.set_title(label_);
+}
+
+void BaseDropdown::set_tooltip(const std::string& text) {
+ tooltip_ = text;
+ display_button_.set_tooltip(tooltip_);
+}
+
+void BaseDropdown::set_enabled(bool on) {
+ set_can_focus(on);
+ push_button_.set_enabled(on);
+ push_button_.set_tooltip(on ? pgettext("dropdown", "Select Item") : "");
+ display_button_.set_enabled(on);
+ list_.set_visible(false);
+}
+
+void BaseDropdown::set_pos(Point point) {
+ UI::Panel::set_pos(point);
+ list_.set_pos(Point(point.x, point.y + get_h()));
+}
+
+void BaseDropdown::clear() {
+ list_.clear();
+ list_.set_size(list_.get_w(), 0);
+ set_layout_toplevel(false);
+}
+
+void BaseDropdown::think() {
+ if (list_.is_visible()) {
+ // Autocollapse with a bit of tolerance for the mouse movement to make it less fiddly.
+ if (!has_focus() || (get_mouse_position().x + mouse_tolerance_) < 0 ||
+ get_mouse_position().x > (get_w() + mouse_tolerance_) ||
+ (get_mouse_position().y + mouse_tolerance_ / 2) < 0 ||
+ get_mouse_position().y > (get_h() + list_.get_h() + mouse_tolerance_)) {
+ toggle_list();
+ }
+ }
+}
+
+uint32_t BaseDropdown::size() const {
+ return list_.size();
+}
+
+void BaseDropdown::set_value() {
+ const std::string name = list_.has_selection() ? list_.get_selected_name() :
+ /** TRANSLATORS: Selection in Dropdown menus. */
+ pgettext("dropdown", "Not Selected");
+
+ if (label_.empty()) {
+ display_button_.set_title(name);
+ } else {
+ /** TRANSLATORS: Label: Value. */
+ display_button_.set_title((boost::format(_("%1%: %2%")) % label_ % (name)).str());
+ }
+ display_button_.set_tooltip(list_.has_selection() ? list_.get_selected_tooltip() : tooltip_);
+ selected();
+}
+
+void BaseDropdown::toggle_list() {
+ list_.set_visible(!list_.is_visible());
+ if (list_.is_visible()) {
+ list_.move_to_top();
+ focus();
+ }
+ // Make sure that the list covers and deactivates the elements below it
+ set_layout_toplevel(list_.is_visible());
+}
+
+bool BaseDropdown::handle_key(bool down, SDL_Keysym code) {
+ if (down) {
+ switch (code.sym) {
+ case SDLK_ESCAPE:
+ case SDLK_KP_ENTER:
+ case SDLK_RETURN:
+ if (list_.is_visible()) {
+ toggle_list();
+ return true;
+ }
+ break;
+ case SDLK_DOWN:
+ if (!list_.is_visible()) {
+ toggle_list();
+ return true;
+ }
+ break;
+ default:
+ break; // not handled
+ }
+ }
+ if (list_.is_visible()) {
+ return list_.handle_key(down, code);
+ }
+ return false;
+}
+
+} // namespace UI
=== added file 'src/ui_basic/dropdown.h'
--- src/ui_basic/dropdown.h 1970-01-01 00:00:00 +0000
+++ src/ui_basic/dropdown.h 2016-09-27 08:44:17 +0000
@@ -0,0 +1,177 @@
+/*
+ * Copyright (C) 2016 by the Widelands Development Team
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#ifndef WL_UI_BASIC_DROPDOWN_H
+#define WL_UI_BASIC_DROPDOWN_H
+
+#include <deque>
+
+#include <boost/signals2.hpp>
+
+#include "ui_basic/box.h"
+#include "ui_basic/button.h"
+#include "ui_basic/listselect.h"
+#include "ui_basic/panel.h"
+
+namespace UI {
+
+/// Implementation for a dropdown menu that lets the user select a value.
+class BaseDropdown : public Panel {
+protected:
+ /// \param parent the parent panel
+ /// \param x the x-position within 'parent'
+ /// \param y the y-position within 'parent'
+ /// \param w the dropdown's width
+ /// \param h the maximum height for the dropdown list
+ /// \param label a label to prefix to the selected entry on the display button.
+ /// \param show_tick if 'true', the dropdown list will show a tick for the currently selected
+ /// entry.
+ BaseDropdown(Panel* parent,
+ int32_t x,
+ int32_t y,
+ uint32_t w,
+ uint32_t h,
+ const std::string& label,
+ bool show_tick = true);
+ ~BaseDropdown() {
+ clear();
+ }
+
+public:
+ boost::signals2::signal<void()> selected;
+
+ /// \return true if an element has been selected from the list
+ bool has_selection() const;
+
+ /// Sets a label that will be prefixed to the currently selected element's name
+ /// and displayed on the display button.
+ void set_label(const std::string& text);
+ /// Sets the tooltip for the display button.
+ void set_tooltip(const std::string& text);
+ /// Enables/disables the dropdown selection.
+ void set_enabled(bool on);
+ /// Move the dropdown. The dropdown's position is relative to the parent.
+ void set_pos(Point point) override;
+
+ /// The number of elements listed in the dropdown.
+ uint32_t size() const;
+
+ /// Handle keypresses
+ bool handle_key(bool down, SDL_Keysym code) override;
+
+protected:
+ /// 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
+ /// \param select_this whether this element should be selected
+ /// \param tooltip_text a tooltip for this entry
+ 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());
+
+ /// \return the index of the selected element
+ uint32_t get_selected() const;
+
+ /// Removes all elements from the list.
+ void clear();
+
+ /// Automatically collapses the list if the mouse gets too far away from the dropdown, or if it
+ /// loses focus.
+ void think() override;
+
+private:
+ /// Updates the title and tooltip of the display button and triggers a 'selected' signal.
+ void set_value();
+ /// Toggles the dropdown list on and off.
+ void toggle_list();
+
+ uint32_t max_list_height_;
+ const int mouse_tolerance_; // Allow mouse outside the panel a bit before autocollapse
+ UI::Box button_box_;
+ UI::Button push_button_;
+ UI::Button display_button_;
+ UI::Listselect<uintptr_t> list_;
+ std::string label_;
+ std::string tooltip_;
+};
+
+/// A dropdown menu that lets the user select a value of the datatype 'Entry'.
+template <typename Entry> class Dropdown : public BaseDropdown {
+public:
+ /// \param parent the parent panel
+ /// \param x the x-position within 'parent'
+ /// \param y the y-position within 'parent'
+ /// \param w the dropdown's width
+ /// \param h the maximum height for the dropdown list
+ /// \param label a label to prefix to the selected entry on the display button.
+ /// \param show_tick if 'true', the dropdown list will show a tick for the currently selected
+ /// entry.
+ Dropdown(Panel* parent,
+ int32_t x,
+ int32_t y,
+ uint32_t w,
+ uint32_t h,
+ const std::string& label,
+ bool show_tick = true)
+ : BaseDropdown(parent, x, y, w, h, label, show_tick) {
+ }
+ ~Dropdown() {
+ clear();
+ }
+
+ /// 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
+ /// \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_cache_.push_back(new Entry(value));
+ BaseDropdown::add(name, size(), pic, select_this, tooltip_text);
+ }
+
+ /// \return the selected element
+ const Entry& get_selected() const {
+ return *entry_cache_[BaseDropdown::get_selected()];
+ }
+
+ /// Removes all elements from the list.
+ void clear() {
+ BaseDropdown::clear();
+ for (Entry* entry : entry_cache_) {
+ delete entry;
+ }
+ entry_cache_.clear();
+ }
+
+private:
+ // Contains the actual elements. The BaseDropdown registers the indices only.
+ std::deque<Entry*> entry_cache_;
+};
+
+} // namespace UI
+
+#endif // end of include guard: WL_UI_BASIC_DROPDOWN_H
=== modified file 'src/ui_basic/listselect.cc'
--- src/ui_basic/listselect.cc 2016-08-04 15:49:05 +0000
+++ src/ui_basic/listselect.cc 2016-09-27 08:44:17 +0000
@@ -60,7 +60,8 @@
selection_(no_selection_index()),
last_click_time_(-10000),
last_selection_(no_selection_index()),
- show_check_(show_check) {
+ show_check_(show_check),
+ background_(nullptr) {
set_thinks(false);
scrollbar_.moved.connect(boost::bind(&BaseListselect::set_scrollpos, this, _1));
@@ -294,6 +295,28 @@
remove(selection_);
}
+/**
+ * \return The name of the currently selected entry.
+ * \throw NoSelection() if no entry has been selected
+ */
+const std::string& BaseListselect::get_selected_name() const {
+ if (selection_ == no_selection_index())
+ throw NoSelection();
+
+ return entry_records_[selection_]->name;
+}
+
+/**
+ * \return The tooltip for the currently selected entry.
+ * \throw NoSelection() if no entry has been selected
+ */
+const std::string& BaseListselect::get_selected_tooltip() const {
+ if (selection_ == no_selection_index())
+ throw NoSelection();
+
+ return entry_records_[selection_]->tooltip;
+}
+
uint32_t BaseListselect::get_lineheight() const {
return lineheight_ + kMargin;
}
@@ -302,6 +325,12 @@
return scrollbar_.is_enabled() ? get_w() - scrollbar_.get_w() : get_w();
}
+void BaseListselect::layout() {
+ scrollbar_.set_size(scrollbar_.get_w(), get_h());
+ scrollbar_.set_pagesize(get_h() - 2 * get_lineheight());
+ scrollbar_.set_steps(entry_records_.size() * get_lineheight() - get_h());
+}
+
/**
Redraw the listselect box
*/
@@ -311,6 +340,10 @@
uint32_t idx = scrollpos_ / lineheight;
int32_t y = 1 + idx * lineheight - scrollpos_;
+ if (background_ != nullptr) {
+ dst.tile(Rect(Point(0, 0), get_w(), get_h()), background_, Point(0, 0));
+ }
+
dst.brighten_rect(Rect(Point(0, 0), get_w(), get_h()), ms_darken_value);
while (idx < entry_records_.size()) {
=== modified file 'src/ui_basic/listselect.h'
--- src/ui_basic/listselect.h 2016-08-04 15:49:05 +0000
+++ src/ui_basic/listselect.h 2016-09-27 08:44:17 +0000
@@ -92,11 +92,20 @@
uint32_t get_selected() const;
void remove_selected();
+ const std::string& get_selected_name() const;
+ const std::string& get_selected_tooltip() const;
+
+ void set_background(const Image* background) {
+ background_ = background;
+ }
+
/// Return the total height (text + spacing) occupied by a single line.
uint32_t get_lineheight() const;
uint32_t get_eff_w() const;
+ void layout() override;
+
// Drawing and event handling
void draw(RenderTarget&) override;
bool handle_mousepress(uint8_t btn, int32_t x, int32_t y) override;
@@ -134,6 +143,7 @@
uint32_t last_selection_; // for double clicks
bool show_check_; // show a green arrow left of selected element
const Image* check_pic_;
+ const Image* background_;
std::string current_tooltip_;
};
@@ -167,6 +177,10 @@
return entry_cache_[BaseListselect::get_selected()];
}
+ void set_background(const Image* background) {
+ BaseListselect::set_background(background);
+ }
+
private:
std::deque<Entry> entry_cache_;
};
@@ -207,6 +221,10 @@
Entry& get_selected() const {
return *Base::get_selected();
}
+
+ void set_background(const Image* background) {
+ *Base::set_background(background);
+ }
};
}
=== modified file 'src/ui_basic/panel.h'
--- src/ui_basic/panel.h 2016-08-04 15:59:26 +0000
+++ src/ui_basic/panel.h 2016-09-27 08:44:17 +0000
@@ -113,7 +113,7 @@
// Geometry
void set_size(int nw, int nh);
void set_desired_size(int w, int h);
- void set_pos(Point);
+ virtual void set_pos(Point);
virtual void move_inside_parent();
virtual void layout();
=== modified file 'src/ui_basic/slider.cc'
--- src/ui_basic/slider.cc 2016-08-04 15:49:05 +0000
+++ src/ui_basic/slider.cc 2016-09-27 08:44:17 +0000
@@ -75,6 +75,7 @@
bar_size_(bar_size),
cursor_size_(cursor_size) {
set_thinks(false);
+ set_can_focus(true);
calculate_cursor_position();
}
@@ -200,6 +201,7 @@
if (enabled_ == enabled)
return;
+ set_can_focus(enabled);
enabled_ = enabled;
if (!enabled) {
pressed_ = false;
@@ -400,6 +402,7 @@
if (btn != SDL_BUTTON_LEFT)
return false;
+ focus();
if (x >= cursor_pos_ && x <= cursor_pos_ + cursor_size_) {
// click on cursor
cursor_pressed(x);
@@ -470,6 +473,7 @@
if (btn != SDL_BUTTON_LEFT)
return false;
+ focus();
if (y >= cursor_pos_ && y <= cursor_pos_ + cursor_size_) {
// click on cursor
cursor_pressed(y);
=== modified file 'src/ui_fsmenu/launch_spg.cc'
--- src/ui_fsmenu/launch_spg.cc 2016-08-27 10:15:32 +0000
+++ src/ui_fsmenu/launch_spg.cc 2016-09-27 08:44:17 +0000
@@ -77,17 +77,13 @@
std::string(),
false,
false),
- wincondition_(this,
- "win_condition",
- get_w() * 7 / 10,
- get_h() * 4 / 10 + buth_,
- butw_,
- buth_,
- g_gr->images().get("images/ui_basic/but1.png"),
- "",
- std::string(),
- false,
- false),
+ win_condition_dropdown_(this,
+ get_w() * 7 / 10,
+ get_h() * 4 / 10 + buth_,
+ butw_,
+ get_h() - get_h() * 4 / 10 - buth_,
+ ""),
+ last_win_condition_(""),
back_(this,
"back",
get_w() * 7 / 10,
@@ -152,15 +148,12 @@
is_scenario_(false) {
select_map_.sigclicked.connect(
boost::bind(&FullscreenMenuLaunchSPG::select_map, boost::ref(*this)));
- wincondition_.sigclicked.connect(
- boost::bind(&FullscreenMenuLaunchSPG::win_condition_clicked, boost::ref(*this)));
+ win_condition_dropdown_.selected.connect(
+ boost::bind(&FullscreenMenuLaunchSPG::win_condition_selected, this));
back_.sigclicked.connect(boost::bind(&FullscreenMenuLaunchSPG::clicked_back, boost::ref(*this)));
ok_.sigclicked.connect(boost::bind(&FullscreenMenuLaunchSPG::clicked_ok, boost::ref(*this)));
lua_ = new LuaInterface();
- win_condition_scripts_ = settings_->settings().win_condition_scripts;
- cur_wincondition_ = -1;
- win_condition_clicked();
title_.set_fontsize(UI_FONT_SIZE_BIG);
@@ -226,68 +219,112 @@
}
/**
- * WinCondition button has been pressed
- */
-void FullscreenMenuLaunchSPG::win_condition_clicked() {
- if (settings_->can_change_map()) {
- cur_wincondition_++;
- cur_wincondition_ %= win_condition_scripts_.size();
- settings_->set_win_condition_script(win_condition_scripts_[cur_wincondition_]);
- }
-
- win_condition_update();
-}
-
-/**
- * update win conditions information
- */
-void FullscreenMenuLaunchSPG::win_condition_update() {
+ * Fill the dropdown with the available win conditions.
+ */
+void FullscreenMenuLaunchSPG::load_win_conditions() {
+ win_condition_dropdown_.clear();
if (settings_->settings().scenario) {
- wincondition_.set_title(_("Scenario"));
- wincondition_.set_tooltip(_("Win condition is set through the scenario"));
+ win_condition_dropdown_.set_label(_("Scenario"));
+ win_condition_dropdown_.set_tooltip(_("Win condition is set through the scenario"));
+ win_condition_dropdown_.set_enabled(false);
} else {
- win_condition_load();
+ win_condition_dropdown_.set_label("");
+ win_condition_dropdown_.set_tooltip("");
+ Widelands::Map map;
+ std::unique_ptr<Widelands::MapLoader> ml =
+ map.get_correct_loader(settings_->settings().mapfilename);
+ if (ml != nullptr) {
+ try {
+ ml->preload_map(true);
+ std::set<std::string> tags = map.get_tags();
+ // Make sure that the last win condition is still valid. If not, pick the first one
+ // available.
+ if (last_win_condition_.empty()) {
+ last_win_condition_ = settings_->settings().win_condition_scripts.front();
+ }
+ std::unique_ptr<LuaTable> t = win_condition_if_valid(last_win_condition_, tags);
+ for (const std::string& win_condition_script :
+ settings_->settings().win_condition_scripts) {
+ if (t) {
+ break;
+ } else {
+ last_win_condition_ = win_condition_script;
+ t = win_condition_if_valid(last_win_condition_, tags);
+ }
+ }
+
+ // Now fill the dropdown.
+ for (const std::string& win_condition_script :
+ settings_->settings().win_condition_scripts) {
+ try {
+ t = win_condition_if_valid(win_condition_script, tags);
+ if (t) {
+ i18n::Textdomain td("win_conditions");
+ win_condition_dropdown_.add(
+ _(t->get_string("name")), win_condition_script, nullptr,
+ win_condition_script == last_win_condition_, t->get_string("description"));
+ }
+ } catch (LuaTableKeyError& e) {
+ log("LaunchSPG: Error loading win condition: %s %s\n",
+ win_condition_script.c_str(), e.what());
+ }
+ }
+ } catch (const std::exception& e) {
+ const std::string error_message =
+ (boost::format(_("Unable to determine valid win conditions because the map '%s' "
+ "could not be loaded.")) %
+ settings_->settings().mapfilename)
+ .str();
+ win_condition_dropdown_.set_label(_("Error"));
+ win_condition_dropdown_.set_tooltip(error_message);
+ log("LaunchSPG: Exception: %s %s\n", error_message.c_str(), e.what());
+ }
+
+ } else {
+ const std::string error_message =
+ (boost::format(_("Unable to determine valid win conditions because the map '%s' could "
+ "not be loaded.")) %
+ settings_->settings().mapfilename)
+ .str();
+ win_condition_dropdown_.set_label(_("Error"));
+ win_condition_dropdown_.set_tooltip(error_message);
+ log("LaunchSPG: No map loader: %s\n", error_message.c_str());
+ }
+ win_condition_dropdown_.set_enabled(true);
}
}
-/**
- * Loads the current win condition script from the settings provider.
- * Calls win_condition_clicked() if the current map can't handle the win condition.
- */
-void FullscreenMenuLaunchSPG::win_condition_load() {
+void FullscreenMenuLaunchSPG::win_condition_selected() {
+ last_win_condition_ = win_condition_dropdown_.get_selected();
+}
+
+std::unique_ptr<LuaTable>
+FullscreenMenuLaunchSPG::win_condition_if_valid(const std::string& win_condition_script,
+ std::set<std::string> tags) const {
bool is_usable = true;
+ std::unique_ptr<LuaTable> t;
try {
- std::unique_ptr<LuaTable> t = lua_->run_script(settings_->get_win_condition_script());
+ t = lua_->run_script(win_condition_script);
t->do_not_warn_about_unaccessed_keys();
// Skip this win condition if the map doesn't have all the required tags
- if (t->has_key("map_tags") && !settings_->settings().mapfilename.empty()) {
- Widelands::Map map;
- std::unique_ptr<Widelands::MapLoader> ml =
- map.get_correct_loader(settings_->settings().mapfilename);
- ml->preload_map(true);
+ if (t->has_key("map_tags")) {
+
for (const std::string& map_tag : t->get_table("map_tags")->array_entries<std::string>()) {
- if (!map.has_tag(map_tag)) {
+ if (!tags.count(map_tag)) {
is_usable = false;
break;
}
}
}
-
- const std::string name = t->get_string("name");
- const std::string descr = t->get_string("description");
- {
- i18n::Textdomain td("win_conditions");
- wincondition_.set_title(_(name));
- }
- wincondition_.set_tooltip(descr.c_str());
- } catch (LuaTableKeyError&) {
- // might be that this is not a win condition after all.
- is_usable = false;
+ } catch (LuaTableKeyError& e) {
+ log(
+ "LaunchSPG: Error loading win condition: %s %s\n", win_condition_script.c_str(), e.what());
}
if (!is_usable) {
- win_condition_clicked();
+ t.reset(nullptr);
}
+ return t;
}
/**
@@ -308,6 +345,7 @@
if (is_scenario_) {
end_modal<FullscreenMenuBase::MenuTarget>(FullscreenMenuBase::MenuTarget::kScenarioGame);
} else {
+ settings_->set_win_condition_script(win_condition_dropdown_.get_selected());
end_modal<FullscreenMenuBase::MenuTarget>(FullscreenMenuBase::MenuTarget::kNormalGame);
}
}
@@ -333,7 +371,6 @@
select_map_.set_visible(settings_->can_change_map());
select_map_.set_enabled(settings_->can_change_map());
- wincondition_.set_enabled(settings_->can_change_map() && !settings.scenario);
if (settings.scenario) {
set_scenario_values();
@@ -352,8 +389,6 @@
// update the player description groups
for (uint32_t i = 0; i < MAX_PLAYERS; ++i)
players_[i]->refresh();
-
- win_condition_update();
}
/**
@@ -380,6 +415,7 @@
safe_place_for_host(nr_players_);
settings_->set_map(mapdata.name, mapdata.filename, nr_players_);
+ load_win_conditions();
}
/**
=== modified file 'src/ui_fsmenu/launch_spg.h'
--- src/ui_fsmenu/launch_spg.h 2016-08-04 15:49:05 +0000
+++ src/ui_fsmenu/launch_spg.h 2016-09-27 08:44:17 +0000
@@ -20,10 +20,12 @@
#ifndef WL_UI_FSMENU_LAUNCH_SPG_H
#define WL_UI_FSMENU_LAUNCH_SPG_H
+#include <memory>
#include <string>
#include "logic/constants.h"
#include "ui_basic/button.h"
+#include "ui_basic/dropdown.h"
#include "ui_basic/multilinetextarea.h"
#include "ui_basic/textarea.h"
#include "ui_fsmenu/base.h"
@@ -64,9 +66,16 @@
LuaInterface* lua_;
void select_map();
- void win_condition_clicked();
- void win_condition_update();
- void win_condition_load();
+ /// Loads all win conditions that can be played with the map into the selection dropdown.
+ /// Disables the dropdown if the map is a scenario.
+ void load_win_conditions();
+ /// Remembers the win condition that is currently selected in the dropdown.
+ void win_condition_selected();
+ /// If the win condition in 'win_condition_script' can be played with the map tags,
+ /// parses the win condition and returns it as a std::unique_ptr<LuaTable>.
+ /// If this win condition can't be played with the map tags, returns a unique_ptr to nullptr.
+ std::unique_ptr<LuaTable> win_condition_if_valid(const std::string& win_condition_script,
+ std::set<std::string> tags) const;
void set_scenario_values();
void switch_to_position(uint8_t);
void safe_place_for_host(uint8_t);
@@ -74,7 +83,10 @@
uint32_t butw_;
uint32_t buth_;
- UI::Button select_map_, wincondition_, back_, ok_;
+ UI::Button select_map_;
+ UI::Dropdown<std::string> win_condition_dropdown_;
+ std::string last_win_condition_;
+ UI::Button back_, ok_;
UI::Button* pos_[MAX_PLAYERS];
UI::Textarea title_, mapname_;
UI::Textarea name_, type_, team_, tribe_, init_, wincondition_type_;
@@ -87,8 +99,6 @@
std::string player_save_tribe_[MAX_PLAYERS];
int8_t nr_players_;
bool is_scenario_;
- std::vector<std::string> win_condition_scripts_;
- uint8_t cur_wincondition_;
};
#endif // end of include guard: WL_UI_FSMENU_LAUNCH_SPG_H
=== modified file 'src/ui_fsmenu/options.cc'
--- src/ui_fsmenu/options.cc 2016-08-04 15:49:05 +0000
+++ src/ui_fsmenu/options.cc 2016-09-27 08:44:17 +0000
@@ -153,11 +153,20 @@
box_sound_(&tabs_, 0, 0, UI::Box::Vertical, 0, 0, padding_),
box_saving_(&tabs_, 0, 0, UI::Box::Vertical, 0, 0, padding_),
box_game_(&tabs_, 0, 0, UI::Box::Vertical, 0, 0, padding_),
- box_language_(&tabs_, 0, 0, UI::Box::Vertical, 0, 0, padding_),
// Interface options
- label_resolution_(&box_interface_, _("In-game resolution"), UI::Align::kLeft),
- resolution_list_(&box_interface_, 0, 0, column_width_ / 2, 80, true),
+ language_dropdown_(&box_interface_,
+ 0,
+ 0,
+ column_width_ / 2,
+ get_inner_h() - tab_panel_y_ - buth_ - hmargin_ - 4 * padding_,
+ _("Language")),
+ resolution_dropdown_(&box_interface_,
+ 0,
+ 0,
+ column_width_ / 2,
+ get_inner_h() - tab_panel_y_ - 2 * buth_ - hmargin_ - 5 * padding_,
+ _("In-game resolution")),
fullscreen_(&box_interface_, Point(0, 0), _("Fullscreen"), "", column_width_),
inputgrab_(&box_interface_, Point(0, 0), _("Grab Input"), "", column_width_),
@@ -257,15 +266,6 @@
/** TRANSLATORS: and it also lets you jump to it on the map. */
single_watchwin_(&box_game_, Point(0, 0), _("Use single watchwindow mode")),
- // Language options
- label_language_(&box_language_, _("Language"), UI::Align::kLeft),
- language_list_(&box_language_,
- 0,
- 0,
- column_width_ / 2,
- get_inner_h() - tab_panel_y_ - 2 * buth_ - hmargin_ - 5 * padding_,
- true),
-
os_(opt) {
// Set up UI Elements
title_.set_fontsize(UI_FONT_SIZE_BIG);
@@ -275,7 +275,6 @@
tabs_.add("options_sound", _("Sound"), &box_sound_, "");
tabs_.add("options_saving", _("Saving"), &box_saving_, "");
tabs_.add("options_game", _("Game"), &box_game_, "");
- tabs_.add("options_language", _("Language"), &box_language_, "");
// We want the last active tab when "Apply" was clicked.
if (os_.active_tab < tabs_.tabs().size()) {
@@ -289,11 +288,10 @@
box_sound_.set_size(tabs_.get_inner_w(), tabs_.get_inner_h());
box_saving_.set_size(tabs_.get_inner_w(), tabs_.get_inner_h());
box_game_.set_size(tabs_.get_inner_w(), tabs_.get_inner_h());
- box_language_.set_size(tabs_.get_inner_w(), tabs_.get_inner_h());
// Interface
- box_interface_.add(&label_resolution_, UI::Align::kLeft);
- box_interface_.add(&resolution_list_, UI::Align::kLeft);
+ box_interface_.add(&language_dropdown_, UI::Align::kLeft);
+ box_interface_.add(&resolution_dropdown_, UI::Align::kLeft);
box_interface_.add(&fullscreen_, UI::Align::kLeft);
box_interface_.add(&inputgrab_, UI::Align::kLeft);
box_interface_.add(&sb_maxfps_, UI::Align::kLeft);
@@ -321,10 +319,6 @@
box_game_.add(&transparent_chat_, UI::Align::kLeft);
box_game_.add(&single_watchwin_, UI::Align::kLeft);
- // Language
- box_language_.add(&label_language_, UI::Align::kLeft);
- box_language_.add(&language_list_, UI::Align::kLeft);
-
// Bind actions
cancel_.sigclicked.connect(boost::bind(&FullscreenMenuOptions::clicked_back, this));
apply_.sigclicked.connect(boost::bind(&FullscreenMenuOptions::clicked_apply, this));
@@ -355,18 +349,18 @@
for (uint32_t i = 0; i < resolutions_.size(); ++i) {
const bool selected = resolutions_[i].xres == opt.xres && resolutions_[i].yres == opt.yres;
did_select_a_res |= selected;
- /** TRANSLATORS: Screen resolution, e.g. 800 x 600*/
- resolution_list_.add(
- (boost::format(_("%1% x %2%")) % resolutions_[i].xres % resolutions_[i].yres).str(),
- nullptr, nullptr, selected);
+ resolution_dropdown_.add(
+ /** TRANSLATORS: Screen resolution, e.g. 800 x 600*/
+ (boost::format(_("%1% x %2%")) % resolutions_[i].xres % resolutions_[i].yres).str(), i,
+ nullptr, selected);
}
if (!did_select_a_res) {
- resolution_list_.add(
- (boost::format(_("%1% x %2%")) % opt.xres % opt.yres).str(), nullptr, nullptr, true);
uint32_t entry = resolutions_.size();
resolutions_.resize(entry + 1);
resolutions_[entry].xres = opt.xres;
resolutions_[entry].yres = opt.yres;
+ resolution_dropdown_.add(
+ (boost::format(_("%1% x %2%")) % opt.xres % opt.yres).str(), entry, nullptr, true);
}
fullscreen_.set_state(opt.fullscreen);
@@ -395,14 +389,13 @@
// Language options
add_languages_to_list(opt.language);
- language_list_.focus();
}
void FullscreenMenuOptions::add_languages_to_list(const std::string& current_locale) {
// We want these two entries on top - the most likely user's choice and the default.
- language_list_.add(_("Try system language"), "", nullptr, current_locale == "");
- language_list_.add("English", "en", nullptr, current_locale == "en");
+ language_dropdown_.add(_("Try system language"), "", nullptr, current_locale == "");
+ language_dropdown_.add("English", "en", nullptr, current_locale == "en");
// Add translation directories to the list
std::vector<LanguageEntry> entries;
@@ -451,8 +444,8 @@
find_selected_locale(&selected_locale, current_locale);
std::sort(entries.begin(), entries.end());
for (const LanguageEntry& entry : entries) {
- language_list_.add(entry.descname.c_str(), entry.localename, nullptr,
- entry.localename == selected_locale, "");
+ language_dropdown_.add(entry.descname.c_str(), entry.localename, nullptr,
+ entry.localename == selected_locale, "");
}
}
@@ -463,9 +456,14 @@
OptionsCtrl::OptionsStruct FullscreenMenuOptions::get_values() {
// Write all data from UI elements
// Interface options
- const uint32_t res_index = resolution_list_.selection_index();
- os_.xres = resolutions_[res_index].xres;
- os_.yres = resolutions_[res_index].yres;
+ if (language_dropdown_.has_selection()) {
+ os_.language = language_dropdown_.get_selected();
+ }
+ if (resolution_dropdown_.has_selection()) {
+ const uint32_t res_index = resolution_dropdown_.get_selected();
+ os_.xres = resolutions_[res_index].xres;
+ os_.yres = resolutions_[res_index].yres;
+ }
os_.fullscreen = fullscreen_.get_state();
os_.inputgrab = inputgrab_.get_state();
os_.maxfps = sb_maxfps_.get_value();
@@ -493,11 +491,6 @@
os_.transparent_chat = transparent_chat_.get_state();
os_.single_watchwin = single_watchwin_.get_state();
- // Language options
- if (language_list_.has_selection()) {
- os_.language = language_list_.get_selected();
- }
-
// Last tab for reloading the options menu
os_.active_tab = tabs_.active();
return os_;
=== modified file 'src/ui_fsmenu/options.h'
--- src/ui_fsmenu/options.h 2016-08-04 15:49:05 +0000
+++ src/ui_fsmenu/options.h 2016-09-27 08:44:17 +0000
@@ -27,7 +27,7 @@
#include "ui_basic/button.h"
#include "ui_basic/checkbox.h"
-#include "ui_basic/listselect.h"
+#include "ui_basic/dropdown.h"
#include "ui_basic/multilinetextarea.h"
#include "ui_basic/spinbox.h"
#include "ui_basic/tabpanel.h"
@@ -122,11 +122,10 @@
UI::Box box_sound_;
UI::Box box_saving_;
UI::Box box_game_;
- UI::Box box_language_;
// Interface options
- UI::Textarea label_resolution_;
- UI::Listselect<void*> resolution_list_;
+ UI::Dropdown<std::string> language_dropdown_;
+ UI::Dropdown<uintptr_t> resolution_dropdown_;
UI::Checkbox fullscreen_;
UI::Checkbox inputgrab_;
UI::SpinBox sb_maxfps_;
@@ -154,10 +153,6 @@
UI::Checkbox transparent_chat_;
UI::Checkbox single_watchwin_;
- // Language options
- UI::Textarea label_language_;
- UI::Listselect<std::string> language_list_;
-
OptionsCtrl::OptionsStruct os_;
class ScreenResolution {
Follow ups
-
[Merge] lp:~widelands-dev/widelands/bug-536489-dropdown into lp:widelands
From: noreply, 2016-10-29
-
Re: [Merge] lp:~widelands-dev/widelands/bug-536489-dropdown into lp:widelands
From: GunChleoc, 2016-10-29
-
Re: [Merge] lp:~widelands-dev/widelands/bug-536489-dropdown into lp:widelands
From: SirVer, 2016-10-29
-
[Merge] lp:~widelands-dev/widelands/bug-536489-dropdown into lp:widelands
From: bunnybot, 2016-10-29
-
Re: [Merge] lp:~widelands-dev/widelands/bug-536489-dropdown into lp:widelands
From: GunChleoc, 2016-10-28
-
[Merge] lp:~widelands-dev/widelands/bug-536489-dropdown into lp:widelands
From: bunnybot, 2016-10-28
-
Re: [Merge] lp:~widelands-dev/widelands/bug-536489-dropdown into lp:widelands
From: SirVer, 2016-10-28
-
Re: [Merge] lp:~widelands-dev/widelands/bug-536489-dropdown into lp:widelands
From: GunChleoc, 2016-10-28
-
Re: [Merge] lp:~widelands-dev/widelands/bug-536489-dropdown into lp:widelands
From: SirVer, 2016-10-28
-
[Merge] lp:~widelands-dev/widelands/bug-536489-dropdown into lp:widelands
From: bunnybot, 2016-10-25
-
Re: [Merge] lp:~widelands-dev/widelands/bug-536489-dropdown into lp:widelands
From: GunChleoc, 2016-10-07
-
Re: [Merge] lp:~widelands-dev/widelands/bug-536489-dropdown into lp:widelands
From: kaputtnik, 2016-10-06
-
Re: [Merge] lp:~widelands-dev/widelands/bug-536489-dropdown into lp:widelands
From: GunChleoc, 2016-10-05
-
Re: [Merge] lp:~widelands-dev/widelands/bug-536489-dropdown into lp:widelands
From: SirVer, 2016-10-04
-
Re: [Merge] lp:~widelands-dev/widelands/bug-536489-dropdown into lp:widelands
From: GunChleoc, 2016-10-03
-
Re: [Merge] lp:~widelands-dev/widelands/bug-536489-dropdown into lp:widelands
From: Klaus Halfmann, 2016-10-03
-
[Merge] lp:~widelands-dev/widelands/bug-536489-dropdown into lp:widelands
From: bunnybot, 2016-10-02
-
[Merge] lp:~widelands-dev/widelands/bug-536489-dropdown into lp:widelands
From: bunnybot, 2016-10-02
-
Re: [Merge] lp:~widelands-dev/widelands/bug-536489-dropdown into lp:widelands
From: kaputtnik, 2016-10-02
-
Re: [Merge] lp:~widelands-dev/widelands/bug-536489-dropdown into lp:widelands
From: GunChleoc, 2016-10-02
-
Re: [Merge] lp:~widelands-dev/widelands/bug-536489-dropdown into lp:widelands
From: kaputtnik, 2016-10-01
-
Re: [Merge] lp:~widelands-dev/widelands/bug-536489-dropdown into lp:widelands
From: Klaus Halfmann, 2016-10-01
-
Re: [Merge] lp:~widelands-dev/widelands/bug-536489-dropdown into lp:widelands
From: Klaus Halfmann, 2016-10-01
-
Re: [Merge] lp:~widelands-dev/widelands/bug-536489-dropdown into lp:widelands
From: GunChleoc, 2016-10-01
-
Re: [Merge] lp:~widelands-dev/widelands/bug-536489-dropdown into lp:widelands
From: kaputtnik, 2016-09-30
-
Re: [Merge] lp:~widelands-dev/widelands/bug-536489-dropdown into lp:widelands
From: GunChleoc, 2016-09-28
-
Re: [Merge] lp:~widelands-dev/widelands/bug-536489-dropdown into lp:widelands
From: GunChleoc, 2016-09-28
-
Re: [Merge] lp:~widelands-dev/widelands/bug-536489-dropdown into lp:widelands
From: kaputtnik, 2016-09-28
-
Re: [Merge] lp:~widelands-dev/widelands/bug-536489-dropdown into lp:widelands
From: Klaus Halfmann, 2016-09-28
-
[Merge] lp:~widelands-dev/widelands/bug-536489-dropdown into lp:widelands
From: bunnybot, 2016-09-27
-
Re: [Merge] lp:~widelands-dev/widelands/bug-536489-dropdown into lp:widelands
From: GunChleoc, 2016-09-27