Implemented a better keybinding widget - recognizes lone modifiers
as hotkeys (like Gtk3 can).
This commit is contained in:
		
							parent
							
								
									b07f97c35e
								
							
						
					
					
						commit
						3cf26cf96c
					
				@ -36,6 +36,15 @@ import capi
 | 
			
		||||
gdk = CDLL("libgdk-x11-2.0.so.0")
 | 
			
		||||
gtk = CDLL("libgtk-x11-2.0.so.0")
 | 
			
		||||
 | 
			
		||||
SPECIAL_MODS = (["Super_L",    "<Super>"],
 | 
			
		||||
                ["Super_R",    "<Super>"],
 | 
			
		||||
                ["Alt_L",      "<Alt>"],
 | 
			
		||||
                ["Alt_R",      "<Alt>"],
 | 
			
		||||
                ["Control_L",  "<Primary>"],
 | 
			
		||||
                ["Control_R",  "<Primary>"],
 | 
			
		||||
                ["Shift_L",    "<Shift>"],
 | 
			
		||||
                ["Shift_R",    "<Shift>"])
 | 
			
		||||
 | 
			
		||||
class GlobalKeyBinding(GObject.GObject, threading.Thread):
 | 
			
		||||
    __gsignals__ = {
 | 
			
		||||
        'activate': (GObject.SignalFlags.RUN_LAST, None, ()),
 | 
			
		||||
@ -57,6 +66,7 @@ class GlobalKeyBinding(GObject.GObject, threading.Thread):
 | 
			
		||||
    def is_hotkey(self, key, modifier):
 | 
			
		||||
        keymatch = False
 | 
			
		||||
        modmatch = False
 | 
			
		||||
        modifier = modifier & ~Gdk.ModifierType.SUPER_MASK
 | 
			
		||||
        modint = int(modifier)
 | 
			
		||||
        if self.get_keycode(key) == self.keycode:
 | 
			
		||||
            keymatch = True
 | 
			
		||||
@ -72,7 +82,7 @@ class GlobalKeyBinding(GObject.GObject, threading.Thread):
 | 
			
		||||
                         Gdk.ModifierType.SUPER_MASK, Gdk.ModifierType.HYPER_MASK)
 | 
			
		||||
        self.known_modifiers_mask = 0
 | 
			
		||||
        for modifier in gdk_modifiers:
 | 
			
		||||
            if "Mod" not in Gtk.accelerator_name(0, modifier):
 | 
			
		||||
            if "Mod" not in Gtk.accelerator_name(0, modifier) or "Mod4" in Gtk.accelerator_name(0, modifier):
 | 
			
		||||
                self.known_modifiers_mask |= modifier
 | 
			
		||||
 | 
			
		||||
    def get_keycode(self, keyval):
 | 
			
		||||
@ -84,6 +94,7 @@ class GlobalKeyBinding(GObject.GObject, threading.Thread):
 | 
			
		||||
 | 
			
		||||
    def grab(self, key):
 | 
			
		||||
        accelerator = key
 | 
			
		||||
        accelerator = accelerator.replace("<Super>", "<Mod4>")
 | 
			
		||||
        keyval, modifiers = Gtk.accelerator_parse(accelerator)
 | 
			
		||||
        if not accelerator or (not keyval and not modifiers):
 | 
			
		||||
            self.keycode = None
 | 
			
		||||
@ -152,82 +163,51 @@ class KeybindingWidget(Gtk.HBox):
 | 
			
		||||
    __gsignals__ = {
 | 
			
		||||
        'accel-edited': (GObject.SignalFlags.RUN_LAST, None, ()),
 | 
			
		||||
    }
 | 
			
		||||
    def __init__(self, desc):
 | 
			
		||||
    def __init__(self, desc, mainwindow):
 | 
			
		||||
        super(KeybindingWidget, self).__init__()
 | 
			
		||||
        self.desc = desc
 | 
			
		||||
        self.win = mainwindow
 | 
			
		||||
        self.label = Gtk.Label(desc)
 | 
			
		||||
        self.model = Gtk.ListStore(str, object)
 | 
			
		||||
        self.tree = Gtk.TreeView.new()
 | 
			
		||||
        self.tree.set_headers_visible(False)
 | 
			
		||||
        self.cell = Gtk.CellRendererAccel()
 | 
			
		||||
        self.cell.set_alignment(.5, .5)
 | 
			
		||||
        self.change_id = self.cell.connect('accel-edited', self.on_my_value_changed)
 | 
			
		||||
        self.cell.set_property('editable', True)
 | 
			
		||||
        self.cell.set_property('accel-mode', Gtk.CellRendererAccelMode.OTHER)
 | 
			
		||||
        col = Gtk.TreeViewColumn("col", self.cell, text=0)
 | 
			
		||||
        col.set_min_width(200)
 | 
			
		||||
        col.set_alignment(.5)
 | 
			
		||||
        self.tree.append_column(col)
 | 
			
		||||
        self.value = ""
 | 
			
		||||
        self.tree.set_model(self.model)
 | 
			
		||||
        self.model.append((self.value, self))
 | 
			
		||||
        if self.desc != "":
 | 
			
		||||
            self.pack_start(self.label, False, False, 0)
 | 
			
		||||
        shadow = Gtk.Frame()
 | 
			
		||||
        shadow.set_shadow_type(Gtk.ShadowType.ETCHED_IN)
 | 
			
		||||
        shadow.add(self.tree)
 | 
			
		||||
        self.pack_start(shadow, False, False, 2)
 | 
			
		||||
 | 
			
		||||
        self.combomodel = Gtk.ListStore(str, str)
 | 
			
		||||
 | 
			
		||||
        customs = (["Select a single modifier...", "x"],
 | 
			
		||||
                   ["Left Super",    "Super_L"      ],
 | 
			
		||||
                   ["Right Super",   "Super_R"      ],
 | 
			
		||||
                   ["Left Control",  "Control_L"    ],
 | 
			
		||||
                   ["Right Control", "Control_R"    ],
 | 
			
		||||
                   ["Left Alt",      "Alt_L"        ],
 | 
			
		||||
                   ["Right Alt",     "Alt_R"        ])
 | 
			
		||||
 | 
			
		||||
        first = None
 | 
			
		||||
        for option in customs:
 | 
			
		||||
            iter = self.combomodel.insert_before(None, None)
 | 
			
		||||
            self.combomodel.set_value(iter, 0, option[0])
 | 
			
		||||
            self.combomodel.set_value(iter, 1, option[1])
 | 
			
		||||
            if first is None:
 | 
			
		||||
                first = iter
 | 
			
		||||
 | 
			
		||||
        self.combo = Gtk.ComboBox.new_with_model(self.combomodel)   
 | 
			
		||||
        renderer_text = Gtk.CellRendererText()
 | 
			
		||||
        self.combo.pack_start(renderer_text, True)
 | 
			
		||||
        self.combo.add_attribute(renderer_text, "text", 0)
 | 
			
		||||
 | 
			
		||||
        self.combo.set_active_iter(first)
 | 
			
		||||
        self.combo.connect('changed', self.on_combo_changed)
 | 
			
		||||
 | 
			
		||||
        self.pack_start(self.combo, False, False, 0)
 | 
			
		||||
        self.button = Gtk.Button()
 | 
			
		||||
        self.button.set_tooltip_text(_("Click to set a new accelerator key for opening and closing the menu.  ") +
 | 
			
		||||
                                     _("Press Escape to cancel the operation"))
 | 
			
		||||
        self.button.connect("clicked", self.focus_grabbed)
 | 
			
		||||
        self.button.set_size_request(200, -1)
 | 
			
		||||
        self.pack_start(self.button, False, False, 4)
 | 
			
		||||
 | 
			
		||||
        self.show_all()
 | 
			
		||||
        self.event_id = None
 | 
			
		||||
 | 
			
		||||
    def on_combo_changed(self, widget):
 | 
			
		||||
        tree_iter = widget.get_active_iter()
 | 
			
		||||
        if tree_iter != None:
 | 
			
		||||
            value = self.combomodel[tree_iter][1]
 | 
			
		||||
            self.set_val(value)
 | 
			
		||||
            self.emit("accel-edited")
 | 
			
		||||
    def focus_grabbed(self, widget):
 | 
			
		||||
        self.button.set_label(_("Pick an accelerator"))
 | 
			
		||||
        self.event_id = self.win.connect( "key-release-event", self.on_key_release )        
 | 
			
		||||
 | 
			
		||||
    def on_my_value_changed(self, cell, path, keyval, mask, keycode):
 | 
			
		||||
    def on_key_release(self, widget, event):
 | 
			
		||||
        self.win.disconnect(self.event_id)
 | 
			
		||||
        if event.keyval == Gdk.KEY_Escape:
 | 
			
		||||
            self.button.set_label(self.value)            
 | 
			
		||||
            return True
 | 
			
		||||
        gtk.gtk_accelerator_name.restype = c_char_p
 | 
			
		||||
        accel_string = gtk.gtk_accelerator_name(keyval, mask)
 | 
			
		||||
        accel_string = accel_string.replace("<Mod2>", "")
 | 
			
		||||
        accel_string = gtk.gtk_accelerator_name(event.keyval, event.state)
 | 
			
		||||
        accel_string = self.sanitize(accel_string)
 | 
			
		||||
        self.value = accel_string
 | 
			
		||||
        self.emit("accel-edited")
 | 
			
		||||
        self.button.set_label(self.value)
 | 
			
		||||
        return True
 | 
			
		||||
 | 
			
		||||
    def sanitize(self, string):
 | 
			
		||||
        accel_string = string.replace("<Mod2>", "")
 | 
			
		||||
        accel_string = accel_string.replace("<Mod4>", "")
 | 
			
		||||
        for single, mod in SPECIAL_MODS:
 | 
			
		||||
            if single in accel_string and mod in accel_string:
 | 
			
		||||
                accel_string = accel_string.replace(mod, "")
 | 
			
		||||
        return accel_string
 | 
			
		||||
 | 
			
		||||
    def get_val(self):
 | 
			
		||||
        return self.value
 | 
			
		||||
 | 
			
		||||
    def set_val(self, value):
 | 
			
		||||
        self.cell.handler_block(self.change_id)
 | 
			
		||||
        self.value = value
 | 
			
		||||
        self.model.clear()
 | 
			
		||||
        self.model.append((self.value, self))
 | 
			
		||||
        self.cell.handler_unblock(self.change_id)
 | 
			
		||||
        self.button.set_label(value)
 | 
			
		||||
 | 
			
		||||
@ -142,7 +142,7 @@ class mintMenuConfig( object ):
 | 
			
		||||
        self.headingColorLabel = self.builder.get_object( "headingColorLabel" )
 | 
			
		||||
        self.showButtonIcon = self.builder.get_object( "showButtonIcon" )
 | 
			
		||||
        self.buttonText = self.builder.get_object( "buttonText" )
 | 
			
		||||
        self.hotkeyWidget = keybinding.KeybindingWidget(_("Keyboard shortcut:"))
 | 
			
		||||
        self.hotkeyWidget = keybinding.KeybindingWidget(_("Keyboard shortcut:"), self.mainWindow)
 | 
			
		||||
        table = self.builder.get_object( "main_table" )
 | 
			
		||||
        table.attach(self.hotkeyWidget, 0, 2, 2, 3, Gtk.AttachOptions.FILL, Gtk.AttachOptions.FILL, 0, 0)
 | 
			
		||||
        self.buttonIcon = self.builder.get_object( "buttonIcon" )
 | 
			
		||||
 | 
			
		||||
		Loading…
	
		Reference in New Issue
	
	Block a user