widelands-dev team mailing list archive
-
widelands-dev team
-
Mailing list archive
-
Message #11928
[Merge] lp:~widelands-dev/widelands/story_message_box into lp:widelands
GunChleoc has proposed merging lp:~widelands-dev/widelands/story_message_box into lp:widelands.
Commit message:
Improvements to StoryMessageBox, keyboard navigation and UI focussing
- Refactored StoryMessageBox to use Box layout. Moved all of its functionality over from lua_game.cc.
- Removed unused customazability of OK button text in StoryMessageBox.
- Story Message Box now tries to use the new renderer before falling back to the old one.
- Atlantean campaign now uses the campaign_message_box function.
- Added keyboard navigation to box scrolling.
- Implemented keyboard navigation for scrollbars in Multilinetextareas.
- Panels will now focus when left-clicked on.
Requested reviews:
Widelands Developers (widelands-dev)
For more details, see:
https://code.launchpad.net/~widelands-dev/widelands/story_message_box/+merge/335003
Improvements to StoryMessageBox, keyboard navigation and UI focussing.
This is a prerequisite for converting the campaigns and scenarios to the new font renderer.
--
Your team Widelands Developers is requested to review the proposed merge of lp:~widelands-dev/widelands/story_message_box into lp:widelands.
=== modified file 'data/campaigns/atl01.wmf/scripting/init.lua'
--- data/campaigns/atl01.wmf/scripting/init.lua 2016-09-17 18:35:48 +0000
+++ data/campaigns/atl01.wmf/scripting/init.lua 2017-12-09 19:34:17 +0000
@@ -33,23 +33,11 @@
-- =================
-- Helper functions
-- =================
--- Show one message box
-function msg_box(i)
- if i.pre_func then i.pre_func() end
-
- if not i.h then i.h = 400 end
-
- message_box(p1, i.title, i.body, i)
-
- if i.post_func then i.post_func() end
-
- sleep(130)
-end
-- Show many message boxes
function msg_boxes(boxes_descr)
for idx,box_descr in ipairs(boxes_descr) do
- msg_box(box_descr)
+ campaign_message_box(box_descr)
end
end
=== modified file 'src/scripting/lua_game.cc'
--- src/scripting/lua_game.cc 2017-08-20 17:45:42 +0000
+++ src/scripting/lua_game.cc 2017-12-09 19:34:17 +0000
@@ -413,9 +413,6 @@
:arg posy: y position of window in pixels. Default: centered
:type posy: :class:`integer`
- :arg button_text: Text on the button. Default: OK.
- :type button_text: :class:`string`
-
:returns: :const:`nil`
*/
// UNTESTED
@@ -430,50 +427,35 @@
uint32_t h = 300;
int32_t posx = -1;
int32_t posy = -1;
- std::string button_text = _("OK");
+ Coords coords = Coords::null();
-#define CHECK_ARG(var, type) \
+#define CHECK_UINT(var) \
lua_getfield(L, -1, #var); \
if (!lua_isnil(L, -1)) \
- var = luaL_check##type(L, -1); \
+ var = luaL_checkuint32(L, -1); \
lua_pop(L, 1);
if (lua_gettop(L) == 4) {
- CHECK_ARG(posx, uint32);
- CHECK_ARG(posy, uint32);
- CHECK_ARG(w, uint32);
- CHECK_ARG(h, uint32);
- CHECK_ARG(button_text, string);
+ CHECK_UINT(posx);
+ CHECK_UINT(posy);
+ CHECK_UINT(w);
+ CHECK_UINT(h);
- // This must be done manually
+ // If a field has been defined, read the coordinated to jump to.
lua_getfield(L, 4, "field");
if (!lua_isnil(L, -1)) {
- Coords c = (*get_user_class<LuaField>(L, -1))->coords();
- game.get_ipl()->map_view()->scroll_to_field(c, MapView::Transition::Jump);
+ coords = (*get_user_class<LuaField>(L, -1))->coords();
+ game.get_ipl()->map_view()->scroll_to_field(coords, MapView::Transition::Jump);
}
lua_pop(L, 1);
}
-#undef CHECK_ARG
-
- uint32_t cspeed = game.game_controller()->desired_speed();
- game.game_controller()->set_desired_speed(0);
-
- game.save_handler().set_allow_saving(false);
-
- StoryMessageBox* mb = new StoryMessageBox(game.get_ipl(), luaL_checkstring(L, 2),
- luaL_checkstring(L, 3), button_text, posx, posy, w, h);
+#undef CHECK_UINT
+ StoryMessageBox* mb = new StoryMessageBox(&game, coords, luaL_checkstring(L, 2),
+ luaL_checkstring(L, 3), posx, posy, w, h);
mb->run<UI::Panel::Returncodes>();
delete mb;
- // Manually force the game to reevaluate it's current state,
- // especially time information.
- game.game_controller()->think();
-
- game.game_controller()->set_desired_speed(cspeed);
-
- game.save_handler().set_allow_saving(true);
-
return 1;
}
=== modified file 'src/ui_basic/box.cc'
--- src/ui_basic/box.cc 2017-03-11 17:10:33 +0000
+++ src/ui_basic/box.cc 2017-12-09 19:34:17 +0000
@@ -135,6 +135,23 @@
layout();
}
+bool Box::handle_mousewheel(uint32_t which, int32_t x, int32_t y) {
+ if (scrollbar_) {
+ assert(scrolling_);
+ return scrollbar_->handle_mousewheel(which, x, y);
+ }
+ return Panel::handle_mousewheel(which, x, y);
+
+}
+bool Box::handle_key(bool down, SDL_Keysym code) {
+ if (scrollbar_) {
+ assert(scrolling_);
+ return scrollbar_->handle_key(down, code);
+ }
+ return Panel::handle_key(down, code);
+}
+
+
/**
* Adjust all the children and the box's size.
*/
=== modified file 'src/ui_basic/box.h'
--- src/ui_basic/box.h 2017-03-11 17:10:33 +0000
+++ src/ui_basic/box.h 2017-12-09 19:34:17 +0000
@@ -72,6 +72,8 @@
protected:
void layout() override;
void update_desired_size() override;
+ bool handle_mousewheel(uint32_t which, int32_t x, int32_t y) override;
+ bool handle_key(bool down, SDL_Keysym code) override;
private:
void get_item_desired_size(uint32_t idx, int* depth, int* breadth);
=== modified file 'src/ui_basic/multilinetextarea.cc'
--- src/ui_basic/multilinetextarea.cc 2017-11-03 22:04:15 +0000
+++ src/ui_basic/multilinetextarea.cc 2017-12-09 19:34:17 +0000
@@ -167,6 +167,9 @@
bool MultilineTextarea::handle_mousewheel(uint32_t which, int32_t x, int32_t y) {
return scrollbar_.handle_mousewheel(which, x, y);
}
+bool MultilineTextarea::handle_key(bool down, SDL_Keysym code) {
+ return scrollbar_.handle_key(down, code);
+}
void MultilineTextarea::scroll_to_top() {
scrollbar_.set_scrollpos(0);
=== modified file 'src/ui_basic/multilinetextarea.h'
--- src/ui_basic/multilinetextarea.h 2017-05-06 10:18:56 +0000
+++ src/ui_basic/multilinetextarea.h 2017-12-09 19:34:17 +0000
@@ -84,6 +84,7 @@
void draw(RenderTarget&) override;
bool handle_mousewheel(uint32_t which, int32_t x, int32_t y) override;
+ bool handle_key(bool down, SDL_Keysym code) override;
void scroll_to_top();
void set_background(const Image* background);
=== modified file 'src/ui_basic/panel.cc'
--- src/ui_basic/panel.cc 2017-09-04 15:02:05 +0000
+++ src/ui_basic/panel.cc 2017-12-09 19:34:17 +0000
@@ -501,7 +501,10 @@
*
* \return true if the mouseclick was processed, flase otherwise
*/
-bool Panel::handle_mousepress(const uint8_t, int32_t, int32_t) {
+bool Panel::handle_mousepress(const uint8_t btn, int32_t, int32_t) {
+ if (btn == SDL_BUTTON_LEFT) {
+ focus();
+ }
return false;
}
=== modified file 'src/ui_basic/scrollbar.cc'
--- src/ui_basic/scrollbar.cc 2017-05-18 21:26:29 +0000
+++ src/ui_basic/scrollbar.cc 2017-12-09 19:34:17 +0000
@@ -438,6 +438,75 @@
return true;
}
+bool Scrollbar::handle_key(bool down, SDL_Keysym code) {
+ if (down) {
+ if (horizontal_) {
+ switch (code.sym) {
+ case SDLK_KP_6:
+ if (code.mod & KMOD_NUM) {
+ break;
+ }
+ FALLS_THROUGH;
+ case SDLK_RIGHT:
+ action(Plus);
+ return true;
+
+ case SDLK_KP_4:
+ if (code.mod & KMOD_NUM) {
+ break;
+ }
+ FALLS_THROUGH;
+ case SDLK_LEFT:
+ action(Minus);
+ return true;
+ default:
+ break; // not handled
+ }
+ } else {
+ switch (code.sym) {
+ case SDLK_KP_2:
+ if (code.mod & KMOD_NUM) {
+ break;
+ }
+ FALLS_THROUGH;
+ case SDLK_DOWN:
+ action(Plus);
+ return true;
+
+ case SDLK_KP_8:
+ if (code.mod & KMOD_NUM) {
+ break;
+ }
+ FALLS_THROUGH;
+ case SDLK_UP:
+ action(Minus);
+ return true;
+
+ case SDLK_KP_3:
+ if (code.mod & KMOD_NUM) {
+ break;
+ }
+ FALLS_THROUGH;
+ case SDLK_PAGEDOWN:
+ action(PlusPage);
+ return true;
+
+ case SDLK_KP_9:
+ if (code.mod & KMOD_NUM) {
+ break;
+ }
+ FALLS_THROUGH;
+ case SDLK_PAGEUP:
+ action(MinusPage);
+ return true;
+ default:
+ break; // not handled
+ }
+ }
+ }
+ return Panel::handle_key(down, code);
+}
+
void Scrollbar::layout() {
if ((2 * kSize + get_knob_size()) > static_cast<uint32_t>((horizontal_ ? get_w() : get_h()))) {
buttonsize_ = kSize / 2;
=== modified file 'src/ui_basic/scrollbar.h'
--- src/ui_basic/scrollbar.h 2017-04-22 11:16:54 +0000
+++ src/ui_basic/scrollbar.h 2017-12-09 19:34:17 +0000
@@ -67,6 +67,7 @@
bool handle_mousepress(uint8_t btn, int32_t x, int32_t y) override;
bool handle_mousewheel(uint32_t, int32_t, int32_t y) override;
+ bool handle_key(bool down, SDL_Keysym code) override;
void set_force_draw(bool const t) {
force_draw_ = t;
=== modified file 'src/wui/story_message_box.cc'
--- src/wui/story_message_box.cc 2017-01-25 18:55:59 +0000
+++ src/wui/story_message_box.cc 2017-12-09 19:34:17 +0000
@@ -19,68 +19,84 @@
#include "wui/story_message_box.h"
+#include "base/i18n.h"
#include "graphic/graphic.h"
+#include "logic/game_controller.h"
+#include "logic/save_handler.h"
#include "ui_basic/button.h"
#include "ui_basic/multilinetextarea.h"
#include "ui_basic/textarea.h"
-
-/**
- * The message box itself
- */
-StoryMessageBox::StoryMessageBox(UI::Panel* const parent,
+#include "wui/interactive_player.h"
+
+namespace {
+constexpr int kPadding = 4;
+}
+
+StoryMessageBox::StoryMessageBox(Widelands::Game* game,
+ const Widelands::Coords coords,
const std::string& title,
const std::string& body,
- const std::string& button_text,
- int32_t const gposx,
- int32_t const gposy,
+ int32_t const x,
+ int32_t const y,
uint32_t const w,
uint32_t const h)
- : UI::Window(parent, "story_message_box", 0, 0, 600, 400, title.c_str()) {
- UI::MultilineTextarea* message_text = nullptr;
- int32_t const spacing = 5;
- int32_t offsy = 5;
- int32_t offsx = spacing;
- int32_t posx = offsx;
- int32_t posy = offsy;
-
- set_inner_size(w, h);
- message_text = new UI::MultilineTextarea(
- this, posx, posy, get_inner_w() - posx - spacing, get_inner_h() - posy - 2 * spacing - 50);
-
- if (message_text)
- message_text->set_text(body);
-
- int32_t const but_width = 120;
- int32_t space = get_inner_w() - 2 * spacing;
- space -= but_width;
- space /= 2; // center button
- posx = spacing;
- posy = get_inner_h() - 30;
- posx += space;
- UI::Button* okbtn = new UI::Button(this, "ok", posx, posy, but_width, 20,
- g_gr->images().get("images/ui_basic/but5.png"), button_text);
- okbtn->sigclicked.connect(boost::bind(&StoryMessageBox::clicked_ok, boost::ref(*this)));
-
- center_to_parent();
-
- if (gposx != -1)
- set_pos(Vector2i(gposx, get_y()));
- if (gposy != -1)
- set_pos(Vector2i(get_x(), gposy));
-
+ : UI::Window(game->get_ipl(), "story_message_box", x, y, w, h, title.c_str()),
+ main_box_(this, kPadding, kPadding, UI::Box::Vertical, 0, 0, kPadding),
+ button_box_(&main_box_, kPadding, kPadding, UI::Box::Horizontal, 0, 0, kPadding),
+ textarea_(&main_box_, 0, 0, 100, 100, ""),
+ ok_(&button_box_, "ok", 0, 0, 120, 0, g_gr->images().get("images/ui_basic/but5.png"), _("OK")),
+ desired_speed_(game->game_controller()->desired_speed()),
+ game_(game) {
+
+ // Pause the game
+ game_->game_controller()->set_desired_speed(0);
+ game_->save_handler().set_allow_saving(false);
+
+ // Adjust map view
+ if (coords != Widelands::Coords::null()) {
+ game_->get_ipl()->map_view()->scroll_to_field(coords, MapView::Transition::Jump);
+ }
+
+ // TODO(GunChleoc): When all campaigns and scenarios have been converted, simply add the body text above.
+ try {
+ textarea_.force_new_renderer();
+ textarea_.set_text(body);
+ log("Story Message Box: using NEW font renderer.\n");
+ } catch (const std::exception& e) {
+ log("Story Message Box: falling back to OLD font renderer:\n%s\n%s\n", body.c_str(), e.what());
+ textarea_.force_new_renderer(false);
+ textarea_.set_text(body);
+ }
+
+
+ // Add and configure the panels
+ main_box_.set_size(get_inner_w() - 3 * kPadding, get_inner_h() - 2 * kPadding);
+
+ main_box_.add(&textarea_, UI::Box::Resizing::kExpandBoth);
+ main_box_.add(&button_box_, UI::Box::Resizing::kFullSize);
+ button_box_.add_inf_space();
+ button_box_.add(&ok_);
+ button_box_.add_inf_space();
+
+ ok_.sigclicked.connect(boost::bind(&StoryMessageBox::clicked_ok, boost::ref(*this)));
+
+ if (x == -1 && y == -1) {
+ center_to_parent();
+ }
move_inside_parent();
+ textarea_.focus();
}
-/**
- * Clicked
- */
void StoryMessageBox::clicked_ok() {
+ // Manually force the game to reevaluate its current state, especially time information.
+ game_->game_controller()->think();
+ // Now get the game running again.
+ game_->game_controller()->set_desired_speed(desired_speed_);
+ game_->save_handler().set_allow_saving(true);
+
end_modal<UI::Panel::Returncodes>(UI::Panel::Returncodes::kOk);
}
-/*
- * Avoid being closed by right click
- */
bool StoryMessageBox::handle_mousepress(const uint8_t btn, int32_t mx, int32_t my) {
if (btn == SDL_BUTTON_RIGHT)
return true;
=== modified file 'src/wui/story_message_box.h'
--- src/wui/story_message_box.h 2017-01-25 18:55:59 +0000
+++ src/wui/story_message_box.h 2017-12-09 19:34:17 +0000
@@ -22,25 +22,48 @@
#include <vector>
+#include "logic/game.h"
+#include "ui_basic/box.h"
+#include "ui_basic/button.h"
+#include "ui_basic/multilinetextarea.h"
#include "ui_basic/window.h"
+/**
+ * A message box window with an OK button for use in scenarios.
+ * Closing this window per right-click is blocked.
+ * The game will be paused for the duration that this window is shown.
+ * If 'coords' != Coords::null(), jumps the map view to the specified coordinates.
+ * If 'x' == 'y' == -1, the message box will be centered on screen.
+ */
struct StoryMessageBox : public UI::Window {
- StoryMessageBox(UI::Panel*,
- const std::string&,
- const std::string&,
- const std::string&,
- int32_t gposx,
- int32_t gposy,
+ StoryMessageBox(Widelands::Game* game,
+ const Widelands::Coords coords,
+ const std::string& title,
+ const std::string& body,
+ int32_t x,
+ int32_t y,
uint32_t w,
uint32_t h);
+protected:
+ /// Avoid being closed by right-click.
bool handle_mousepress(uint8_t btn, int32_t mx, int32_t my) override;
- /// Handle keypresses
+ /// Handle keypresses for the OK button.
bool handle_key(bool down, SDL_Keysym code) override;
private:
+ /// Get the game running again and close the window.
void clicked_ok();
+
+ // UI elements
+ UI::Box main_box_;
+ UI::Box button_box_;
+ UI::MultilineTextarea textarea_;
+ UI::Button ok_;
+
+ const uint32_t desired_speed_; // Remember the previous game speed
+ Widelands::Game* game_; // For controlling the game speed
};
#endif // end of include guard: WL_WUI_STORY_MESSAGE_BOX_H
Follow ups