Fix recent list of files menu

This is a problem introduced with the plugin groups, but now we use a
group to store the list of recent files. With this commit we fixed
some bugs in the impl of menu groups.
This commit is contained in:
David Capello 2020-04-08 17:50:17 -03:00
parent 85af3ce735
commit 2af6a0493e
5 changed files with 81 additions and 42 deletions

View File

@ -616,7 +616,7 @@
<item command="OpenFile" text="@.file_open" group="file_open" /> <item command="OpenFile" text="@.file_open" group="file_open" />
<menu text="@.file_open_recent" group="file_recent"> <menu text="@.file_open_recent" group="file_recent">
<item command="ReopenClosedFile" text="@.file_reopen_closed" group="file_recent_reopen" /> <item command="ReopenClosedFile" text="@.file_reopen_closed" group="file_recent_reopen" />
<separator id="recent_files_placeholder" /> <separator id="recent_files_placeholder" group="file_recent_list" />
<separator /> <separator />
<item command="ClearRecentFiles" text="@.file_clear_recent_files" group="file_recent_clear" /> <item command="ClearRecentFiles" text="@.file_clear_recent_files" group="file_recent_clear" />
</menu> </menu>

View File

@ -38,8 +38,12 @@
#include "tinyxml.h" #include "tinyxml.h"
#include <cctype> #include <cctype>
#include <cstdio>
#include <cstring> #include <cstring>
#include <string>
#include <algorithm>
#include <cstdlib>
#define MENUS_TRACE(...) // TRACEARGS
namespace app { namespace app {
@ -61,6 +65,8 @@ const int kUnicodeRight = 0xF703; // NSRightArrowFunctionKey
const int kUnicodeUp = 0xF700; // NSUpArrowFunctionKey const int kUnicodeUp = 0xF700; // NSUpArrowFunctionKey
const int kUnicodeDown = 0xF701; // NSDownArrowFunctionKey const int kUnicodeDown = 0xF701; // NSDownArrowFunctionKey
const char* kFileRecentListGroup = "file_recent_list";
void destroy_instance(AppMenus* instance) void destroy_instance(AppMenus* instance)
{ {
delete instance; delete instance;
@ -320,20 +326,22 @@ AppMenus::~AppMenus()
void AppMenus::reload() void AppMenus::reload()
{ {
MENUS_TRACE("MENUS: AppMenus::reload()");
XmlDocumentRef doc(GuiXml::instance()->doc()); XmlDocumentRef doc(GuiXml::instance()->doc());
TiXmlHandle handle(doc.get()); TiXmlHandle handle(doc.get());
const char* path = GuiXml::instance()->filename(); const char* path = GuiXml::instance()->filename();
//////////////////////////////////////// ////////////////////////////////////////
// Remove all menu items added from scripts so we can re-add them // Remove all menu items added to groups from recent files and
// later in the new menus. // scripts so we can re-add them later in the new menus.
for (auto& it : m_groups) { for (auto& it : m_groups) {
GroupInfo& group = it.second; GroupInfo& group = it.second;
group.end = nullptr; MENUS_TRACE("MENUS: - groups", it.first, "with", group.items.size(), "item(s)");
for (auto& item : group.items) { group.end = nullptr; // This value will be restored later
for (auto& item : group.items)
item->parent()->removeChild(item); item->parent()->removeChild(item);
}
} }
//////////////////////////////////////// ////////////////////////////////////////
@ -379,11 +387,13 @@ void AppMenus::reload()
} }
//////////////////////////////////////// ////////////////////////////////////////
// Re-add menu items from scripts // Re-add menu items in groups (recent files & scripts)
for (auto& it : m_groups) { for (auto& it : m_groups) {
GroupInfo& group = it.second; GroupInfo& group = it.second;
if (group.end) { if (group.end) {
MENUS_TRACE("MENUS: - re-adding group ", it.first, "with", group.items.size(), "item(s)");
auto menu = group.end->parent(); auto menu = group.end->parent();
int insertIndex = menu->getChildIndex(group.end); int insertIndex = menu->getChildIndex(group.end);
for (auto& item : group.items) { for (auto& item : group.items) {
@ -393,8 +403,9 @@ void AppMenus::reload()
} }
// Delete items that don't have a group now // Delete items that don't have a group now
else { else {
MENUS_TRACE("MENUS: - deleting group ", it.first, "with", group.items.size(), "item(s)");
for (auto& item : group.items) for (auto& item : group.items)
delete item; item->deferDelete();
group.items.clear(); group.items.clear();
} }
} }
@ -483,6 +494,8 @@ void AppMenus::initTheme()
bool AppMenus::rebuildRecentList() bool AppMenus::rebuildRecentList()
{ {
MENUS_TRACE("MENUS: AppMenus::rebuildRecentList m_recentFilesPlaceholder=", m_recentFilesPlaceholder);
if (!m_recentFilesPlaceholder) if (!m_recentFilesPlaceholder)
return true; return true;
@ -494,13 +507,10 @@ bool AppMenus::rebuildRecentList()
if (!owner || owner->hasSubmenuOpened()) if (!owner || owner->hasSubmenuOpened())
return false; return false;
int insertIndex = menu->getChildIndex(m_recentFilesPlaceholder)+1;
// Remove active items // Remove active items
while (auto appItem = dynamic_cast<AppMenuItem*>(menu->at(insertIndex))) { for (auto item : m_recentMenuItems)
menu->removeChild(appItem); removeMenuItemFromGroup(item);
appItem->deferDelete(); m_recentMenuItems.clear();
}
Command* openFile = Commands::instance()->byId(CommandId::OpenFile()); Command* openFile = Commands::instance()->byId(CommandId::OpenFile());
@ -517,18 +527,24 @@ bool AppMenus::rebuildRecentList()
for (const auto& fn : files) { for (const auto& fn : files) {
params.set("filename", fn.c_str()); params.set("filename", fn.c_str());
auto menuitem = new AppMenuItem(base::get_file_name(fn).c_str(), std::unique_ptr<AppMenuItem> menuitem(
openFile, params); new AppMenuItem(base::get_file_name(fn).c_str(),
openFile, params));
menuitem->setIsRecentFileItem(true); menuitem->setIsRecentFileItem(true);
menu->insertChild(insertIndex++, menuitem);
m_recentMenuItems.push_back(menuitem.get());
addMenuItemIntoGroup(kFileRecentListGroup, std::move(menuitem));
} }
} }
else { else {
auto menuitem = new AppMenuItem( std::unique_ptr<AppMenuItem> menuitem(
Strings::main_menu_file_no_recent_file(), nullptr); new AppMenuItem(
Strings::main_menu_file_no_recent_file(), nullptr));
menuitem->setIsRecentFileItem(true); menuitem->setIsRecentFileItem(true);
menuitem->setEnabled(false); menuitem->setEnabled(false);
menu->insertChild(insertIndex++, menuitem);
m_recentMenuItems.push_back(menuitem.get());
addMenuItemIntoGroup(kFileRecentListGroup, std::move(menuitem));
} }
// Sync native menus // Sync native menus
@ -546,14 +562,9 @@ bool AppMenus::rebuildRecentList()
} }
void AppMenus::addMenuItemIntoGroup(const std::string& groupId, void AppMenus::addMenuItemIntoGroup(const std::string& groupId,
const std::string& title,
std::unique_ptr<MenuItem>&& menuItem) std::unique_ptr<MenuItem>&& menuItem)
{ {
auto it = m_groups.find(groupId); GroupInfo& group = m_groups[groupId];
if (it == m_groups.end())
return;
GroupInfo& group = it->second;
Widget* menu = group.end->parent(); Widget* menu = group.end->parent();
ASSERT(menu); ASSERT(menu);
int insertIndex = menu->getChildIndex(group.end); int insertIndex = menu->getChildIndex(group.end);
@ -565,25 +576,46 @@ void AppMenus::addMenuItemIntoGroup(const std::string& groupId,
menuItem.release(); menuItem.release();
} }
void AppMenus::removeMenuItemWithCommand(Command* cmd) template<typename Pred>
void AppMenus::removeMenuItemFromGroup(Pred pred)
{ {
for (auto& it : m_groups) { for (auto& it : m_groups) {
GroupInfo& group = it.second; GroupInfo& group = it.second;
group.end = nullptr;
for (auto it=group.items.begin(); it != group.items.end(); ) { for (auto it=group.items.begin(); it != group.items.end(); ) {
auto& item = *it; auto& item = *it;
auto appMenuItem = dynamic_cast<AppMenuItem*>(item); if (pred(item)) {
if (appMenuItem && if (item == group.end)
appMenuItem->getCommand() == cmd) { group.end = group.end->previousSibling();
delete appMenuItem;
item->parent()->removeChild(item);
item->deferDelete();
it = group.items.erase(it); it = group.items.erase(it);
} }
else else {
++it; ++it;
}
} }
} }
} }
void AppMenus::removeMenuItemFromGroup(Command* cmd)
{
removeMenuItemFromGroup(
[cmd](Widget* item){
auto appMenuItem = dynamic_cast<AppMenuItem*>(item);
return (appMenuItem && appMenuItem->getCommand() == cmd);
});
}
void AppMenus::removeMenuItemFromGroup(Widget* menuItem)
{
removeMenuItemFromGroup(
[menuItem](Widget* item){
return (item == menuItem);
});
}
Menu* AppMenus::loadMenuById(TiXmlHandle& handle, const char* id) Menu* AppMenus::loadMenuById(TiXmlHandle& handle, const char* id)
{ {
ASSERT(id != NULL); ASSERT(id != NULL);
@ -642,7 +674,8 @@ Widget* AppMenus::convertXmlelemToMenuitem(TiXmlElement* elem)
m_recentFilesPlaceholder = item; m_recentFilesPlaceholder = item;
} }
} }
if (group) m_groups[group].end = item; if (group)
m_groups[group].end = item;
return item; return item;
} }
@ -674,7 +707,8 @@ Widget* AppMenus::convertXmlelemToMenuitem(TiXmlElement* elem)
if (id) menuitem->setId(id); if (id) menuitem->setId(id);
menuitem->processMnemonicFromText(); menuitem->processMnemonicFromText();
if (group) m_groups[group].end = menuitem; if (group)
m_groups[group].end = menuitem;
// Has it a ID? // Has it a ID?
if (id) { if (id) {

View File

@ -65,12 +65,16 @@ namespace app {
const KeyPtr& key); const KeyPtr& key);
void syncNativeMenuItemKeyShortcuts(); void syncNativeMenuItemKeyShortcuts();
// Menu item handling in groups
void addMenuItemIntoGroup(const std::string& groupId, void addMenuItemIntoGroup(const std::string& groupId,
const std::string& title,
std::unique_ptr<MenuItem>&& menuItem); std::unique_ptr<MenuItem>&& menuItem);
void removeMenuItemWithCommand(Command* cmd); void removeMenuItemFromGroup(Command* cmd);
void removeMenuItemFromGroup(Widget* menuItem);
private: private:
template<typename Pred>
void removeMenuItemFromGroup(Pred pred);
Menu* loadMenuById(TiXmlHandle& handle, const char *id); Menu* loadMenuById(TiXmlHandle& handle, const char *id);
Menu* convertXmlelemToMenu(TiXmlElement* elem); Menu* convertXmlelemToMenu(TiXmlElement* elem);
Widget* convertXmlelemToMenuitem(TiXmlElement* elem); Widget* convertXmlelemToMenuitem(TiXmlElement* elem);
@ -88,7 +92,7 @@ namespace app {
#endif #endif
struct GroupInfo { struct GroupInfo {
Widget* end; Widget* end = nullptr;
WidgetsList items; WidgetsList items;
}; };
@ -107,6 +111,8 @@ namespace app {
std::unique_ptr<Menu> m_inkPopupMenu; std::unique_ptr<Menu> m_inkPopupMenu;
obs::scoped_connection m_recentFilesConn; obs::scoped_connection m_recentFilesConn;
std::vector<Menu*> m_menus; std::vector<Menu*> m_menus;
// List of recent menu items pointing to recent files.
WidgetsList m_recentMenuItems;
// Extension points for plugins (each group is a place where new // Extension points for plugins (each group is a place where new
// menu items can be added). // menu items can be added).
std::map<std::string, GroupInfo> m_groups; std::map<std::string, GroupInfo> m_groups;

View File

@ -645,7 +645,7 @@ void Extension::exitScripts()
if (cmd) { if (cmd) {
#ifdef ENABLE_UI #ifdef ENABLE_UI
// TODO use a signal // TODO use a signal
AppMenus::instance()->removeMenuItemWithCommand(cmd); AppMenus::instance()->removeMenuItemFromGroup(cmd);
#endif // ENABLE_UI #endif // ENABLE_UI
cmds->remove(cmd); cmds->remove(cmd);

View File

@ -134,8 +134,7 @@ int Plugin_newCommand(lua_State* L)
App::instance()->isGui()) { // On CLI menus do not make sense App::instance()->isGui()) { // On CLI menus do not make sense
if (auto appMenus = AppMenus::instance()) { if (auto appMenus = AppMenus::instance()) {
std::unique_ptr<MenuItem> menuItem(new AppMenuItem(title, cmd)); std::unique_ptr<MenuItem> menuItem(new AppMenuItem(title, cmd));
appMenus->addMenuItemIntoGroup( appMenus->addMenuItemIntoGroup(group, std::move(menuItem));
group, title, std::move(menuItem));
} }
} }
#endif // ENABLE_UI #endif // ENABLE_UI