Here's a first attempt at a patch for the back button for story screens:

http://wiki.wesnoth.org/Good_Ideas
http://www.wesnoth.org/forum/viewtopic.php?t=8857

There is already a patch in the tracker for this:

https://gna.org/patch/?1536

However I didn't realise someone had started when I wrote this, and
this is rather further along than that one is.

Problems with that patch:

* Back doesn't work properly (try it on the last page of the intro
where it shows you the map) - can't go back.
* Doesn't wire up back button in all the situations next button is clickable.
* Probably violates screen size in USE_TINY_GUI mode
* There's no key shortcut for back.

Features this patch has:

* Handles back properly so that you can go all the way back to the
start from the end.
* Handles skip properly so that all segments are skipped not just per
segment (but can still go back so this is ok!)
* Adds functional quit button.
* Adds key controls: RIGHT is added for next, LEFT and BACKSPACE for back.

Improves the implementation:
* no longer throws quit exception to quit (was just caught and
suppressed anyway).
* play story segments from either end so that back can work properly.
* adds reverse direction iteration for config::const_child_iterator (was needed)
Index: src/storyscreen/render.cpp
===================================================================
--- src/storyscreen/render.cpp	(revision 41922)
+++ src/storyscreen/render.cpp	(working copy)
@@ -81,13 +81,17 @@
 
 namespace storyscreen {
 
-part_ui::part_ui(part& p, display& disp, gui::button& next_button, gui::button& skip_button)
+part_ui::part_ui(part& p, display& disp, 
+		 gui::button& next_button, gui::button& back_button, 
+		 gui::button& skip_button, gui::button& quit_button)
 	: p_(p)
 	, disp_(disp)
 	, video_(disp.video())
 	, keys_()
 	, next_button_(next_button)
+	, back_button_(back_button)
 	, skip_button_(skip_button)
+	, quit_button_(quit_button)
 	, ret_(NEXT)
 	, scale_factor_(1.0)
 	, base_rect_()
@@ -139,8 +143,10 @@
 	buttons_x_ = video_.getx() - 50;
 	buttons_y_ = base_rect_.y + base_rect_.h - 20;
 
-	next_button_.set_location(buttons_x_, buttons_y_ - 20);
-	skip_button_.set_location(buttons_x_, buttons_y_);
+	next_button_.set_location(buttons_x_, buttons_y_ - 60);
+	back_button_.set_location(buttons_x_, buttons_y_ - 40);
+	skip_button_.set_location(buttons_x_, buttons_y_ - 20);
+	quit_button_.set_location(buttons_x_, buttons_y_);
 
 #else // elif !defined(USE_TINY_GUI)
 
@@ -163,12 +169,16 @@
 		buttons_y_ = video_.gety() - 40;
 		break;
 	}
-	next_button_.set_location(buttons_x_, buttons_y_ - 30);
-	skip_button_.set_location(buttons_x_, buttons_y_);
+	next_button_.set_location(buttons_x_, buttons_y_ - 90);
+	back_button_.set_location(buttons_x_, buttons_y_ - 60);
+	skip_button_.set_location(buttons_x_, buttons_y_ - 30);
+	quit_button_.set_location(buttons_x_, buttons_y_);
 #endif
 
     next_button_.set_volatile(true);
+    back_button_.set_volatile(true);
     skip_button_.set_volatile(true);
+    quit_button_.set_volatile(true);
 }
 
 void part_ui::prepare_floating_images()
@@ -210,10 +220,18 @@
 					ret_ = SKIP;
 					return false;
 				}
+				else if(quit_button_.pressed()) {
+					ret_ = QUIT;
+					return false;
+				}
 				else if(next_button_.pressed()) {
 					ret_ = NEXT;
 					return false;
 				}
+				else if(back_button_.pressed()) {
+					ret_ = BACK;
+					return false;
+				}
 
 				disp_.delay(fi.display_delay() / 50);
 
@@ -238,7 +256,9 @@
 			}
 		}
 
-		if(keys_[SDLK_ESCAPE] || next_button_.pressed() || skip_button_.pressed()) {
+		if(keys_[SDLK_ESCAPE] || 
+		   next_button_.pressed() || back_button_.pressed() || 
+		   skip_button_.pressed() || quit_button_.pressed()) {
 			skip = true;
 			++fi_n;
 			continue;
@@ -429,10 +449,10 @@
 		// by the buttons being hidden and unhidden in this scope.
 		update_locker locker(video_);
 
-		//back_button_.hide();
 		next_button_.hide();
+		back_button_.hide();
 		skip_button_.hide();
-		//quit_button_.hide();
+		quit_button_.hide();
 
 #ifndef LOW_MEM
 		blur_area(video_, fix_text_y, fix_text_h);
@@ -450,8 +470,12 @@
 		// Make GUI1 buttons aware of background modifications
 		next_button_.set_location(next_button_.location());
 		next_button_.hide(false);
+		back_button_.set_location(back_button_.location());
+		back_button_.hide(false);
 		skip_button_.set_location(skip_button_.location());
 		skip_button_.hide(false);
+		quit_button_.set_location(quit_button_.location());
+		quit_button_.hide(false);
 	}
 
 	if(imgs_.empty()) {
@@ -479,9 +503,9 @@
 			++scan.y;
 		}
 
-		const bool keydown = keys_[SDLK_SPACE] || keys_[SDLK_RETURN] || keys_[SDLK_KP_ENTER];
+		const bool next_keydown = keys_[SDLK_SPACE] || keys_[SDLK_RETURN] || keys_[SDLK_KP_ENTER] || keys_[SDLK_RIGHT];
 
-		if((keydown && !last_key) || next_button_.pressed()) {
+		if((next_keydown && !last_key) || next_button_.pressed()) {
 			if(skip == true || scan_finished) {
 				ret_ = NEXT;
 				break;
@@ -490,11 +514,22 @@
 			}
 		}
 
-		last_key = keydown;
+		const bool back_keydown = keys_[SDLK_BACKSPACE] || keys_[SDLK_LEFT];
 
+		if((back_keydown && !last_key) || back_button_.pressed()) {
+			ret_ = BACK;
+			break;
+		}
+
+		last_key = next_keydown || back_keydown;
+
 		if(keys_[SDLK_ESCAPE] || skip_button_.pressed()) {
 			ret_ = SKIP;
 			return;
+		} 
+		else if(quit_button_.pressed()) {
+			ret_ = QUIT;
+			return;
 		}
 
 		events::pump();
@@ -517,20 +552,32 @@
 {
 	bool last_key = true;
 	while(true) {
-		const bool keydown = keys_[SDLK_SPACE] || keys_[SDLK_RETURN] || keys_[SDLK_KP_ENTER];
+		const bool next_keydown = keys_[SDLK_SPACE] || keys_[SDLK_RETURN] || keys_[SDLK_KP_ENTER] || keys_[SDLK_RIGHT];
 
-		if((keydown && !last_key) || next_button_.pressed()) {
+		if((next_keydown && !last_key) || next_button_.pressed()) {
 			ret_ = NEXT;
 			break;
 		}
 
-		last_key = keydown;
+		const bool back_keydown = keys_[SDLK_BACKSPACE] || keys_[SDLK_LEFT];
 
+		if((back_keydown && !last_key) || back_button_.pressed()) {
+			ret_ = BACK;
+			break;
+		}
+
+		last_key = next_keydown || back_keydown;
+
 		if(keys_[SDLK_ESCAPE] || skip_button_.pressed()) {
 			ret_ = SKIP;
 			return;
 		}
 
+		if(quit_button_.pressed()) {
+			ret_ = QUIT;
+			return;
+		}
+
 		events::pump();
 		events::raise_process_event();
 		events::raise_draw_event();
Index: src/storyscreen/interface.cpp
===================================================================
--- src/storyscreen/interface.cpp	(revision 41922)
+++ src/storyscreen/interface.cpp	(working copy)
@@ -47,20 +47,19 @@
 	}
 } // end anonymous namespace
 
-void show_storyscreen(display& disp, const vconfig& story_cfg, const std::string& scenario_name)
+storyscreen::STORY_RESULT show_storyscreen(display& disp, const vconfig& story_cfg, 
+					   const std::string& scenario_name,
+					   storyscreen::START_POSITION startpos)
 {
 	LOG_NG << "entering storyscreen procedure...\n";
 
 	storyscreen::controller ctl(disp, story_cfg, scenario_name);
 
-	try {
-		ctl.show();
-	} catch(storyscreen::controller::quit const&) {
-		LOG_NG << "leaving storyscreen for titlescreen...\n";
-		STUB();
-	}
+	storyscreen::STORY_RESULT ret = ctl.show(startpos);
 
 	LOG_NG << "leaving storyscreen procedure...\n";
+
+	return ret;
 }
 
 void show_endscreen(display& /*disp*/, const t_string& /*text*/, unsigned int /*duration*/)
Index: src/storyscreen/controller.cpp
===================================================================
--- src/storyscreen/controller.cpp	(revision 41922)
+++ src/storyscreen/controller.cpp	(working copy)
@@ -118,17 +118,17 @@
 	}
 }
 
-void controller::show()
+STORY_RESULT controller::show(START_POSITION startpos)
 {
 	if(parts_.empty()) {
 		LOG_NG << "no storyscreen parts to show\n";
-		return;
+		return NEXT;
 	}
 
 	gui::button next_button(disp_.video(),_("Next") + std::string(" >"));
+	gui::button back_button(disp_.video(),std::string("< ")+_("Back"));
 	gui::button skip_button(disp_.video(),_("Skip"));
-	// TODO:
-	//  gui::button back_button(disp_.video(),std::string("< ")+_("Next"));
+	gui::button quit_button(disp_.video(),_("Quit"));
 
 	// Build renderer cache unless built for a low-memory environment;
 	// caching the scaled backgrounds can take over a decent amount of memory.
@@ -136,18 +136,28 @@
 	std::vector< render_pointer_type > uis_;
 	foreach(part_pointer_type p, parts_) {
 		ASSERT_LOG( p != NULL, "Ouch: hit NULL storyscreen part in collection" );
-		render_pointer_type const rpt(new part_ui(*p, disp_, next_button, skip_button));
+		render_pointer_type const rpt(new part_ui(*p, disp_, next_button, back_button, skip_button, quit_button));
 		uis_.push_back(rpt);
 	}
 #endif
 
 	size_t k = 0;
+	switch(startpos) {
+	case START_BEGINNING:
+		break;
+	case START_END:
+		k = parts_.size() -1;
+		break;
+	default:
+		assert(false);
+		break;
+	}
 
 	while(k < parts_.size()) {
 #ifndef LOW_MEM
 		render_reference_type render_interface = *uis_[k];
 #else
-		render_value_type render_interface(*parts_[k], disp_, next_button, skip_button);
+		render_value_type render_interface(*parts_[k], disp_, next_button, back_button, skip_button, quit_button);
 #endif
 
 		LOG_NG << "displaying storyscreen part " << k+1 << " of " << parts_.size() << '\n';
@@ -157,18 +167,23 @@
 			++k;
 			break;
 		case part_ui::BACK:
-			// If we are at the first page we can't go back.
 			if(k > 0) {
 				--k;
+			} 
+			else {
+				return BACK;
 			}
 			break;
 		case part_ui::SKIP:
-			k = parts_.size();
-			break;
+			return SKIP;
+		case part_ui::QUIT:
+			return QUIT;
 		default:
-			throw quit();
+			assert(false);
+			return QUIT;
 		}
 	}
+	return NEXT;
 }
 
 } // end namespace storyscreen
Index: src/storyscreen/render.hpp
===================================================================
--- src/storyscreen/render.hpp	(revision 41922)
+++ src/storyscreen/render.hpp	(working copy)
@@ -45,9 +45,9 @@
 	/** Storyscreen result. */
 	enum RESULT {
 		NEXT,	/**< The user pressed the go-next button. */
-		BACK,	/**< The user pressed the go-back button (TODO: not implemented). */
+		BACK,	/**< The user pressed the go-back button. */
 		SKIP,	/**< The user pressed the skip button. */
-		QUIT	/**< The user pressed the quit button (TODO: not implemented). */
+		QUIT	/**< The user pressed the quit button. */
 	};
 
 	/**
@@ -57,7 +57,9 @@
 	 * @param next_button Next button. Shouldn't be destroyed before the part_ui object.
 	 * @param skip_button Skip button. Shouldn't be destroyed before the part_ui object.
 	 */
-	part_ui(part& p, display& disp, gui::button& next_button, gui::button& skip_button);
+        part_ui(part& p, display& disp, 
+                gui::button& next_button, gui::button& back_button, 
+                gui::button& skip_button, gui::button& quit_button);
 
 	/**
 	 * Render and display the storyscreen, process and return user input.
@@ -70,10 +72,10 @@
 	CVideo& video_; // convenience, it's currently obtained from disp_
 	CKey keys_;     // convenience
 
-	//gui::button& back_button_;
 	gui::button& next_button_;
+	gui::button& back_button_;
 	gui::button& skip_button_;
-	//gui::button& quit_button_;
+	gui::button& quit_button_;
 
 	RESULT ret_;
 
Index: src/storyscreen/interface.hpp
===================================================================
--- src/storyscreen/interface.hpp	(revision 41922)
+++ src/storyscreen/interface.hpp	(working copy)
@@ -27,6 +27,22 @@
 
 #include <string>
 
+namespace storyscreen {
+
+enum STORY_RESULT {
+	NEXT,
+	BACK,
+	SKIP,
+	QUIT
+};
+
+enum START_POSITION {
+	START_BEGINNING,
+	START_END
+};
+
+} /* storyscreen namespace */
+
 /**
  * Function to show an introduction sequence using story WML.
  * The WML config data (story_cfg) has a format similar to:
@@ -41,8 +57,11 @@
  * storyline,and 'img' is a background image. Each part of the sequence will
  * be displayed in turn, with the user able to go to the next part, or skip
  * it entirely.
+ * @return is NEXT if the story played to the end, BACK if the story played to the beginning, and QUIT if the story was quit
  */
-void show_storyscreen(display& disp, const vconfig& story_cfg, const std::string& scenario_name);
+storyscreen::STORY_RESULT show_storyscreen(display& disp, const vconfig& story_cfg, 
+					   const std::string& scenario_name,
+					   storyscreen::START_POSITION startpos);
 
 /**
  * Displays a simple fading screen with any user-provided text.
Index: src/storyscreen/controller.hpp
===================================================================
--- src/storyscreen/controller.hpp	(revision 41922)
+++ src/storyscreen/controller.hpp	(working copy)
@@ -21,6 +21,7 @@
 #define STORYSCREEN_CONTROLLER_HPP_INCLUDED
 
 #include "events.hpp"
+#include "interface.hpp"
 #include "variable.hpp"
 #include "video.hpp"
 
@@ -44,7 +45,7 @@
 	/**
 	 * Display all story screen parts in a first..last sequence.
 	 */
-	void show();
+	STORY_RESULT show(START_POSITION startpos=START_BEGINNING);
 
 private:
 	typedef boost::shared_ptr< part    > part_pointer_type;
@@ -73,8 +74,6 @@
 
 public:
 	struct no_parts {};
-	struct quit {};
-
 };
 
 } // end namespace storyscreen
Index: src/playsingle_controller.cpp
===================================================================
--- src/playsingle_controller.cpp	(revision 41922)
+++ src/playsingle_controller.cpp	(working copy)
@@ -286,8 +286,35 @@
 	sound::commit_music_changes();
 
 	if(!skip_replay) {
-		foreach (const config &s, story) {
-			show_storyscreen(*gui_, vconfig(s, true), level_["name"]);
+		config::const_child_iterator itor = story.first;
+		storyscreen::START_POSITION startpos = storyscreen::START_BEGINNING;
+		while(itor != story.second) {
+			storyscreen::STORY_RESULT result = show_storyscreen(*gui_, vconfig(*itor, true), level_["name"], startpos);
+			switch(result) {
+			case storyscreen::NEXT:
+				if(itor != story.second) {
+					++itor;
+					startpos = storyscreen::START_BEGINNING;
+				}
+				break;
+			case storyscreen::BACK:
+				if(itor != story.first) {
+					--itor;
+					startpos = storyscreen::START_END;
+				}
+				break;
+			case storyscreen::SKIP:
+				itor = story.second;
+				--itor;
+				startpos = storyscreen::START_END;
+				break;
+			case storyscreen::QUIT:
+				return QUIT;
+			default:
+				assert(false);
+				itor = story.second;
+				break;
+			}
 		}
 	}
 	gui_->labels().read(level_);
Index: src/config.hpp
===================================================================
--- src/config.hpp	(revision 41922)
+++ src/config.hpp	(working copy)
@@ -131,6 +131,8 @@
 
 		const_child_iterator &operator++() { ++i_; return *this; }
 		const_child_iterator operator++(int) { return const_child_iterator(i_++); }
+		const_child_iterator &operator--() { --i_; return *this; }
+		const_child_iterator operator--(int) { return const_child_iterator(i_--); }
 
 		const config &operator*() const { return **i_; }
 		const config *operator->() const { return &**i_; }
Index: changelog
===================================================================
--- changelog	(revision 41922)
+++ changelog	(working copy)
@@ -26,6 +26,7 @@
    * Fixed picking the proper locale, the problem only occurred on some
      systems
    * Added a way to compile wesnoth on windows by using CMake + MSVC9.
+   * Added back and quit buttons to the story screens.
 
 Version 1.8.0:
  * AI:
_______________________________________________
Wesnoth-dev mailing list
Wesnoth-dev@gna.org
https://mail.gna.org/listinfo/wesnoth-dev

Reply via email to