Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

window on-move / move inconsistency #149

Open
SimonLSchlee opened this issue Dec 1, 2019 · 2 comments
Open

window on-move / move inconsistency #149

SimonLSchlee opened this issue Dec 1, 2019 · 2 comments

Comments

@SimonLSchlee
Copy link

SimonLSchlee commented Dec 1, 2019

On my system (linux manjaro xfce gtk) move seems to treat the position as the outer window position (left-top of the decorated window). While on-move and get-x / get-y report the position of the undecorated window (the left-top of the inside of the window).

Actually on-move first reports the outer window position and immediately afterwards the inner window position. Except when the outer window position (set via move) happens to be equal to the inner window position, than it appears that move does nothing and on-move is not called.

Expected Behaviour:
calling (send frame move x1 y1)
results in a call of (on-move x1 y1)

Observed Behaviour:
calling (send frame move x1 y1)
results in a call of (on-move x1 y1) followed by (on-move x2 y2)

Proposal 1 (preferred):

  • to make move more consistent it should always take inner window coordinates
  • on-move only gets called once with the inner window coordinates

Proposal 2:

  • add methods get-outer-x and get-outer-y which return the outer window coordinates
  • ensure those coordinates are consistent with move (and remove or fix the caching that seems to happen within move)

Example:

#lang racket/gui

(define moveframe%
  (class frame%
    (super-new)

    (define/override (on-move x y)
      (displayln (format "~a ~a" x y)))))

(define (example)
  (define f (new moveframe% [label "move frame"] [width 300] [height 300]))
  (define (m msg x y)
    (displayln (~a msg " " x " " y ":"))
    (send f move x y))

  (m "init" 100 100)
  (send f show #t)
  (sleep/yield 1)
  (m "reset" 100 100)
  (sleep/yield 1)
  ;; this depends on the window decoration specific delta which is (1 24) on my system
  (m "stays the same, because x y are already" (send f get-x) (send f get-y))
  (sleep/yield 1)
  (m "window decoration delta gets added" (add1 (send f get-x)) (send f get-y))
  (sleep/yield 1)
  (m "window decoration delta gets added again" (add1 (send f get-x)) (send f get-y))
  (sleep/yield 1)
  (send f show #f))

(module+ main
  (example))

Output (Linux):

init 100 100:
100 100
101 124
reset 100 100:
100 100
101 124
stays the same, because x y are already 101 124:
window decoration delta gets added 102 124:
102 124
103 148
window decoration delta gets added again 104 148:
104 148
105 172

Output (Windows Virtual Machine):

init 100 100:
100 100
reset 100 100:
stays the same, because x y are already 100 100:
window decoration delta gets added 101 100:
101 100
window decoration delta gets added again 102 100:
102 100

The behavior on windows is as expected.

@SimonLSchlee
Copy link
Author

And here is an ugly workaround I created that calculates the delta between the outer and inner of the window:

#lang racket

(provide get-delta)

(require racket/gui/base)

(define deltaframe%
  (class frame%
    (init-field channel)
    (super-new [label "get-delta-frame"])

    (define ch channel)
    (define first-pos #f)
    (define/override (on-move x y)
      (when ch
        (if (not first-pos)
            (set! first-pos (list x y))
            (begin
              (calc-delta ch first-pos (list x y))
              (set! ch #f)))))

    (define (calc-delta ch first second)
      (match-define (list hx hy) first)
      (match-define (list vx vy) second)
      (define delta (list (- hx vx) (- hy vy)))
      (thread
       (thunk
        (channel-put ch delta)
        (send this show #f))))

    (send this move 100 100)
    (send this show #t)))

(define (get-delta/fetch)
  (define ch (make-channel))
  (thread
   (thunk (new deltaframe% [channel ch])))
  (define (loop)
    (sync (handle-evt ch (λ (delta) delta))
          (handle-evt always-evt (λ (x) (sleep/yield 0) (loop)))))
  (loop))

(define delta #f)
(define (get-delta)
  (set! delta (get-delta/fetch))
  (set! get-delta (λ () delta))
  delta)

;; result of get-delta on my system: (list -1 -24)

@alex-hhh
Copy link
Sponsor Contributor

alex-hhh commented Aug 7, 2020

More context: https://groups.google.com/g/racket-users/c/3Yk5hpbDIcw/m/xTLTcOgYCgAJ, with the relevant messages quoted below:

From Alex:

Based on you description, it seems that the window manager you use on Linux will adjust the window position to make room for the border, but this seems to be specific to the window manager itself. It is not necessarily a bug that on-move is called multiple times and not always in response to calling the frame%/move method (for example, it will be called when the windows is dragged with the mouse).

This does not seem like a Racket GUI problem to me: I think that if you wrote a GTK application in C, the behavior would be the same.

Also the windows platform is not without its quirks in this regard: if you use frame%/move to move the window to 0,0, you'll notice that the top menu bar is lined up with the top of the screen, but there is an 8 pixel gap on the left. This is because, while only a 1 pixel border is drawn around the window, there are an extra 8 pixels of invisible border, so the user can find the resize handle for the window. Visually, moving a window to 0,0 will not move it to the top-left corner of the screen.

On Windows, there are API calls you can access using FFI, which allow calculating the window border to adjust for it. I suspect there are similar APIs on Linux for GTK.

Alex.

Response from Simon:

Yes, when I put on my pragmatic thinking hat, I certainly agree with you.
But it also makes me think about whether I should create a utility library which does a more abstracted window placement and restoring.
That library would care more about logical "human" positioning and for example snap to screens and possibly use platform specific code like you suggested.
Currently such a library seems to be nice to have, but not that urgent to me.

On the other hand when I play devil's advocate I think racket/gui is a cross-platform gui library and should have a highlevel api where these platform specific quirks don't leak through.

I am unsure what is most reasonable and the better approach, I guess changing the move behavior might be difficult because of backwards compatibility,
so the utility library might be the way to go.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants