toga icon indicating copy to clipboard operation
toga copied to clipboard

Window size setting requests not being ignored while in full screen (2)

Open hyuri opened this issue 8 months ago • 8 comments

Describe the bug

As mentioned in https://github.com/beeware/toga/issues/2862#issuecomment-2559258650 , the new API for resizing the window should ignore resizing requests when in full screen.

But testing the provided code below with Toga v0.5.0, instead of ignored requests, I'm getting exceptions and consequential blocking of downstream code execution.

Error in handler: Cannot resize window while in WindowState.FULLSCREEN
Traceback (most recent call last):
  File "/Users/user/full_screen_resize_issue/venv/lib/python3.13/site-packages/toga/handlers.py", line 161, in _handler
    result = handler(interface, *args, **kwargs)
  File "/Users/user/full_screen_resize_issue/fullscreenresizeissue/src/fullscreenresizeissue/app.py", line 95, in go_to_page_one
    self.main_window.size = (800, 600)
    ^^^^^^^^^^^^^^^^^^^^^
  File "/Users/user/full_screen_resize_issue/venv/lib/python3.13/site-packages/toga/window.py", line 445, in size
    raise RuntimeError(f"Cannot resize window while in {self.state}")
RuntimeError: Cannot resize window while in WindowState.FULLSCREEN

Sample Code

FullScreenResizeIssue - Hyuri.zip

When the "Go To Page 1" button is pressed, the go_to_page_one callback runs 1*) Resize the window and 2*) Change the window content. But the errors produced are blocking the execution of number 2. So, in this case, unless you leave full screen, you can't change the window content — not after resizing.

def go_to_page_one(self, widget):
	self.main_window.size = (800, 600) # 1*
	self.main_window.content = self.page_one # 2*

Steps to reproduce

  1. Have a page that sets a window size
  2. Go into full screen, using the green window button
  3. Go to another page
  4. Come back to the page that sets a window size
  5. See issue

Expected behavior

Toga should ignore window size setting when app/window is in full screen, instead of raising exceptions, especially so that it doesn't block any downstream code within the same execution branch.

Screenshots

No response

Environment

  • Operating System: macOS 13.7.4
  • Python version: 3.13.2
  • Software versions:
    • Briefcase: 0.3.22
    • Toga: 0.5.0

Logs


Additional context

Issue that led to the discovery of this issue: #2862

hyuri avatar Apr 05 '25 14:04 hyuri

I can replicate this on macOS 14.7.3.

Here's a more minimal (if less visually interesting) reproduction:

import toga


class FullScreenResizeIssue(toga.App):
    def startup(self):
        self.indicator = toga.Label("1")

        self.main_window = toga.MainWindow()
        self.main_window.content = toga.Box(
            children=[
                toga.Button(
                   "Change page", on_press=self.change
                ),
                toga.Label("Current page: "),
                self.indicator
            ]
        )
        self.main_window.show()

    def change(self, widget):
        if self.indicator.text == "1":
            self.main_window.size = (800, 600)
            self.indicator.text = "2"
        else:
            self.indicator.text = "1"


if __name__ == "__main__":
    FullScreenResizeIssue("Full Screen Resize Issue", "com.example").main_loop()

It doesn't matter which page is shown first, it just matters whether or not the code that tries to alter anything else displayed tries to change the window dimensions first. (In this example, 1 -> 2 doesn't work in full-screen, but 2 -> 1 always works.)

HalfWhitt avatar Apr 05 '25 16:04 HalfWhitt

@hyuri Hi! I'd like to work on this issue for a university software engineering course. It looks unclaimed — would it be okay if I took a stab at it?

boydstonl avatar Apr 09 '25 19:04 boydstonl

@boydstonl We don't use a formal "claim" or "assignment" process for issues - so stab away!

freakboy3742 avatar Apr 10 '25 10:04 freakboy3742

Hi @hyuri, it seems like there has been a misunderstanding due to my previously outdated comment.

The behavior you are encountering is clearly documented in the docs: https://toga.readthedocs.io/en/stable/reference/api/window.html#toga.Window.size.

The RuntimeError is validly raised as the native cocoa APIs do allow resizing of the window when in FULLSCREEN or PRESENTATION mode, but the resultant behavior is a glitchy bug which will resize the window content to the requested size but will fill the rest of the screen will black. Hence, window resizing had been disabled in such window states. The RuntimeError serves as a warning that if the user were to invoke the native API directly, then there would be unexpected behavior. This was the reason for the inclusion of raising the RuntimeError in the initial PR.

However, it seems like the actual problem you are having is due to the blocking of downstream code execution after the exception is raised. For this, there are 2 simple fixes:

You can do:

try:
    self.main_window.size = (800, 600)
except RuntimeError:
    pass

or

if self.main_window.state not in {WindowState.FULLSCREEN, WindowState.PRESENTATION}:
    self.main_window.size = (800, 600)

proneon267 avatar Jul 05 '25 12:07 proneon267

@proneon267 Oh, I see. So it's a design decision made to accommodate one [buggy] platform.

Thanks for the fixes, they work.

The RuntimeError is validly raised as the native cocoa APIs do allow resizing of the window when in FULLSCREEN or PRESENTATION mode, but the resultant behavior is a glitchy bug which will resize the window content to the requested size but will fill the rest of the screen will black.

But isn't this approach a little redundant? Since all platform will raise this exception, we now always have to guard every instance of main_window.size for no practical purpose that I can see. Ignoring the resizing request sounds more practical — and future-proof, in case platforms start allowing resizing while in fullscreen. And then leave a warning in the documentation: "Cocoa APIs do allow resizing the window while in fullscreen but it's glitchy, filling parts of the screen with black, so, if you want that behaviour, you're gonna have to use the native APIs directly".

Should I close this issue and maybe open a feature request to make the case above?

hyuri avatar Jul 05 '25 17:07 hyuri

@proneon267 Oh, I see. So it's a design decision made to accommodate one [buggy] platform. ... But isn't this approach a little redundant? Since all platform will raise this exception

I had used macOS just as an example (since you had reported the issue on it 😄), almost all platforms show some form of glitchy behavior when the window is resized while it is in FULLSCREEN or PRESENTATION state. This is true for all 3 desktop platforms - WinForms, GTK & Cocoa. While implementing the Window state API, we had found through manual testing that certain window operations during certain window states were always invalid and caused glitchy behavior. The raised RuntimeError exception is not specific to just macOS and serves as a clear warning against unexpected behavior.

As for ignoring the request vs. raising an exception, I'll leave that up to Russell to decide. But in the initial PR #2473, we had discussed of raising a clear warning, since the behavior was glitchy on all 3 platforms.

proneon267 avatar Jul 06 '25 12:07 proneon267

I might be misreading/misremembering the context for this - but my understanding is that the currently documented, cross-platform behavior is "raise RuntimeError on a size change request while in FULLSCREEN/PRESENTATION mode". That works, but it would be better if that RuntimeError was not raised.

Completely ignoring the request would be easy to implement (just replace the RuntimeError with a pass) - but that's going to lead to surprising behavior - you set a size, it appears to work (because there's no error), but then you exit fullscreen and the window isn't the size you requested.

So - this ticket is really a "make size requests work in the context of restored windows" request - which is a reasonable request to make, but complicated request to satisfy, because you need to inject the "new" size at exactly the right place in the "exit fullscreen" cycle.

For me, it will come down to how complex the implementation of this feature is in practice, and whether it leads to unexpected graphical glitches (like flickers between old and new window sizes on exit-fullscreen). If it can't be implemented easily and reliably, I'm almost inclined to close this ticket WONTFIX.

freakboy3742 avatar Jul 08 '25 00:07 freakboy3742

Another option would be to issue a warning rather than an error. That way developers are notified of what's going on (or, I suppose, not going on), but further code still executes.

HalfWhitt avatar Jul 08 '25 12:07 HalfWhitt