mirror of https://github.com/KLayout/klayout.git
Added methods to manipulate key bindings and menu visibility through scripts (MainWindow#set_key_bindings, MainWindow#get_key_bindings, MainWindow#set_menu_items_hidden ...)
This commit is contained in:
parent
6a23769387
commit
c7113b8c72
|
|
@ -23,6 +23,7 @@
|
|||
#include "gsiDecl.h"
|
||||
#include "gsiSignals.h"
|
||||
#include "layMainWindow.h"
|
||||
#include "layConfig.h"
|
||||
|
||||
#if defined(HAVE_QTBINDINGS)
|
||||
# include "gsiQtGuiExternals.h"
|
||||
|
|
@ -230,12 +231,99 @@ get_config_names (lay::MainWindow *mw)
|
|||
return names;
|
||||
}
|
||||
|
||||
void
|
||||
static void
|
||||
config_end (lay::MainWindow *mw)
|
||||
{
|
||||
mw->dispatcher ()->config_end ();
|
||||
}
|
||||
|
||||
static void
|
||||
set_key_bindings (lay::MainWindow *mw, const std::map<std::string, std::string> &bindings)
|
||||
{
|
||||
std::map<std::string, std::string> b = mw->menu ()->get_shortcuts (false);
|
||||
std::map<std::string, std::string> b_def = mw->menu ()->get_shortcuts (true);
|
||||
|
||||
for (std::map<std::string, std::string>::const_iterator i = bindings.begin (); i != bindings.end (); ++i) {
|
||||
b[i->first] = i->second;
|
||||
}
|
||||
|
||||
// cfg_key_bindings needs a special notation: lay::Action::no_shortcut () to force "none" instead of default
|
||||
// and and empty string to restore default.
|
||||
for (std::map<std::string, std::string>::iterator i = b.begin (); i != b.end (); ++i) {
|
||||
std::map<std::string, std::string>::const_iterator j = b_def.find (i->first);
|
||||
if (j != b_def.end ()) {
|
||||
if (i->second != j->second) {
|
||||
if (i->second.empty ()) {
|
||||
i->second = lay::Action::no_shortcut ();
|
||||
}
|
||||
} else {
|
||||
i->second.clear ();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<std::pair<std::string, std::string> > bv (b.begin (), b.end ());
|
||||
mw->dispatcher ()->config_set (lay::cfg_key_bindings, lay::pack_key_binding (bv));
|
||||
}
|
||||
|
||||
static std::map<std::string, std::string>
|
||||
get_key_bindings (lay::MainWindow *mw)
|
||||
{
|
||||
return mw->menu ()->get_shortcuts (false);
|
||||
}
|
||||
|
||||
static std::map<std::string, std::string>
|
||||
get_default_key_bindings (lay::MainWindow *mw)
|
||||
{
|
||||
return mw->menu ()->get_shortcuts (true);
|
||||
}
|
||||
|
||||
|
||||
static std::map<std::string, bool>
|
||||
get_menu_items_hidden (lay::MainWindow *mw)
|
||||
{
|
||||
std::map<std::string, std::string> kb = get_key_bindings (mw);
|
||||
std::map<std::string, bool> h;
|
||||
|
||||
if (mw->dispatcher ()->menu ()) {
|
||||
for (std::map<std::string, std::string>::const_iterator i = kb.begin (); i != kb.end (); ++i) {
|
||||
lay::Action *a = mw->dispatcher ()->menu ()->action (i->first);
|
||||
if (a) {
|
||||
h.insert (std::make_pair (i->first, a->is_hidden ()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return h;
|
||||
}
|
||||
|
||||
static std::map<std::string, bool>
|
||||
get_default_menu_items_hidden (lay::MainWindow *mw)
|
||||
{
|
||||
std::map<std::string, std::string> kb = get_key_bindings (mw);
|
||||
|
||||
// currently, all menu items are visible by default
|
||||
std::map<std::string, bool> h;
|
||||
for (std::map<std::string, std::string>::const_iterator i = kb.begin (); i != kb.end (); ++i) {
|
||||
h.insert (std::make_pair (i->first, false));
|
||||
}
|
||||
|
||||
return h;
|
||||
}
|
||||
|
||||
static void
|
||||
set_menu_items_hidden (lay::MainWindow *mw, const std::map<std::string, bool> &hidden)
|
||||
{
|
||||
std::map<std::string, bool> h = get_menu_items_hidden (mw);
|
||||
for (std::map<std::string, bool>::const_iterator i = hidden.begin (); i != hidden.end (); ++i) {
|
||||
h[i->first] = i->second;
|
||||
}
|
||||
|
||||
std::vector<std::pair<std::string, bool> > hv;
|
||||
hv.insert (hv.end (), h.begin (), h.end ());
|
||||
mw->dispatcher ()->config_set (lay::cfg_menu_items_hidden, lay::pack_menu_items_hidden (hv));
|
||||
}
|
||||
|
||||
Class<lay::MainWindow> decl_MainWindow (QT_EXTERNAL_BASE (QMainWindow) "lay", "MainWindow",
|
||||
|
||||
// Dispatcher interface and convenience functions
|
||||
|
|
@ -245,31 +333,121 @@ Class<lay::MainWindow> decl_MainWindow (QT_EXTERNAL_BASE (QMainWindow) "lay", "M
|
|||
) +
|
||||
method_ext ("clear_config", &clear_config,
|
||||
"@brief Clears the configuration parameters\n"
|
||||
"Since version 0.27 this is a convenience method which is equivalent to 'dispatcher().clear_config()'. See \\Dispatcher#clear_config for details."
|
||||
"This method is provided for using MainWindow without an Application object. "
|
||||
"It's a convience method which is equivalent to 'dispatcher().clear_config()'. See \\Dispatcher#clear_config for details.\n"
|
||||
"\n"
|
||||
"This method has been introduced in version 0.27.\n"
|
||||
) +
|
||||
method_ext ("write_config", &write_config, gsi::arg ("file_name"),
|
||||
"@brief Writes configuration to a file\n"
|
||||
"Since version 0.27 this is a convenience method which is equivalent to 'dispatcher().write_config(...)'. See \\Dispatcher#write_config for details."
|
||||
"This method is provided for using MainWindow without an Application object. "
|
||||
"It's a convience method which is equivalent to 'dispatcher().write_config(...)'. See \\Dispatcher#write_config for details.\n"
|
||||
"\n"
|
||||
"This method has been introduced in version 0.27.\n"
|
||||
) +
|
||||
method_ext ("read_config", &read_config, gsi::arg ("file_name"),
|
||||
"@brief Reads the configuration from a file\n"
|
||||
"Since version 0.27 this is a convenience method which is equivalent to 'dispatcher().read_config(...)'. See \\Dispatcher#read_config for details."
|
||||
"This method is provided for using MainWindow without an Application object. "
|
||||
"It's a convience method which is equivalent to 'dispatcher().read_config(...)'. See \\Dispatcher#read_config for details.\n"
|
||||
"\n"
|
||||
"This method has been introduced in version 0.27.\n"
|
||||
) +
|
||||
method_ext ("get_config", &get_config, gsi::arg ("name"),
|
||||
"@brief Gets the value of a local configuration parameter\n"
|
||||
"Since version 0.27 this is a convenience method which is equivalent to 'dispatcher().get_config(...)'. See \\Dispatcher#get_config for details."
|
||||
"This method is provided for using MainWindow without an Application object. "
|
||||
"It's a convience method which is equivalent to 'dispatcher().get_config(...)'. See \\Dispatcher#get_config for details.\n"
|
||||
"\n"
|
||||
"This method has been introduced in version 0.27.\n"
|
||||
) +
|
||||
method_ext ("set_config", &set_config, gsi::arg ("name"), gsi::arg ("value"),
|
||||
"@brief Set a local configuration parameter with the given name to the given value\n"
|
||||
"Since version 0.27 this is a convenience method which is equivalent to 'dispatcher().set_config(...)'. See \\Dispatcher#set_config for details."
|
||||
"This method is provided for using MainWindow without an Application object. "
|
||||
"It's a convience method which is equivalent to 'dispatcher().set_config(...)'. See \\Dispatcher#set_config for details.\n"
|
||||
"\n"
|
||||
"This method has been introduced in version 0.27.\n"
|
||||
) +
|
||||
method_ext ("get_config_names", &get_config_names,
|
||||
"@brief Gets the configuration parameter names\n"
|
||||
"Since version 0.27 this is a convenience method which is equivalent to 'dispatcher().get_config_names(...)'. See \\Dispatcher#get_config_names for details."
|
||||
"This method is provided for using MainWindow without an Application object. "
|
||||
"It's a convience method which is equivalent to 'dispatcher().get_config_names(...)'. See \\Dispatcher#get_config_names for details.\n"
|
||||
"\n"
|
||||
"This method has been introduced in version 0.27.\n"
|
||||
) +
|
||||
method_ext ("commit_config", &config_end,
|
||||
"@brief Commits the configuration settings\n"
|
||||
"Since version 0.27 this is a convenience method which is equivalent to 'dispatcher().config_end(...)'. See \\Dispatcher#config_end for details."
|
||||
"This method is provided for using MainWindow without an Application object. "
|
||||
"It's a convience method which is equivalent to 'dispatcher().config_end(...)'. See \\Dispatcher#config_end for details.\n"
|
||||
"\n"
|
||||
"This method has been introduced in version 0.27.\n"
|
||||
) +
|
||||
|
||||
// key binding configuration
|
||||
gsi::method_ext ("get_key_bindings", &get_key_bindings,
|
||||
"@brief Gets the current key bindings\n"
|
||||
"This method returns a hash with the key binding vs. menu item path.\n"
|
||||
"\n"
|
||||
"This method has been introduced in version 0.27.\n"
|
||||
) +
|
||||
gsi::method_ext ("get_default_key_bindings", &get_default_key_bindings,
|
||||
"@brief Gets the default key bindings\n"
|
||||
"This method returns a hash with the default key binding vs. menu item path.\n"
|
||||
"You can use this hash with \\set_key_bindings to reset all key bindings to the default ones.\n"
|
||||
"\n"
|
||||
"This method has been introduced in version 0.27.\n"
|
||||
) +
|
||||
gsi::method_ext ("set_key_bindings", &set_key_bindings, gsi::arg ("bindings"),
|
||||
"@brief Sets key bindings.\n"
|
||||
"Sets the given key bindings. "
|
||||
"Pass a hash listing the key bindings per menu item paths. Key strings follow the usual notation, e.g. 'Ctrl+A', 'Shift+X' or just 'F2'.\n"
|
||||
"Use an empty value to remove a key binding from a menu entry.\n"
|
||||
"\n"
|
||||
"\\get_key_bindings will give you the current key bindings, \\get_default_key_bindings will give you the default ones.\n"
|
||||
"\n"
|
||||
"Examples:\n"
|
||||
"\n"
|
||||
"@code\n"
|
||||
"# reset all key bindings to default:\n"
|
||||
"mw = RBA::MainWindow.instance()\n"
|
||||
"mw.set_key_bindings(mw.get_default_key_bindings())\n"
|
||||
"\n"
|
||||
"# disable key binding for 'copy':\n"
|
||||
"RBA::MainWindow.instance.set_key_bindings({ \"edit_menu.copy\" => \"\" })\n"
|
||||
"\n"
|
||||
"# configure 'copy' to use Shift+K and 'cut' to use Ctrl+K:\n"
|
||||
"RBA::MainWindow.instance.set_key_bindings({ \"edit_menu.copy\" => \"Shift+K\", \"edit_menu.cut\" => \"Ctrl+K\" })\n"
|
||||
"@/code\n"
|
||||
"\n"
|
||||
"This method has been introduced in version 0.27.\n"
|
||||
) +
|
||||
gsi::method_ext ("get_menu_items_hidden", &get_menu_items_hidden,
|
||||
"@brief Gets the flags indicating whether menu items are hidden\n"
|
||||
"This method returns a hash with the hidden flag vs. menu item path.\n"
|
||||
"You can use this hash with \\set_menu_items_hidden.\n"
|
||||
"\n"
|
||||
"This method has been introduced in version 0.27.\n"
|
||||
) +
|
||||
gsi::method_ext ("get_default_menu_items_hidden", &get_default_menu_items_hidden,
|
||||
"@brief Gets the flags indicating whether menu items are hidden by default\n"
|
||||
"You can use this hash with \\set_menu_items_hidden to restore the visibility of all menu items.\n"
|
||||
"\n"
|
||||
"This method has been introduced in version 0.27.\n"
|
||||
) +
|
||||
gsi::method_ext ("set_menu_items_hidden", &set_menu_items_hidden,
|
||||
"@brief sets the flags indicating whether menu items are hidden\n"
|
||||
"This method allows hiding certain menu items. It takes a hash with hidden flags vs. menu item paths. "
|
||||
"\n"
|
||||
"Examples:\n"
|
||||
"\n"
|
||||
"@code\n"
|
||||
"# show all menu items:\n"
|
||||
"mw = RBA::MainWindow.instance()\n"
|
||||
"mw.set_menu_items_hidden(mw.get_default_menu_items_hidden())\n"
|
||||
"\n"
|
||||
"# hide the 'copy' entry from the 'Edit' menu:\n"
|
||||
"RBA::MainWindow.instance().set_menu_items_hidden({ \"edit_menu.copy\" => true })\n"
|
||||
"@/code\n"
|
||||
"\n"
|
||||
"This method has been introduced in version 0.27.\n"
|
||||
) +
|
||||
|
||||
// QMainWindow interface
|
||||
|
|
|
|||
|
|
@ -356,27 +356,6 @@ CustomizeMenuConfigPage::~CustomizeMenuConfigPage ()
|
|||
mp_ui = 0;
|
||||
}
|
||||
|
||||
static void get_shortcuts (const lay::AbstractMenu &menu, const std::string &root, std::map<std::string, std::string> &bindings, bool with_defaults)
|
||||
{
|
||||
std::vector<std::string> items = menu.items (root);
|
||||
for (std::vector<std::string>::const_iterator i = items.begin (); i != items.end (); ++i) {
|
||||
if (i->size () > 0) {
|
||||
if (menu.is_valid (*i) && menu.action (*i)->is_visible ()) {
|
||||
if (menu.is_menu (*i)) {
|
||||
// a menu must be listed (so it can be hidden), but does not have a shortcut
|
||||
// but we don't include special menus
|
||||
if (i->at (0) != '@') {
|
||||
bindings.insert (std::make_pair (*i, std::string ()));
|
||||
}
|
||||
get_shortcuts (menu, *i, bindings, with_defaults);
|
||||
} else if (! menu.is_separator (*i)) {
|
||||
bindings.insert (std::make_pair (*i, with_defaults ? menu.action (*i)->get_default_shortcut () : menu.action (*i)->get_effective_shortcut ()));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
CustomizeMenuConfigPage::reset_clicked ()
|
||||
{
|
||||
|
|
@ -405,12 +384,10 @@ CustomizeMenuConfigPage::apply (const std::vector<std::pair<std::string, std::st
|
|||
m_paths_for_action.clear ();
|
||||
|
||||
// get the current bindings
|
||||
m_current_bindings.clear ();
|
||||
get_shortcuts (*mp_dispatcher->menu (), std::string (), m_current_bindings, false);
|
||||
m_current_bindings = mp_dispatcher->menu ()->get_shortcuts (false);
|
||||
|
||||
// get the default bindings
|
||||
std::map<std::string, std::string> default_bindings;
|
||||
get_shortcuts (*mp_dispatcher->menu (), std::string (), default_bindings, true);
|
||||
std::map<std::string, std::string> default_bindings = mp_dispatcher->menu ()->get_shortcuts (true);
|
||||
|
||||
m_enable_event = false;
|
||||
|
||||
|
|
|
|||
|
|
@ -1677,4 +1677,26 @@ AbstractMenu::collect_configure_actions (std::vector<lay::ConfigureAction *> &ca
|
|||
}
|
||||
}
|
||||
|
||||
void
|
||||
AbstractMenu::get_shortcuts (const std::string &root, std::map<std::string, std::string> &bindings, bool with_defaults)
|
||||
{
|
||||
std::vector<std::string> items = this->items (root);
|
||||
for (std::vector<std::string>::const_iterator i = items.begin (); i != items.end (); ++i) {
|
||||
if (i->size () > 0) {
|
||||
if (is_valid (*i) && action (*i)->is_visible ()) {
|
||||
if (is_menu (*i)) {
|
||||
// a menu must be listed (so it can be hidden), but does not have a shortcut
|
||||
// but we don't include special menus
|
||||
if (i->at (0) != '@') {
|
||||
bindings.insert (std::make_pair (*i, std::string ()));
|
||||
}
|
||||
get_shortcuts (*i, bindings, with_defaults);
|
||||
} else if (! is_separator (*i)) {
|
||||
bindings.insert (std::make_pair (*i, with_defaults ? action (*i)->get_default_shortcut () : action (*i)->get_effective_shortcut ()));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -773,6 +773,18 @@ public:
|
|||
*/
|
||||
QActionGroup *make_exclusive_group (const std::string &name);
|
||||
|
||||
/**
|
||||
* @brief Gets the keyboard shortcuts
|
||||
* @param with_defaults Returns the default shortcuts if true. Otherwise returns the effective shortcut.
|
||||
* @return a hash with menu paths for keys and key binding for values
|
||||
*/
|
||||
std::map<std::string, std::string> get_shortcuts (bool with_defaults)
|
||||
{
|
||||
std::map<std::string, std::string> b;
|
||||
get_shortcuts (std::string (), b, with_defaults);
|
||||
return b;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Gets the root node of the menu
|
||||
*/
|
||||
|
|
@ -798,6 +810,7 @@ private:
|
|||
void collect_group (std::vector<std::string> &grp, const std::string &name, const AbstractMenuItem &item) const;
|
||||
void collect_configure_actions (std::vector<ConfigureAction *> &ca, AbstractMenuItem &item);
|
||||
void emit_changed ();
|
||||
void get_shortcuts (const std::string &root, std::map<std::string, std::string> &bindings, bool with_defaults);
|
||||
|
||||
Dispatcher *mp_dispatcher;
|
||||
AbstractMenuItem m_root;
|
||||
|
|
|
|||
|
|
@ -383,6 +383,42 @@ RESULT
|
|||
|
||||
end
|
||||
|
||||
def test_6
|
||||
|
||||
app = RBA::Application.instance
|
||||
mw = app.main_window
|
||||
|
||||
assert_equal(mw.get_key_bindings["file_menu.exit"], "Ctrl+Q")
|
||||
|
||||
# key bindings
|
||||
|
||||
mw.set_key_bindings({"file_menu.exit" => "F2"})
|
||||
assert_equal(mw.get_key_bindings["file_menu.exit"], "F2")
|
||||
|
||||
mw.set_key_bindings({"file_menu.exit" => ""})
|
||||
assert_equal(mw.get_key_bindings["file_menu.exit"], "")
|
||||
|
||||
mw.set_key_bindings(mw.get_default_key_bindings)
|
||||
assert_equal(mw.get_key_bindings["file_menu.exit"], "Ctrl+Q")
|
||||
|
||||
mw.set_key_bindings({"file_menu.exit" => ""})
|
||||
assert_equal(mw.get_key_bindings["file_menu.exit"], "")
|
||||
|
||||
# menu items hidden
|
||||
|
||||
assert_equal(mw.get_menu_items_hidden["file_menu.exit"], false)
|
||||
|
||||
mw.set_menu_items_hidden({"file_menu.exit" => true})
|
||||
assert_equal(mw.get_menu_items_hidden["file_menu.exit"], true)
|
||||
|
||||
mw.set_menu_items_hidden(mw.get_default_menu_items_hidden)
|
||||
assert_equal(mw.get_menu_items_hidden["file_menu.exit"], false)
|
||||
|
||||
mw.set_menu_items_hidden({"file_menu.exit" => true})
|
||||
assert_equal(mw.get_menu_items_hidden["file_menu.exit"], true)
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
load("test_epilogue.rb")
|
||||
|
|
|
|||
Loading…
Reference in New Issue