wmww / gtk4-layer-shell

A library to create panels and other desktop components for Wayland using the Layer Shell protocol and GTK4

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Problem with Keyboard Mode

dhansmair opened this issue · comments

Hi! I've been playing with LayerShell in the past days and I have encountered a problem when setting the keyboard mode.
First, this is a minimal code:

from ctypes import CDLL
CDLL('libgtk4-layer-shell.so')

import gi
gi.require_version("Gtk", "4.0")
gi.require_version('Gtk4LayerShell', '1.0')
from gi.repository import Gtk, Gtk4LayerShell as LayerShell

def on_activate(app):
    win = Gtk.ApplicationWindow(application=app)

    LayerShell.init_for_window(win)
    LayerShell.set_layer(win, LayerShell.Layer.TOP)
    LayerShell.auto_exclusive_zone_enable(win)
    LayerShell.set_anchor(win, LayerShell.Edge.TOP, True)
    LayerShell.set_keyboard_mode(win, LayerShell.KeyboardMode.NONE)
    
    root = Gtk.Box()
    win.set_child(root)
    button = Gtk.Button(label="click me")
    root.append(button)
    def close(*args):
        app.quit()
    popover = Gtk.Popover()
    box = Gtk.Box()
    input = Gtk.Text(placeholder_text='placeholder text')
    button2 = Gtk.Button(label='close')
    button2.connect('clicked', close)
    box.append(input)
    box.append(button2)
    popover.set_child(box)
    popover.set_parent(button)

    def clicked(*args):
        popover.popup()
    button.connect('clicked', clicked)

    win.present()
    
app = Gtk.Application(application_id='org.gtk.Example')
app.connect('activate', on_activate)
app.run(None)

So my plan is to create a panel that contains a button. When the button gets clicked, a popover window with an input field should open (for an application menu with a search field).

The problem on hyprland is:

  • with KeyboardMode.NONE, the input cannot receive focus
  • with KeyboardMode.EXCLUSIVE, no other window can receive focus
  • with KeyboardMode.ON_DEMAND, it is the same as with EXCLUSIVE, even though I expected that to work.

On the other hand, when running the same code on plasma wayland, setting the keyboard mode has no effect at all, meaning I can focus any window, as well as typing in the text field, regardless of the mode.

could you give me a hint on how to solve this problem?

Sounds like on-demand keyboard mode isn't supported by your compositor. you could try to set mode to exclusive when the input field is selected and set it back to none when it's deselected, or get your compositor to fully support layer shell v4. Not something I can fix in the library, or something I'm going to try to work around here.

Thank you for the suggestion, I was able to work around ON_DEMAND:

from ctypes import CDLL
CDLL('libgtk4-layer-shell.so')
import gi
gi.require_version("Gtk", "4.0")
gi.require_version('Gtk4LayerShell', '1.0')
from gi.repository import Gtk, GLib, Gtk4LayerShell as LayerShell


def on_activate(app):
    win = Gtk.ApplicationWindow(application=app)
    LayerShell.init_for_window(win)
    LayerShell.set_layer(win, LayerShell.Layer.TOP)
    LayerShell.auto_exclusive_zone_enable(win)
    LayerShell.set_anchor(win, LayerShell.Edge.TOP, True)
    LayerShell.set_anchor(win, LayerShell.Edge.LEFT, True)
    LayerShell.set_anchor(win, LayerShell.Edge.RIGHT, True)
    
    root = Gtk.Box()
    win.set_child(root)
    button = Gtk.Button(icon_name='system-search-symbolic')
    root.append(button)
    popover = Gtk.Popover(child=Gtk.Text())
    popover.set_parent(button)

    def clicked_cb(*args):
        print("setting mode to EXCLUSIVE")
        LayerShell.set_keyboard_mode(win, LayerShell.KeyboardMode.EXCLUSIVE)
        GLib.idle_add(popover.popup) # need to do this instead of popover.popup()
        
    def close_cb(*args):
        print("setting mode to NONE")
        LayerShell.set_keyboard_mode(win, LayerShell.KeyboardMode.NONE)
        
    button.connect('clicked', clicked_cb)
    popover.connect('closed', close_cb)
    win.present()
    

app = Gtk.Application(application_id='org.gtk.Example')
app.connect('activate', on_activate)
app.run(None)