2

When I assign a Gtk.GestureClick to a Gtk.Scale there's no released signal emitted.

See code for example.

import gi
gi.require_version('Gtk', '4.0')
from gi.repository import Gtk

class Main():
    def on_activate(self, app):
        win = Gtk.ApplicationWindow(application=app)
        gesture = Gtk.GestureClick.new()
        gesture.connect("pressed", self.press)
        gesture.connect("released", self.release)
        scale = Gtk.Scale()
        win.set_child(scale)
        scale.add_controller(gesture)
        win.present()

    def press(self, *_):
        print("pressed")

    def release(self, *_):
        print("released")

app = Gtk.Application(application_id='com.example.GtkApplication')
runner = Main()
app.connect('activate', runner.on_activate)

app.run(None)
swanux
  • 67
  • 7

2 Answers2

2

I actually took a look at the source code for the GTK "GestureClick" widget, and what I found in the code was that the "released" signal is only emitted at the end of the click event (gtk_gesture_click_end). That event is tied to the GTK Gesture widget's "end" signal. So, just as a test I revised the "self.release" function to utilize the widget's inherited "end" event as follows.

gesture.connect("end", self.release)

That then did print out the word "released" on the terminal immediately after printing "pressed", but it does so as a part of the click event and does not wait for the actual release of the mouse button.

You might try out my small code change just to see the effect. I am hesitant to call this a bug as I am still not too experienced with GTK gestures at this point. However, test this out. You possibly may need to just rely on the click event at this time.

Additional Findings.

To be brief, in reviewing other GTK4 click gesture examples, the "press" and "release" gestures both work with other widgets. Apparently, there is some peculiarity with the scale widget. I revised your sample code swapping in a label widget for the scale widget.

import gi
gi.require_version('Gtk', '4.0')
from gi.repository import Gtk

class Main():
    def on_activate(self, app):
        win = Gtk.ApplicationWindow(application=app)
        gesture = Gtk.GestureClick.new()
        gesture.connect("pressed", self.press)
        gesture.connect("released", self.release)
        label = Gtk.Label(label="This is a label widget")
        win.set_child(label)
        win.set_default_size(400, 200)
        label.add_controller(gesture)
        win.present()
        
    def press(self, *_):
        print("pressed")
    
    def release(self, *_):
        print("released")
    
app = Gtk.Application(application_id='com.example.GtkApplication')
runner = Main()
app.connect('activate', runner.on_activate)

app.run(None)

Following is a result of the terminal output for clicking and releasing the mouse button on the label widget.

Press and Release Label

FYI, just to confirm that this issue seems to be associated with the scale widget, I built a similar program in C testing out a scale widget versus other widgets. Again the scale widget would not emit a released signal. At least the scale widget peculiarity is consistent in different languages. So it looks like the bottom line is that scale widgets will not emit a released signal. I hope the extra information helps.

Regards.

NoDakker
  • 3,390
  • 1
  • 10
  • 11
  • Thank you for the answer! You're right, it seems like the `Gtk.Scale` takes avay focus or something, and it instantly emits the `end` signal. Strange. It may or may not be a bug, but in any way so far it looks like I have to look for a workaround. – swanux May 19 '22 at 19:03
  • 1
    FYI, I did a bit more investigation. See my "additional notes" that I appended to my answer. The GTK scale widget appears to have some issue with not emitting the released signal. – NoDakker May 19 '22 at 22:07
  • Thank you for the research! It's good to know that it isn't language related and that it appears to be limited to the scale widget. In the meantime I took a look into other possible solutions (to detect when a scale is grabbed and released) but so far no luck. Hope this will be solved in upstream, if it is a bug indeed. – swanux May 20 '22 at 08:04
0

I have same problem, but i temporary fixed with gtk button. use cancel signal for release. it's working for me.

func (c *Control) topCenter() *gtk.Box {
    boxroot := gtk.NewBox(gtk.OrientationHorizontal, 0)
    boxroot.SetHExpand(true)

    root := gtk.NewOverlay()
    root.SetCSSClasses([]string{"player-control-scale"})
    root.SetHExpand(true)

    c.cacheadjustment = gtk.NewAdjustment(0, 0, 1, 1, 10, 0)
    c.cacheprocess = gtk.NewScale(gtk.OrientationHorizontal, c.cacheadjustment)
    c.cacheprocess.AddCSSClass("timescalebuffer")
    root.SetChild(c.cacheprocess)

    c.timeadjustment = gtk.NewAdjustment(0, 0, 1, 1, 10, 0)
    c.timeprocess = gtk.NewScale(gtk.OrientationHorizontal, c.timeadjustment)
    c.timeprocess.AddCSSClass("timescale")

    tsstext := gtk.NewLabel("testing")
    timepopover := gtk.NewPopover()
    timepopover.SetSizeRequest(80, 0)
    timepopover.SetChild(tsstext)
    timepopover.SetPosition(gtk.PosTop)
    timepopover.SetAutohide(false)
    boxroot.Append(timepopover)
    timepopover.AddCSSClass("timePopup")

    motionctrl := gtk.NewEventControllerMotion()
    c.timeprocess.AddController(motionctrl)

    motionctrl.ConnectEnter(func(x, y float64) {
        glib.IdleAddPriority(glib.PRIORITY_HIGH_IDLE, func() {
            rect := gdk.NewRectangle(int(x), 0, 0, 0)
            timepopover.SetPointingTo(&rect)
            timepopover.Show()
        })
    })

    motionctrl.ConnectLeave(func() {
        glib.IdleAddPriority(glib.PRIORITY_HIGH_IDLE, func() {
            timepopover.Hide()
        })
    })

    motionctrl.ConnectMotion(func(x, y float64) {
        glib.IdleAddPriority(glib.PRIORITY_HIGH_IDLE, func() {

            rect := gdk.NewRectangle(int(x), 0, 0, 0)
            timepopover.SetPointingTo(&rect)
            prr := math.Round(percent.PercentOf(int(x), c.timeprocess.AllocatedWidth()))
            step := c.timeadjustment.StepIncrement()
            value := (step*math.Round(prr*(c.duration-0)/step) + 0) / 100
            drtime, _ := time.ParseDuration(fmt.Sprintf("%fs", value))
            c.Lock()
            c.lastValPos = drtime
            c.Unlock()

            currentTime := parsing.NewTime().Duration(drtime)
            tsstext.SetText(currentTime)
            if drtime.Seconds() < 0 {
                timepopover.Hide()
            } else if drtime.Seconds() > c.duration {
                timepopover.Hide()
            } else {
                if !timepopover.IsVisible() {
                    timepopover.Show()
                }
            }

        })
    })

    root.AddOverlay(c.timeprocess)

    // add button for temp fixed when gtk scale not emitted release signal
    c.btntracks = gtk.NewButton()
    c.btntracks.SetCSSClasses([]string{"transparent-btn"})

    clickges := gtk.NewGestureClick()
    c.timeprocess.AddController(clickges)

    clickges.ConnectPressed(func(nPress int, x, y float64) {
        fmt.Println("ConnectPressed")
        c.Lock()
        c.seekOnHold = true
        c.Unlock()
    })

    // use cancel for release signal
    clickges.ConnectCancel(func(sequence *gdk.EventSequence) {
        fmt.Println("ConnectCancel")
        c.Lock()
        c.seekOnHold = false
        c.Unlock()

        glib.IdleAddPriority(glib.PRIORITY_HIGH_IDLE, func() {
            c.Lock()
            val := c.lastValPos.Seconds()
            c.Unlock()
            c.timeadjustment.SetValue(val)
            c.main.player.Bridge().Seek(val)
        })
    })

    c.btntracks.SetChild(root)
    c.timeprocess.SetSensitive(false)
    c.cacheprocess.SetSensitive(false)
    c.btntracks.SetSensitive(false)
    boxroot.Append(c.btntracks)

    return boxroot
}

And add css for transparent button

.transparent-btn{
    background:transparent;
    box-shadow: none;
    border-radius: 0px;
    border: 0px;
    text-shadow: none; 
    -gtk-icon-shadow: none;
    padding: 0px;
    &:hover{
        box-shadow: none;
        background: transparent;
    }
}
  • So, if I get it correctly I'd basically have to do this: Scale as child of non sensitive button, GestureClick connected as controller to Scale, Monitoring cancel and pressed signals? Because when I try and do this, I don't even get a pressed signal. – swanux Jul 13 '22 at 09:22