From d37f00d3dc3ee2c042935ccc34fbf33bd4fcb1ec Mon Sep 17 00:00:00 2001 From: codereader Date: Wed, 4 Jan 2017 19:13:32 +0100 Subject: [PATCH] Add event handler to refresh menus on demand --- plugins/uimanager/menu/MenuBar.cpp | 33 ++++++++++++++++++++++-- plugins/uimanager/menu/MenuBar.h | 6 +++-- plugins/uimanager/menu/MenuElement.cpp | 31 +++++++++++++++++++--- plugins/uimanager/menu/MenuElement.h | 30 +++++++++++---------- plugins/uimanager/menu/MenuFolder.cpp | 27 ++++++++++++------- plugins/uimanager/menu/MenuFolder.h | 6 ++++- plugins/uimanager/menu/MenuItem.cpp | 4 +-- plugins/uimanager/menu/MenuItem.h | 5 ++-- plugins/uimanager/menu/MenuManager.cpp | 24 ++++++++++------- plugins/uimanager/menu/MenuManager.h | 4 +-- plugins/uimanager/menu/MenuRootElement.h | 2 +- plugins/uimanager/menu/MenuSeparator.cpp | 6 +++-- plugins/uimanager/menu/MenuSeparator.h | 4 +-- 13 files changed, 127 insertions(+), 55 deletions(-) diff --git a/plugins/uimanager/menu/MenuBar.cpp b/plugins/uimanager/menu/MenuBar.cpp index a4c60deb08..d5d25899f8 100644 --- a/plugins/uimanager/menu/MenuBar.cpp +++ b/plugins/uimanager/menu/MenuBar.cpp @@ -2,6 +2,8 @@ #include +#include "MenuFolder.h" + namespace ui { @@ -13,14 +15,16 @@ wxMenuBar* MenuBar::getWidget() { if (_menuBar == nullptr) { - constructWidget(); + construct(); } return _menuBar; } -void MenuBar::constructWidget() +void MenuBar::construct() { + _needsRefresh = false; + if (_menuBar != nullptr) { MenuElement::constructChildren(); @@ -29,6 +33,18 @@ void MenuBar::constructWidget() _menuBar = new wxMenuBar; + // Set up the listener ensuring that the opened menu is up to date + _menuBar->Bind(wxEVT_MENU_OPEN, [&](wxMenuEvent& ev) + { + MenuElementPtr menu = findMenu(ev.GetMenu()); + + if (menu && menu->needsRefresh() && std::dynamic_pointer_cast(menu)) + { + // Rebuild this entire menu + std::static_pointer_cast(menu)->refresh(); + } + }); + MenuElement::constructChildren(); } @@ -43,4 +59,17 @@ void MenuBar::deconstruct() } } +MenuElementPtr MenuBar::findMenu(wxMenu* menu) +{ + for (const MenuElementPtr& candidate : _children) + { + if (candidate->getWidget() == menu) + { + return candidate; + } + } + + return MenuElementPtr(); +} + } diff --git a/plugins/uimanager/menu/MenuBar.h b/plugins/uimanager/menu/MenuBar.h index fab28f05aa..64fad77584 100644 --- a/plugins/uimanager/menu/MenuBar.h +++ b/plugins/uimanager/menu/MenuBar.h @@ -17,10 +17,12 @@ class MenuBar : virtual wxMenuBar* getWidget() override; +protected: + virtual void construct() override; virtual void deconstruct() override; -protected: - virtual void constructWidget() override; +private: + MenuElementPtr findMenu(wxMenu* menu); }; } diff --git a/plugins/uimanager/menu/MenuElement.cpp b/plugins/uimanager/menu/MenuElement.cpp index 1d137fa54e..ef508985ef 100644 --- a/plugins/uimanager/menu/MenuElement.cpp +++ b/plugins/uimanager/menu/MenuElement.cpp @@ -30,7 +30,8 @@ MenuElement::MenuElement(const MenuElementPtr& parent) : _widget(nullptr), _type(menuNothing), _constructed(false), - _isVisible(true) + _isVisible(true), + _needsRefresh(false) {} MenuElement::~MenuElement() @@ -206,6 +207,7 @@ int MenuElement::getMenuPosition(const MenuElementPtr& child) return -1; // not found or wrong type } +#if 0 wxObject* MenuElement::getWidget() { // Check for toggle, allocate the Gtk::Widget* @@ -216,6 +218,7 @@ wxObject* MenuElement::getWidget() return _widget; } +#endif MenuElementPtr MenuElement::find(const std::string& menuPath) { @@ -252,9 +255,9 @@ MenuElementPtr MenuElement::find(const std::string& menuPath) return MenuElementPtr(); } +#if 0 void MenuElement::construct() { -#if 0 if (_type == menuBar) { wxMenuBar* menuBar = new wxMenuBar; @@ -371,8 +374,8 @@ void MenuElement::construct() } _constructed = true; -#endif } +#endif void MenuElement::connectEvent() { @@ -405,6 +408,16 @@ void MenuElement::disconnectEvent() } } +bool MenuElement::needsRefresh() +{ + return _needsRefresh; +} + +void MenuElement::setNeedsRefresh(bool needsRefresh) +{ + _needsRefresh = needsRefresh; +} + void MenuElement::setWidget(wxObject* object) { // Disconnect the old widget before setting a new one @@ -476,11 +489,21 @@ MenuElementPtr MenuElement::CreateFromNode(const xml::Node& node) return item; } +void MenuElement::setNeedsRefreshRecursively(bool needsRefresh) +{ + setNeedsRefresh(needsRefresh); + + for (const MenuElementPtr& child : _children) + { + child->setNeedsRefreshRecursively(needsRefresh); + } +} + void MenuElement::constructChildren() { for (const MenuElementPtr& child : _children) { - child->constructWidget(); + child->construct(); } } diff --git a/plugins/uimanager/menu/MenuElement.h b/plugins/uimanager/menu/MenuElement.h index a8bc2527ff..f4badb5801 100644 --- a/plugins/uimanager/menu/MenuElement.h +++ b/plugins/uimanager/menu/MenuElement.h @@ -52,6 +52,10 @@ class MenuElement : bool _isVisible; + // Checked if anything in this item or below has changed + // and needs reconstruction the next time the menu is opened + bool _needsRefresh; + static int _nextMenuItemId; public: @@ -116,17 +120,14 @@ class MenuElement : void connectEvent(); void disconnectEvent(); + bool needsRefresh(); + void setNeedsRefresh(bool needsRefresh); + // Use this to get the corresponding wx menu widget out of this item. virtual wxObject* getWidget() = 0; void setWidget(wxObject* object); - // Destroys the wxWidget instantiation of this element and all children - // (which also nullifies the widget pointers). Next time - // the getWidget() method is called the widgets will be reconstructed. - // Subclasses should override this - virtual void deconstruct() = 0; - // Tries to (recursively) locate the MenuElement by looking up the path MenuElementPtr find(const std::string& menuPath); @@ -137,22 +138,23 @@ class MenuElement : static MenuElementPtr CreateFromNode(const xml::Node& node); protected: + void setNeedsRefreshRecursively(bool needsRefresh); + // Instantiates this all current child elements recursively as wxObjects // to be overridden by subclasses - virtual void constructWidget() = 0; + virtual void construct() = 0; + + // Destroys the wxWidget instantiation of this element and all children + // (which also nullifies the widget pointers). Next time + // the getWidget() method is called the widgets will be reconstructed. + // Subclasses should override this + virtual void deconstruct() = 0; // This default implementaton here does as exepected: constructs all children virtual void constructChildren(); // This default implementation passes the deconstruct() call to all children virtual void deconstructChildren(); - -private: - /** greebo: This constructs the actual widgets. This is invoked as soon - * as the first getWidget of this object is requested. - * DEPRECATED - */ - void construct(); }; } // namespace ui diff --git a/plugins/uimanager/menu/MenuFolder.cpp b/plugins/uimanager/menu/MenuFolder.cpp index 2329597c3e..09c6a27ec3 100644 --- a/plugins/uimanager/menu/MenuFolder.cpp +++ b/plugins/uimanager/menu/MenuFolder.cpp @@ -19,13 +19,21 @@ wxMenu* MenuFolder::getWidget() { if (_menu == nullptr) { - constructWidget(); + construct(); } return _menu; } -void MenuFolder::constructWidget() +void MenuFolder::refresh() +{ + deconstructChildren(); + constructChildren(); + + setNeedsRefreshRecursively(false); +} + +void MenuFolder::construct() { if (_menu != nullptr) { @@ -70,7 +78,7 @@ void MenuFolder::deconstruct() { if (_parentItem->GetMenu() != nullptr) { - // This is a submenu, we can just rely on the parent item deleting the _menu object + // This is a submenu, remove the item we're attached to first _parentItem->GetMenu()->Delete(_parentItem); } else @@ -80,9 +88,6 @@ void MenuFolder::deconstruct() } _parentItem = nullptr; - - // Parent item deleted the _menu already in its destructor - _menu = nullptr; } if (_menu != nullptr) @@ -102,11 +107,13 @@ void MenuFolder::deconstruct() } } } - - // Regardless of whether we have a bar attached, we need to delete the menu - delete _menu; - _menu = nullptr; } + + // Regardless what happened before, we need to delete the menu ourselves + // (Any parent item didn't delete the _menu in its destructor + // as wxWidgets nullified the submenu member before deleting it) + delete _menu; + _menu = nullptr; } } diff --git a/plugins/uimanager/menu/MenuFolder.h b/plugins/uimanager/menu/MenuFolder.h index 2ab8c59590..49d631378d 100644 --- a/plugins/uimanager/menu/MenuFolder.h +++ b/plugins/uimanager/menu/MenuFolder.h @@ -21,8 +21,12 @@ class MenuFolder : virtual wxMenu* getWidget() override; + // Empties this menu and rebuilds the wxWidget objects + // Clears the needsRefresh flag on this object and all children + void refresh(); + protected: - virtual void constructWidget() override; + virtual void construct() override; virtual void deconstruct() override; }; diff --git a/plugins/uimanager/menu/MenuItem.cpp b/plugins/uimanager/menu/MenuItem.cpp index ed82a531ec..3a79ce1c12 100644 --- a/plugins/uimanager/menu/MenuItem.cpp +++ b/plugins/uimanager/menu/MenuItem.cpp @@ -17,13 +17,13 @@ wxMenuItem* MenuItem::getWidget() { if (_menuItem == nullptr) { - constructWidget(); + construct(); } return _menuItem; } -void MenuItem::constructWidget() +void MenuItem::construct() { if (_menuItem != nullptr) { diff --git a/plugins/uimanager/menu/MenuItem.h b/plugins/uimanager/menu/MenuItem.h index 3325659b71..736259ffde 100644 --- a/plugins/uimanager/menu/MenuItem.h +++ b/plugins/uimanager/menu/MenuItem.h @@ -18,10 +18,9 @@ class MenuItem : virtual wxMenuItem* getWidget() override; - virtual void deconstruct() override; - protected: - virtual void constructWidget() override; + virtual void construct() override; + virtual void deconstruct() override; }; } diff --git a/plugins/uimanager/menu/MenuManager.cpp b/plugins/uimanager/menu/MenuManager.cpp index 7d84cf709e..23f1c68dc1 100644 --- a/plugins/uimanager/menu/MenuManager.cpp +++ b/plugins/uimanager/menu/MenuManager.cpp @@ -67,12 +67,12 @@ void MenuManager::setVisibility(const std::string& path, bool visible) { element->setIsVisible(visible); - // The corresponding menu should be reconstructed - MenuElementPtr parentMenu = findParentMenu(element); + // The corresponding top level menu needs reconstruction + MenuElementPtr parentMenu = findTopLevelMenu(element); if (parentMenu) { - //parentMenu->deconstruct(); + parentMenu->setNeedsRefresh(true); } } } @@ -418,18 +418,22 @@ void MenuManager::remove(const std::string& path) #endif } -MenuElementPtr MenuManager::findParentMenu(const MenuElementPtr& element) +MenuElementPtr MenuManager::findTopLevelMenu(const MenuElementPtr& element) { - if (!element) return MenuElementPtr(); + MenuElementPtr candidate = element; - MenuElementPtr parent = element->getParent(); - - while (parent) + while (candidate) { - if (std::dynamic_pointer_cast(parent)) + MenuElementPtr parent = candidate->getParent(); + + if (candidate && + std::dynamic_pointer_cast(candidate) && + std::dynamic_pointer_cast(parent)) { - return parent; + return candidate; } + + candidate = parent; } return MenuElementPtr(); diff --git a/plugins/uimanager/menu/MenuManager.h b/plugins/uimanager/menu/MenuManager.h index ea4e052fd0..cec0603bf0 100644 --- a/plugins/uimanager/menu/MenuManager.h +++ b/plugins/uimanager/menu/MenuManager.h @@ -83,8 +83,8 @@ class MenuManager : void clear(); private: - // Returns the parent menu (== MenuFolder) for the given element - MenuElementPtr findParentMenu(const MenuElementPtr& element); + // Returns the top level menu (== MenuFolder) this element is part of + MenuElementPtr findTopLevelMenu(const MenuElementPtr& element); }; } // namespace ui diff --git a/plugins/uimanager/menu/MenuRootElement.h b/plugins/uimanager/menu/MenuRootElement.h index 09f45e2906..f2c17050b6 100644 --- a/plugins/uimanager/menu/MenuRootElement.h +++ b/plugins/uimanager/menu/MenuRootElement.h @@ -20,7 +20,7 @@ class MenuRootElement : } protected: - void constructWidget() override + void construct() override {} void deconstruct() override diff --git a/plugins/uimanager/menu/MenuSeparator.cpp b/plugins/uimanager/menu/MenuSeparator.cpp index cbbcc85718..1fe0fab2a2 100644 --- a/plugins/uimanager/menu/MenuSeparator.cpp +++ b/plugins/uimanager/menu/MenuSeparator.cpp @@ -16,14 +16,16 @@ wxMenuItem* MenuSeparator::getWidget() { if (_separator == nullptr) { - constructWidget(); + construct(); } return _separator; } -void MenuSeparator::constructWidget() +void MenuSeparator::construct() { + _needsRefresh = false; + if (_separator != nullptr) { MenuElement::constructChildren(); diff --git a/plugins/uimanager/menu/MenuSeparator.h b/plugins/uimanager/menu/MenuSeparator.h index 242fa51056..8e92e7ec6d 100644 --- a/plugins/uimanager/menu/MenuSeparator.h +++ b/plugins/uimanager/menu/MenuSeparator.h @@ -16,10 +16,10 @@ class MenuSeparator : MenuSeparator(); virtual wxMenuItem* getWidget() override; - virtual void deconstruct() override; protected: - virtual void constructWidget() override; + virtual void construct() override; + virtual void deconstruct() override; }; }