← Back to team overview

widelands-dev team mailing list archive

[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