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

Added tabbingIdentifier to Window in Cocoa #2311

Draft
wants to merge 6 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions android/tests_backend/app.py
Original file line number Diff line number Diff line change
Expand Up @@ -106,3 +106,11 @@ def rotate(self):
self.native.findViewById(
R.id.content
).getViewTreeObserver().dispatchOnGlobalLayout()

@property
def tabbing_enabled(self):
xfail("Tabbed windows not implemented for this backend.")

@tabbing_enabled.setter
def tabbing_enabled(self, value):
xfail("Tabbed windows not implemented for this backend.")
7 changes: 7 additions & 0 deletions android/tests_backend/window.py
Original file line number Diff line number Diff line change
Expand Up @@ -87,3 +87,10 @@ def assert_toolbar_item(self, index, label, tooltip, has_icon, enabled):

def press_toolbar_button(self, index):
self.native.onOptionsItemSelected(self._toolbar_items()[index])

@property
def tabs(self):
pytest.xfail("Tabbed windows not implemented for this backend.")

def merge_all_windows(self):
pytest.xfail("Tabbed windows not implemented for this backend.")
1 change: 1 addition & 0 deletions changes/2311.feature.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
On macOS, windows now only tab with others of the same class.
5 changes: 4 additions & 1 deletion cocoa/src/toga_cocoa/window.py
Original file line number Diff line number Diff line change
Expand Up @@ -148,6 +148,9 @@ def __init__(self, interface, title, position, size):
self.native.interface = self.interface
self.native.impl = self

# This causes windows to only tab together with others of the same class.
self.native.tabbingIdentifier = str(self.interface.__class__)

# Cocoa releases windows when they are closed; this causes havoc with
# Toga's widget cleanup because the ObjC runtime thinks there's no
# references to the object left. Add a reference that can be released
Expand All @@ -163,7 +166,7 @@ def __init__(self, interface, title, position, size):
self.container = Container(on_refresh=self.content_refreshed)
self.native.contentView = self.container.native

# Ensure that the container renders it's background in the same color as the window.
# Ensure that the container renders its background in the same color as the window.
self.native.wantsLayer = True
self.container.native.backgroundColor = self.native.backgroundColor

Expand Down
10 changes: 9 additions & 1 deletion cocoa/tests_backend/app.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ def __init__(self, app):
super().__init__()
self.app = app
# Prevents erroneous test fails from secondary windows opening as tabs
NSWindow.allowsAutomaticWindowTabbing = False
self.tabbing_enabled = False
assert isinstance(self.app._impl.native, NSApplication)

@property
Expand Down Expand Up @@ -175,3 +175,11 @@ def keystroke(self, combination):
keyCode=key_code,
)
return toga_key(event)

@property
def tabbing_enabled(self):
return NSWindow.allowsAutomaticWindowTabbing

@tabbing_enabled.setter
def tabbing_enabled(self, value):
NSWindow.allowsAutomaticWindowTabbing = value
7 changes: 7 additions & 0 deletions cocoa/tests_backend/window.py
Original file line number Diff line number Diff line change
Expand Up @@ -261,3 +261,10 @@ def press_toolbar_button(self, index):
restype=None,
argtypes=[objc_id],
)

@property
def tabs(self):
return self.native.tabbedWindows

def merge_all_windows(self):
self.native.mergeAllWindows(self.native)
8 changes: 8 additions & 0 deletions gtk/tests_backend/app.py
Original file line number Diff line number Diff line change
Expand Up @@ -154,3 +154,11 @@ def keystroke(self, combination):
event.state = state

return toga_key(event)

@property
def tabbing_enabled(self):
pytest.xfail("Tabbed windows not implemented for this backend.")

@tabbing_enabled.setter
def tabbing_enabled(self, value):
pytest.xfail("Tabbed windows not implemented for this backend.")
9 changes: 9 additions & 0 deletions gtk/tests_backend/window.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@
from pathlib import Path
from unittest.mock import Mock

from pytest import xfail

from toga_gtk.libs import Gdk, Gtk

from .probe import BaseProbe
Expand Down Expand Up @@ -251,3 +253,10 @@ def assert_toolbar_item(self, index, label, tooltip, has_icon, enabled):
def press_toolbar_button(self, index):
item = self.impl.native_toolbar.get_nth_item(index)
item.emit("clicked")

@property
def tabs(self):
xfail("Tabbed windows not implemented for this backend.")

def merge_all_windows(self):
xfail("Tabbed windows not implemented for this backend.")
8 changes: 8 additions & 0 deletions iOS/tests_backend/app.py
Original file line number Diff line number Diff line change
Expand Up @@ -69,3 +69,11 @@ def terminate(self):
def rotate(self):
self.native = self.app._impl.native
self.native.delegate.application(self.native, didChangeStatusBarOrientation=0)

@property
def tabbing_enabled(self):
pytest.xfail("Tabbed windows not implemented for this backend.")

@tabbing_enabled.setter
def tabbing_enabled(self, value):
pytest.xfail("Tabbed windows not implemented for this backend.")
7 changes: 7 additions & 0 deletions iOS/tests_backend/window.py
Original file line number Diff line number Diff line change
Expand Up @@ -77,3 +77,10 @@ async def close_select_folder_dialog(self, dialog, result, multiple_select):

def has_toolbar(self):
pytest.skip("Toolbars not implemented on iOS")

@property
def tabs(self):
pytest.xfail("Tabbed windows not implemented for this backend.")

def merge_all_windows(self):
pytest.xfail("Tabbed windows not implemented for this backend.")
64 changes: 63 additions & 1 deletion testbed/tests/test_window.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@

def window_probe(app, window):
module = import_module("tests_backend.window")
return getattr(module, "WindowProbe")(app, window)
return module.WindowProbe(app, window)


@pytest.fixture
Expand Down Expand Up @@ -216,6 +216,68 @@ async def test_secondary_window_with_args(app, second_window, second_window_prob

assert second_window not in app.windows

def probe_for_window_class(app, WindowClass):
window = WindowClass()
window.show()
probe = window_probe(app, window)
return probe
freakboy3742 marked this conversation as resolved.
Show resolved Hide resolved

async def test_window_tabbing(app, app_probe, main_window_probe):
"""Windows tab only with others of the same class (only implemented on macOS)"""

class WindowSubclass(toga.Window):
pass

base_probes = [probe_for_window_class(app, toga.Window) for _ in range(2)]
subclass_probes = [
probe_for_window_class(app, WindowSubclass) for _ in range(2)
]

try:
base_probe, _ = base_probes
subclass_probe, _ = subclass_probes
freakboy3742 marked this conversation as resolved.
Show resolved Hide resolved

app_probe.tabbing_enabled = True

# Double check that nothing's tabbed initially.
assert not any(
[main_window_probe.tabs, base_probe.tabs, subclass_probe.tabs]
)

main_window_probe.merge_all_windows()
await main_window_probe.wait_for_window(
"Merge All Windows called on MainWindow"
)
assert not any(
[main_window_probe.tabs, base_probe.tabs, subclass_probe.tabs]
)
freakboy3742 marked this conversation as resolved.
Show resolved Hide resolved

base_probe.merge_all_windows()

await main_window_probe.wait_for_window(
"Merge All Windows called on base Window"
)
assert (
not main_window_probe.tabs
and len(base_probe.tabs) == 2
and not subclass_probe.tabs
)
freakboy3742 marked this conversation as resolved.
Show resolved Hide resolved

subclass_probe.merge_all_windows()
await main_window_probe.wait_for_window(
"Merge All Windows called on Window subclass"
)
assert (
not main_window_probe.tabs
and len(base_probe.tabs) == 2
and len(subclass_probe.tabs) == 2
)

finally:
app_probe.tabbing_enabled = False
for probe in [*base_probes, *subclass_probes]:
probe.close()

async def test_secondary_window_cleanup(app_probe):
"""Memory for windows is cleaned up when windows are deleted."""
# Create and show a window with content. We can't use the second_window fixture
Expand Down
8 changes: 8 additions & 0 deletions winforms/tests_backend/app.py
Original file line number Diff line number Diff line change
Expand Up @@ -154,3 +154,11 @@ def activate_menu_minimize(self):

def keystroke(self, combination):
return winforms_to_toga_key(toga_to_winforms_key(combination))

@property
def tabbing_enabled(self):
pytest.xfail("Tabbed windows not implemented for this backend.")

@tabbing_enabled.setter
def tabbing_enabled(self, value):
pytest.xfail("Tabbed windows not implemented for this backend.")
8 changes: 8 additions & 0 deletions winforms/tests_backend/window.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import asyncio
from unittest.mock import Mock

from pytest import xfail
from System import EventArgs
from System.Windows.Forms import (
Form,
Expand Down Expand Up @@ -148,3 +149,10 @@ def assert_toolbar_item(self, index, label, tooltip, has_icon, enabled):

def press_toolbar_button(self, index):
self._native_toolbar_item(index).OnClick(EventArgs.Empty)

@property
def tabs(self):
xfail("Tabbed windows not implemented for this backend.")

def merge_all_windows(self):
xfail("Tabbed windows not implemented for this backend.")
Loading