-
-
Notifications
You must be signed in to change notification settings - Fork 640
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
I recently created a component to make use Quasars's AppFullscreen plugin and thought it were also helpful for other users, so I documented it and added it to the NiceGUI docu. It enables the user to switch to fullscreen programmatically which especially on a smartphone or tablet context greatly improves the user experience. ![image](https://github.com/user-attachments/assets/b55be2a6-785c-4a46-8740-30811a3626da) --------- Co-authored-by: Falko Schindler <[email protected]>
- Loading branch information
1 parent
1fb2d12
commit ce015a4
Showing
7 changed files
with
206 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,32 @@ | ||
export default { | ||
props: { | ||
requireEscapeHold: Boolean, | ||
}, | ||
mounted() { | ||
document.addEventListener("fullscreenchange", this.handleFullscreenChange); | ||
document.addEventListener("mozfullscreenchange", this.handleFullscreenChange); | ||
document.addEventListener("webkitfullscreenchange", this.handleFullscreenChange); | ||
document.addEventListener("msfullscreenchange", this.handleFullscreenChange); | ||
}, | ||
unmounted() { | ||
document.removeEventListener("fullscreenchange", this.handleFullscreenChange); | ||
document.removeEventListener("mozfullscreenchange", this.handleFullscreenChange); | ||
document.removeEventListener("webkitfullscreenchange", this.handleFullscreenChange); | ||
document.removeEventListener("msfullscreenchange", this.handleFullscreenChange); | ||
}, | ||
methods: { | ||
handleFullscreenChange() { | ||
this.$emit("update:model-value", Quasar.AppFullscreen.isActive); | ||
}, | ||
enter() { | ||
Quasar.AppFullscreen.request().then(() => { | ||
if (this.requireEscapeHold && navigator.keyboard && typeof navigator.keyboard.lock === "function") { | ||
navigator.keyboard.lock(["Escape"]); | ||
} | ||
}); | ||
}, | ||
exit() { | ||
Quasar.AppFullscreen.exit(); | ||
}, | ||
}, | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,59 @@ | ||
from typing import Optional | ||
|
||
from ..events import Handler, ValueChangeEventArguments | ||
from .mixins.value_element import ValueElement | ||
|
||
|
||
class Fullscreen(ValueElement, component='fullscreen.js'): | ||
LOOPBACK = None | ||
|
||
def __init__(self, *, | ||
require_escape_hold: bool = False, | ||
on_value_change: Optional[Handler[ValueChangeEventArguments]] = None) -> None: | ||
"""Fullscreen control element | ||
This element is based on Quasar's `AppFullscreen <https://quasar.dev/quasar-plugins/app-fullscreen>`_ plugin | ||
and provides a way to enter, exit and toggle the fullscreen mode. | ||
Important notes: | ||
* Due to security reasons, the fullscreen mode can only be entered from a previous user interaction such as a button click. | ||
* The long-press escape requirement only works in some browsers like Google Chrome or Microsoft Edge. | ||
*Added in version 2.11.0* | ||
:param require_escape_hold: whether the user needs to long-press the escape key to exit fullscreen mode | ||
:param on_value_change: callback which is invoked when the fullscreen state changes | ||
""" | ||
super().__init__(value=False, on_value_change=on_value_change) | ||
self._props['requireEscapeHold'] = require_escape_hold | ||
|
||
@property | ||
def require_escape_hold(self) -> bool: | ||
"""Whether the user needs to long-press of the escape key to exit fullscreen mode. | ||
This feature is only supported in some browsers like Google Chrome or Microsoft Edge. | ||
In unsupported browsers, this setting has no effect. | ||
""" | ||
return self._props['requireEscapeHold'] | ||
|
||
@require_escape_hold.setter | ||
def require_escape_hold(self, value: bool) -> None: | ||
self._props['requireEscapeHold'] = value | ||
self.update() | ||
|
||
def enter(self) -> None: | ||
"""Enter fullscreen mode.""" | ||
self.value = True | ||
|
||
def exit(self) -> None: | ||
"""Exit fullscreen mode.""" | ||
self.value = False | ||
|
||
def toggle(self) -> None: | ||
"""Toggle fullscreen mode.""" | ||
self.value = not self.value | ||
|
||
def _handle_value_change(self, value: bool) -> None: | ||
super()._handle_value_change(value) | ||
self.run_method('enter' if value else 'exit') |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,68 @@ | ||
from unittest.mock import patch | ||
|
||
import pytest | ||
|
||
from nicegui import ui | ||
from nicegui.testing import Screen | ||
|
||
|
||
@pytest.mark.parametrize('require_escape_hold', [True, False]) | ||
def test_fullscreen_creation(screen: Screen, require_escape_hold: bool): | ||
fullscreen = ui.fullscreen(require_escape_hold=require_escape_hold) | ||
assert not fullscreen.value | ||
assert fullscreen.require_escape_hold == require_escape_hold | ||
|
||
screen.open('/') | ||
|
||
|
||
def test_fullscreen_methods(screen: Screen): | ||
values = [] | ||
|
||
fullscreen = ui.fullscreen(on_value_change=lambda e: values.append(e.value)) | ||
|
||
screen.open('/') | ||
|
||
with patch.object(fullscreen, 'run_method') as mock_run: | ||
fullscreen.enter() | ||
mock_run.assert_called_once_with('enter') | ||
mock_run.reset_mock() | ||
|
||
fullscreen.exit() | ||
mock_run.assert_called_once_with('exit') | ||
mock_run.reset_mock() | ||
|
||
fullscreen.toggle() | ||
mock_run.assert_called_once_with('enter') | ||
mock_run.reset_mock() | ||
|
||
fullscreen.value = False | ||
mock_run.assert_called_once_with('exit') | ||
mock_run.reset_mock() | ||
|
||
fullscreen.value = True | ||
mock_run.assert_called_once_with('enter') | ||
mock_run.reset_mock() | ||
|
||
assert values == [True, False, True, False, True] | ||
|
||
|
||
def test_fullscreen_button_click(screen: Screen): | ||
"""Test that clicking a button to enter fullscreen creates the correct JavaScript call. | ||
Note: We cannot test actual fullscreen behavior as it requires user interaction, | ||
but we can verify the JavaScript method is called correctly. | ||
""" | ||
values = [] | ||
|
||
fullscreen = ui.fullscreen(on_value_change=lambda e: values.append(e.value)) | ||
ui.button('Enter Fullscreen', on_click=fullscreen.enter) | ||
ui.button('Exit Fullscreen', on_click=fullscreen.exit) | ||
|
||
screen.open('/') | ||
screen.click('Enter Fullscreen') | ||
screen.wait(0.5) | ||
assert values == [True] | ||
|
||
screen.click('Exit Fullscreen') | ||
screen.wait(0.5) | ||
assert values == [True, False] |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,42 @@ | ||
from nicegui import ui | ||
|
||
from . import doc | ||
|
||
|
||
@doc.demo(ui.fullscreen) | ||
def main_demo() -> None: | ||
fullscreen = ui.fullscreen() | ||
|
||
ui.button('Enter Fullscreen', on_click=fullscreen.enter) | ||
ui.button('Exit Fullscreen', on_click=fullscreen.exit) | ||
ui.button('Toggle Fullscreen', on_click=fullscreen.toggle) | ||
|
||
|
||
@doc.demo('Requiring long-press to exit', ''' | ||
You can require users to long-press the escape key to exit fullscreen mode. | ||
This is useful to prevent accidental exits, for example when working on forms or editing data. | ||
Note that this feature only works in some browsers like Google Chrome or Microsoft Edge. | ||
''') | ||
def long_press_demo(): | ||
fullscreen = ui.fullscreen() | ||
ui.switch('Require escape hold').bind_value_to(fullscreen, 'require_escape_hold') | ||
ui.button('Toggle Fullscreen', on_click=fullscreen.toggle) | ||
|
||
|
||
@doc.demo('Tracking fullscreen state', ''' | ||
You can track when the fullscreen state changes. | ||
Note that due to security reasons, fullscreen mode can only be entered from a previous user interaction | ||
such as a button click. | ||
''') | ||
def state_demo(): | ||
fullscreen = ui.fullscreen( | ||
on_value_change=lambda e: ui.notify('Enter' if e.value else 'Exit') | ||
) | ||
ui.button('Toggle Fullscreen', on_click=fullscreen.toggle) | ||
ui.label().bind_text_from(fullscreen, 'state', | ||
lambda state: 'Fullscreen' if state else '') | ||
|
||
|
||
doc.reference(ui.fullscreen) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters