-
-
Notifications
You must be signed in to change notification settings - Fork 669
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
Added support for maximize, minimize, full_screen in WinForms #1874
Conversation
This adds supports for the following: On a
On a window by window basis:
|
Skipping
|
@freakboy3742 Take a look at this and let me know what you think. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There's still some core API design issues here; I've flagged them inline.
core/src/toga/window.py
Outdated
@@ -8,6 +9,14 @@ | |||
from toga.widgets.base import WidgetRegistry | |||
|
|||
|
|||
@unique | |||
class WindowStates(Enum): |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Enum classes should be singular, not plural, so they read as constants: WindowState.NORMAL
.
def set_normal_screen(self): | ||
"""Sets the app's windows in normal state""" | ||
self._impl.set_normal_screen(self.windows) | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This collection of changes doesn't make a whole lot of sense. An app can't be maximised - only a window can be. There is maybe an argument for an app-wide minimise, as a "minimise all, or a "minimise these windows" that mirrors the existing set_full_screen()
call.
Speaking of - there's an existing set_full_screen()
API, so having a property as well doesn't make sense.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes an app-wide as well as per window basis window state operations is what I aimed for the API.
But, we now have window state properties, so we can use them instead of functions like set_maximize_screen()
or set_minimize_screen()
. Hence, I have removed them to keep a clean API and provide a single way to do a thing.
If there will be maximized
,minimized
properties, then it's natural to have a full_screen
property also to have an API continuity between the various windows states properties.
set_full_screen()
is still there as it was defined in the core API and was also specified in the docs. So, for backwards compatibility, it is still present.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sure - but that doesn't address "set_normal_screen()".
core/src/toga/window.py
Outdated
return self._impl.minimized | ||
|
||
@minimized.setter | ||
def minimized(self, condition: bool): |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
"condition" isn't an especially meaningful variable name; I'd suggest minimize
.
core/src/toga/window.py
Outdated
return self._impl.maximized | ||
|
||
@maximized.setter | ||
def maximized(self, value: bool): |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'd suggest maximize
rather than the generic "value".
core/src/toga/window.py
Outdated
@property | ||
def full_screen(self): | ||
return self._is_full_screen | ||
return self._impl.full_screen |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The impl layer shouldn't be a property - it should be anis_full_screen()
method.
core/tests/test_window.py
Outdated
# instead checks the _window_state for calculating the result. | ||
# Hence, there will be no return of value in platforms where | ||
# there is no implementation. I have Manually tested this case | ||
# on windows for this case and it works fine. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
No - you can't ever disable a test, especially on the Core API. The backend implementations shouldn't matter - because the core tests run against the dummy.
core/src/toga/app.py
Outdated
if condition: | ||
self._impl.set_maximize_screen(self.windows) | ||
else: | ||
self._impl.set_normal_screen(self.windows) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
why explicit set_maximize_screen()
calls, rather than set_window_state()
?
core/src/toga/window.py
Outdated
if value: | ||
self._impl.set_maximize_screen() | ||
else: | ||
self._impl.set_normal_screen() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
As above - why explicit set_maximize/normal_screen
() calls, rather than a single set_window_state()
?
changes/1874.feature.rst
Outdated
@@ -0,0 +1 @@ | |||
Added support for maximize, minimize, full_screen in WinForms. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Added support for maximize, minimize, full_screen in WinForms. | |
An API for maximising and minimising windows was added. |
Also - this should be tied to the ticket discussing the feature: #1857 - rather than the PR.
Thanks. I will work on the changes as you requested and push another commit. |
I have identified the problems and solved them. I will publish the changes soon. |
The APIs are as: #1874 (comment) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
More comments from another review.
Keep in mind that API design is key here. There is an existing API for part of the feature you're describing, so that existing API (and implementation needs to be accommodated. 2 parallel implementations isn't a viable option here.
core/src/toga/window.py
Outdated
@@ -54,7 +63,8 @@ def __init__( | |||
self._impl = None | |||
self._app = None | |||
self._content = None | |||
self._is_full_screen = False | |||
|
|||
self.window_state = WindowState.NORMAL |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In general, we should be deferring to the backend for this, preferring any native mechanism for determining actual current state over a proxy variable.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The problem is in the backend there is no native differentiation between a full screened window and a maximized window state. Hence, the proxy variable.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
But that's a backend issue. If Windows doesn't provide a way to differentiate, that's something the Windows backend can work around with a proxy variable.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sure, I will change accordingly in the next commit.
def set_normal_screen(self): | ||
"""Sets the app's windows in normal state""" | ||
self._impl.set_normal_screen(self.windows) | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sure - but that doesn't address "set_normal_screen()".
for window in self.windows: | ||
window.set_normal_screen() | ||
|
||
# For backwards compatibility in core API |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Backwards compatibility that is only visible with a code comment isn't really backwards compatibility.
This should be causing a DeprecationWarning, and should then be implemented in terms of the new API, rather than maintaining a parallel implementation.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I will modify it to add a DeprecationWarning and change the internal implementation. But I am not sure what do you mean by "address set_normal_screen()". Can you tell me what is the intended behaviour you want with set_normal_screen
API?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I want to know why it is needed at all. As an API entry point, it's completely inconsistent with all the other methods - there's no "inverse", and it's a method, not a property. Why isn't maximized=False
(or minimized=False
) sufficient? Why is an explicit method needed for "normal" state? Or, if there is a need for a method - why isn't it set_window_state()
, rather than a normal specific method?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I mean there might be situations where the app developer doesn't care about what state a particular window is in and just wants the window to not remain in maximize
, minimize
or full_screen
state.
Making normal=True/False
as a property doesn't make much sense as doing normal=False
doesn't make any logical sense.
Hence, there is a method set_normal_screen()
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sure - but then, why not have set_window_state()
? That means you have an explicit API to handle all window states, rather than a single method that stands out as a different pattern to all the others. Yes, window.set_window_state(WindowState.MAXIMIZED)
is exactly the same as window.maximized=True
... but that's a documentation issue.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@freakboy3742 I agree we can have a set_window_state()
as a single method. Do you want it to become a core API? If so, please confirm, so that I will change it in the next commit.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, I agree a core-level API is called for; however, to be consistent, it should be a window_state
property with a getter and setter, not a set_window_state()
method.
I'd also suggest that the property is state
on the Window class, and window_state
on the App class.
Note that this doesn't change the backend API, which only requires the get/set_window_state() method (as all the core APIs can be expressed based on that single underlying API).
As an aside - this sort of back-and-forth is why writing up finalising design tickets like #1857 before implementing a feature is important. The discussion on #1857 had lots of ideas, but it didn't really get to a final concrete approved design. Rather than go through lots of code churn where the comments are 50% code style and 50% API design questions, it's better to sort out a complete design first.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I agree, we should have finalized API first. In the future, I will first discuss with you and then start on the implementation.
Anyways, I will implement as per your suggestion and submit a commit.
@property | ||
def is_full_screen(self): | ||
"""Is the app currently in full screen mode?""" | ||
return self._full_screen_windows is not None | ||
|
||
# For backwards compatibility in core API |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This isn't backwards-compatibility - it's maintaining a feature set: the ability to set a selection of windows full screen.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I will modify it to add a DeprecationWarning and change the internal implementation.
core/src/toga/app.py
Outdated
window.full_screen = True | ||
else: | ||
for window in self.windows: | ||
window.full_screen = False |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This approach won't work with the Cocoa implementation. This needs to be done in a batch, so that Cocoa can assign windows to screens.
This is likely true of other platforms as well - there's no point making all the windows full screen on the same screen.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I am not sure that I understand you correctly.
Doingtoga.App.full_screen = True
means that all the windows of the app will be set to full screen. If only selected windows need to be full screened then the user can do window.full_screen = True
on those windows.
Also what do you mean by "assign window to screens"? Are you taking about multi-monitor setup or something similar?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm saying look at the existing implementation of the exiting feature on Cocoa. The API that you've proposed here won't work.
And yes - it's the multi-monitor situation that is the problem here. The full-screen feature exists specifically because of multi-screen support. It was added to support Podium - where you have 2 screens - a slide presentation screen, and a speakers note screen. But - you can have multiple "presentation" documents open, and you only want 2 windows to be made full screen, and you need a specific window to go to screen 1.
I have modified as per the suggestions. There is now complete gtk support and partial cocoa support. However, please test the cocoa implementation and let me know how it goes. The gtk backend also needs testing but not as much as the cocoa backend. Now, there are no shadow variable for getting the current window state. The state is retrieved directly. All the window operations are done in the screen where the windows are present. But, I couldn't write a proper test for dummy core. I think the problem is incorrect setting and retrieving of a shadow variable @freakboy3742 Please take look at this and let me know how should I proceed. |
The dummy backend should behave like a real backend. If a real backend is "stateful"... then the dummy backend should dummy out that state. This can be done by directly adding an attribute to the dummy widget (see the ProgressBar dummy), or by using the |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This pass doesn't feel any closer to the final goal - in fact, it feels further away from being mergable in many respects, because the implementation you've provided here has changed a bunch of the behavior and API surface of the previous implementation, and it's completely altered the implementation of existing features.
I think we need to stop iterating on implementations, and actually finish the design discussion; otherwise, we're going to keep going round in circles without getting any closer to a working design.
NSNumber.numberWithBool(True), forKey="NSFullScreenModeAllScreens" | ||
) | ||
|
||
# Find the screen on which the window is present |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This isn't what the previous implementation did - and it's not a change that will satisfy the use case for which the previous implementation existed.
|
||
def set_window_state(self, window_state): | ||
for window in self.interface.windows: | ||
window.state = window_state |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why is get_window_state an app level method here? The Window is the object that has state.
Closing this and opening a new one to prevent non meaningful commit messages. |
Added support for maximize, minimize, full_screen in WinForms.
PR Checklist: