diff --git a/src/gui/dialogs/lua_interpreter.cpp b/src/gui/dialogs/lua_interpreter.cpp index cd4877ea1459..34c417e1bd8a 100644 --- a/src/gui/dialogs/lua_interpreter.cpp +++ b/src/gui/dialogs/lua_interpreter.cpp @@ -1,5 +1,5 @@ /* - Copyright (C) 2011 - 2014 by Yurii Chernyi + Copyright (C) 2014 by Chris Beck Part of the Battle for Wesnoth Project http://www.wesnoth.org/ This program is free software; you can redistribute it and/or modify @@ -17,10 +17,12 @@ #include "gui/dialogs/lua_interpreter.hpp" #include "gui/auxiliary/find_widget.tpp" +#include "gui/auxiliary/window_builder.hpp" #include "gui/dialogs/field.hpp" #include "gui/dialogs/helper.hpp" #include "gui/widgets/button.hpp" #include "gui/widgets/label.hpp" +#include "gui/widgets/scroll_label.hpp" #include "gui/widgets/settings.hpp" #include "gui/widgets/text_box.hpp" #include "gui/widgets/window.hpp" @@ -37,11 +39,9 @@ #include "log.hpp" #include "text.hpp" -#include #include #include #include -#include #include #include @@ -51,12 +51,14 @@ static lg::log_domain log_lua_int("lua/interpreter"); #define WRN_LUA LOG_STREAM(warn, log_lua_int) #define ERR_LUA LOG_STREAM(err, log_lua_int) +class CVideo; + namespace gui2 { /*WIKI * @page = GUIWindowDefinitionWML - * @order = 3_chat_log + * @order = 3_lua_interpretter * * == Settings manager == * @@ -67,71 +69,40 @@ namespace gui2 REGISTER_DIALOG(lua_interpreter) -void tlua_interpreter::display(CVideo& video, lua_kernel_base * lk) { - if (!lk) { - ERR_LUA << "Tried to open console with a null lua kernel pointer.\n"; - return; - } +// Model, View, Controller definitions - tlua_interpreter(*lk).show(video); -} +class tlua_interpreter::view { +private: + tscroll_label* msg_label; //the view is extremely simple, it's pretty much just this one widget that gets updated -void tlua_interpreter::display(CVideo& video, tlua_interpreter::WHICH_KERNEL which) { - if (which == tlua_interpreter::APP) { - display(video, resources::app_lua_kernel); - } else if (which == tlua_interpreter::GAME) { - display(video, resources::lua_kernel); +public: + view() : msg_label(NULL) {} + + /** Bind the scroll label widget to my pointer, and configure */ + void bind(twindow& window) { + msg_label = &find_widget(&window, "msg", false); + msg_label->set_use_markup(true); + msg_label->set_vertical_scrollbar_mode(tscrollbar_container::always_visible); + msg_label->set_label(""); } -} -twindow* tlua_interpreter::build_window(CVideo& video) -{ - return build(video, window_id()); -} - - -void tlua_interpreter::bind(twindow& window) -{ - LOG_LUA << "Entering tlua_interpreter::bind" << std::endl; - msg_label = &find_widget(&window, "msg", false); - msg_label->set_use_markup(true); - msg_label->set_vertical_scrollbar_mode(tscrollbar_container::always_visible); - msg_label->set_label(""); - - register_text("text_entry", false, text_entry_, true); - text_entry = &find_widget(&window, "text_entry", false); - //text_entry->set_text_changed_callback( - // boost::bind(&view::filter, this, boost::ref(window))); - window.keyboard_capture(text_entry); - window.set_click_dismiss(false); - window.set_enter_disabled(true); - - connect_signal_pre_key_press( - *text_entry, - boost::bind(&tlua_interpreter::input_keypress_callback, - this, - _3, - _4, - _5, - boost::ref(window))); - - - copy_button = &find_widget(&window, "copy", false); - connect_signal_mouse_left_click( - *copy_button, - boost::bind(&tlua_interpreter::handle_copy_button_clicked, - this, - boost::ref(window))); + /** Update the scroll label contents */ + void update_contents(const std::string & str) + { + assert(msg_label); - if (!desktop::clipboard::available()) { - copy_button->set_active(false); - copy_button->set_tooltip(_("Clipboard support not found, contact your packager.")); + msg_label->set_label(str); + msg_label->scroll_vertical_scrollbar(tscrollbar_::END); } +}; - LOG_LUA << "Exiting tlua_interpreter::bind" << std::endl; -} - - +/** + * The model is responsible to interact with the lua kernel base and keep track of what should be displayed in the console. + * It registers its stringstream with the lua kernel when it is created, and unregisters when it is destroyed. + * + * It is responsible to execute commands as strings, or add dialog messages for the user. It is also responsible to ask + * the lua kernel for help with tab completion. + */ class tlua_interpreter::model { private: lua_kernel_base & L_; @@ -149,7 +120,6 @@ class tlua_interpreter::model { //DBG_LUA << "recieved:\n" << log_.str() << "\n.\n"; DBG_LUA << "finished constructing a tlua_interpreter::model\n"; - } ~model() @@ -158,17 +128,66 @@ class tlua_interpreter::model { L_.set_external_log(NULL); //deregister our log since it's about to be destroyed } - bool execute(const std::string & cmd); // no throw of lua_error. should handle formatting any syntax errors to the interpreter log. + /** Ask the lua kernel to execute a command. No throw of game::lua_error, instead the error message is formatted and printed to console.*/ + bool execute(const std::string & cmd); - void add_dialog_message(const std::string & msg); //formats a message from the dialog, puts in interpreter log but not lua's log + /** Add a message from the dialog, formatted in blue to distinguish from issued commands. + * This message gets put in the interpreter log, but does not get entered in the kernel log, so if the window is closed this message will + * not appear the next time it is opened. + **/ + void add_dialog_message(const std::string & msg); - std::string get_log() const { return log_.str(); } - std::string get_name() const { return L_.my_name(); } + std::string get_log() const { return log_.str(); } //< Get the log string + std::string get_name() const { return L_.my_name(); } //< Get a string describing the name of lua kernel + //* Tab completion: Get list of presently defined global variables */ std::vector get_globals() { return L_.get_global_var_names(); } + //* Tab completion: Get list of attributes for variable corresponding to this path. */ std::vector get_attribute_names(const std::string & s) { return L_.get_attribute_names(s); } }; +/** + * The controller is responsible to hold all the input widgets, and a pointer to the model and view. + * It is responsible to bind the input signals to appropriate handler methods, which it holds. + * It is also responsible to ask the view to update based on the output of the model, typically in + * response to some input. + */ +class tlua_interpreter::controller { +private: + tbutton* copy_button; + + ttext_box* text_entry; + std::string text_entry_; + + boost::scoped_ptr model_; + boost::scoped_ptr view_; +public: + controller(lua_kernel_base & lk) + : copy_button(NULL) + , text_entry(NULL) + , text_entry_() + , model_(new tlua_interpreter::model(lk)) + , view_(new tlua_interpreter::view()) + {} + + /** Bind my pointers to the widgets found in the window */ + void bind(twindow& window); + + void handle_copy_button_clicked(twindow & window); + + void input_keypress_callback(bool& handled, + bool& halt, + const SDLKey key, + twindow& window); + + void update_view(); //< Update the view based on the model + + friend class tlua_interpreter; +}; + +// Model impl + +/** Execute a command, and report any errors encountered. */ bool tlua_interpreter::model::execute (const std::string & cmd) { LOG_LUA << "tlua_interpreter::model::execute...\n"; @@ -181,52 +200,90 @@ bool tlua_interpreter::model::execute (const std::string & cmd) } } +/** Add a dialog message, which will appear in blue. */ void tlua_interpreter::model::add_dialog_message(const std::string & msg) { log_ << "" << font::escape_text(msg) << "\n"; } -tlua_interpreter::tlua_interpreter(lua_kernel_base & lk) - : model_(new tlua_interpreter::model(lk)) - , msg_label(NULL) - , copy_button(NULL) - , text_entry(NULL) +// View impl + +// Controller impl + +/** Update the view based on the model. */ +void tlua_interpreter::controller::update_view() { - LOG_LUA << "entering tlua_interpreter ctor...\n"; - LOG_LUA << "finished tlua_interpreter ctor...\n"; + LOG_LUA << "tlua_interpreter update_view...\n"; + assert(model_); + assert(view_); + + view_->update_contents(model_->get_log()); + + LOG_LUA << "tlua_interpreter update_view finished\n"; } -void tlua_interpreter::update_contents() +/** Find all the widgets managed by the controller and connect them to handler methods. */ +void tlua_interpreter::controller::bind(twindow& window) { - LOG_LUA << "tlua_interpreter update_contents...\n"; - if (msg_label) { - msg_label->set_label(model_->get_log()); - msg_label->scroll_vertical_scrollbar(tscrollbar_::END); + LOG_LUA << "Entering tlua_interpreter::controller::bind" << std::endl; + assert(view_); + view_->bind(window); + + text_entry = &find_widget(&window, "text_entry", false); + //text_entry->set_text_changed_callback( + // boost::bind(&view::filter, this, boost::ref(window))); + window.keyboard_capture(text_entry); + window.set_click_dismiss(false); + window.set_enter_disabled(true); + + connect_signal_pre_key_press( + *text_entry, + boost::bind(&tlua_interpreter::controller::input_keypress_callback, + this, + _3, + _4, + _5, + boost::ref(window))); + + + copy_button = &find_widget(&window, "copy", false); + connect_signal_mouse_left_click( + *copy_button, + boost::bind(&tlua_interpreter::controller::handle_copy_button_clicked, + this, + boost::ref(window))); + + if (!desktop::clipboard::available()) { + copy_button->set_active(false); + copy_button->set_tooltip(_("Clipboard support not found, contact your packager.")); } - LOG_LUA << "tlua_interpreter update_contents finished\n"; + + LOG_LUA << "Exiting tlua_interpreter::controller::bind" << std::endl; } -void tlua_interpreter::handle_copy_button_clicked(twindow & /*window*/) +/** Copy text to the clipboard */ +void tlua_interpreter::controller::handle_copy_button_clicked(twindow & /*window*/) { desktop::clipboard::copy_to_clipboard(model_->get_log(), false); } -void tlua_interpreter::input_keypress_callback(bool& handled, +/** Handle return key (execute) or tab key (tab completion) */ +void tlua_interpreter::controller::input_keypress_callback(bool& handled, bool& halt, const SDLKey key, twindow& /*window*/) { LOG_LUA << "keypress_callback\n"; - if(key == SDLK_RETURN || key == SDLK_KP_ENTER) { + if(key == SDLK_RETURN || key == SDLK_KP_ENTER) { // handle executing whatever is in the command entry field LOG_LUA << "executing...\n"; if (model_->execute(text_entry->get_value())) { text_entry->save_to_history(); } - update_contents(); + update_view(); text_entry->set_value(""); handled = true; halt = true; LOG_LUA << "finished executing\n"; - } else if(key == SDLK_TAB) { + } else if(key == SDLK_TAB) { // handle tab completion std::string text = text_entry->get_value(); std::string prefix; @@ -295,7 +352,7 @@ void tlua_interpreter::input_keypress_callback(bool& handled, } model_->add_dialog_message(buffer); - update_contents(); + update_view(); } text_entry->set_value(prefix + text); handled = true; @@ -303,17 +360,54 @@ void tlua_interpreter::input_keypress_callback(bool& handled, } } +// Dialog implementation + +/** Display a new console, using given video and lua kernel */ +void tlua_interpreter::display(CVideo& video, lua_kernel_base * lk) { + if (!lk) { + ERR_LUA << "Tried to open console with a null lua kernel pointer.\n"; + return; + } + + tlua_interpreter(*lk).show(video); +} + +/** Helper function to assist those callers which don't want to include resources.hpp */ +void tlua_interpreter::display(CVideo& video, tlua_interpreter::WHICH_KERNEL which) { + if (which == tlua_interpreter::APP) { + display(video, resources::app_lua_kernel); + } else if (which == tlua_interpreter::GAME) { + display(video, resources::lua_kernel); + } +} + +/** Call inherited method */ +twindow* tlua_interpreter::build_window(CVideo& video) +{ + return build(video, window_id()); +} + +/** Bind the controller, initialize one of the static labels with info about this kernel, and update the view. */ void tlua_interpreter::pre_show(CVideo& /*video*/, twindow& window) { LOG_LUA << "Entering tlua_interpreter::view::pre_show" << std::endl; - bind(window); + register_text("text_entry", false, controller_->text_entry_, true); + controller_->bind(window); tlabel *kernel_type_label = &find_widget(&window, "kernel_type", false); - kernel_type_label->set_label(model_->get_name()); + kernel_type_label->set_label(controller_->model_->get_name()); - update_contents(); + controller_->update_view(); //window.invalidate_layout(); // workaround for assertion failure LOG_LUA << "Exiting tlua_interpreter::view::pre_show" << std::endl; } +tlua_interpreter::tlua_interpreter(lua_kernel_base & lk) + : controller_(new tlua_interpreter::controller(lk)) +{ + LOG_LUA << "entering tlua_interpreter ctor...\n"; + LOG_LUA << "finished tlua_interpreter ctor...\n"; +} + + } // end of namespace gui2 diff --git a/src/gui/dialogs/lua_interpreter.hpp b/src/gui/dialogs/lua_interpreter.hpp index 85aa867256b8..776da8bb6fad 100644 --- a/src/gui/dialogs/lua_interpreter.hpp +++ b/src/gui/dialogs/lua_interpreter.hpp @@ -16,9 +16,6 @@ #define GUI_DIALOGS_LUA_INT_HPP_INCLUDED #include "gui/dialogs/dialog.hpp" -#include "gui/widgets/button.hpp" -#include "gui/widgets/scroll_label.hpp" -#include "gui/widgets/text_box.hpp" #include @@ -31,6 +28,8 @@ class tlua_interpreter : public tdialog { public: class model; + class view; + class controller; tlua_interpreter(lua_kernel_base & lk); @@ -44,29 +43,12 @@ class tlua_interpreter : public tdialog static void display(CVideo& video, lua_kernel_base * lk); static void display(CVideo& video, WHICH_KERNEL which); private: - boost::scoped_ptr model_; + boost::scoped_ptr controller_; /** Inherited from tdialog, implemented by REGISTER_DIALOG. */ virtual const std::string& window_id() const; - - void bind(twindow& window); - - void update_contents(); - - void handle_copy_button_clicked(twindow & window); - - void input_keypress_callback(bool& handled, - bool& halt, - const SDLKey key, - twindow& window); - - tscroll_label* msg_label; - tbutton* copy_button; - ttext_box* text_entry; - - std::string text_entry_; }; } -#endif /* ! GUI_DIALOGS_CHAT_LOG_HPP_INCLUDED */ +#endif /* ! GUI_DIALOGS_LUA_INT_HPP_INCLUDED */