Did a bit of UI polishing

This commit is contained in:
Cian Hughes
2023-11-09 15:02:45 +00:00
parent 17f5636a2d
commit f50d718c0c
3 changed files with 83 additions and 32 deletions

Binary file not shown.

After

Width:  |  Height:  |  Size: 26 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

View File

@@ -1,5 +1,6 @@
from collections import defaultdict from collections import defaultdict
from functools import wraps from functools import wraps
from pathlib import Path
from typing import Callable, DefaultDict, Optional, Tuple from typing import Callable, DefaultDict, Optional, Tuple
import flet as ft import flet as ft
@@ -10,24 +11,61 @@ from .disk_dropdown import disk_dropdown
from .types import CreateDiskArgs from .types import CreateDiskArgs
# Hotkeys will be mapped using using a dict to avoid a big, slow if-elif-else chain
def _no_hotkey() -> Callable[[ft.KeyboardEvent], None]:
def dummy_func(e: ft.KeyboardEvent) -> None:
pass
return dummy_func
HOTKEY_MAP: DefaultDict[str, Callable[[ft.KeyboardEvent], None]] = defaultdict(_no_hotkey)
def main(page: ft.Page) -> None: def main(page: ft.Page) -> None:
page.title = "I-Form Server Node Deployer" page.title = "I-Form Server Node Deployer"
page.vertical_alignment = ft.MainAxisAlignment.CENTER page.vertical_alignment = ft.MainAxisAlignment.CENTER
# TODO: Add a logo
# TODO: Add a progress bar # TODO: Add a progress bar
# TODO: Finalise arrangement of fields
# TODO: Add save/load functionality? # TODO: Add save/load functionality?
# Lets start with the easiest part: a logo in the top left
if page.platform_brightness == ft.ThemeMode.DARK:
logo = ft.Image(str(Path(__file__).parent / "assets/logo_dark.png"), width=210)
else:
logo = ft.Image(str(Path(__file__).parent / "assets/logo_light.png"), width=210)
logo_container = ft.Container(
content=logo,
padding=10,
alignment=ft.alignment.top_left,
)
page.add(logo_container)
# These fields are used to get the parameters for the disk creation # These fields are used to get the parameters for the disk creation
disk, dd_element = disk_dropdown(tooltip="Select the disk to write to", label="Disk") disk, dd_element = disk_dropdown(tooltip="Select the disk to write to", label="Disk")
hostname = ft.TextField(value="host", label="Hostname", text_align=ft.TextAlign.LEFT) hostname = ft.TextField(
password = ft.TextField( value="host", label="Hostname", text_align=ft.TextAlign.LEFT, width=page.window_width // 3
label="Password", password=True, can_reveal_password=True, text_align=ft.TextAlign.LEFT )
password = ft.TextField(
label="Password",
password=True,
can_reveal_password=True,
text_align=ft.TextAlign.LEFT,
width=page.window_width // 3,
)
switch_ip = ft.TextField(
label="Switch IP", text_align=ft.TextAlign.LEFT, width=page.window_width // 3
)
switch_port = ft.TextField(
label="Switch Port",
value="4789",
text_align=ft.TextAlign.LEFT,
width=page.window_width // 3,
)
swarm_token = ft.TextField(
label="Swarm Token", text_align=ft.TextAlign.LEFT, width=int((2 * page.window_width) // 3)
) )
switch_ip = ft.TextField(label="Switch IP", text_align=ft.TextAlign.LEFT)
switch_port = ft.TextField(label="Switch Port", value="4789", text_align=ft.TextAlign.LEFT)
swarm_token = ft.TextField(label="Swarm Token", text_align=ft.TextAlign.LEFT)
# Add varnames, as they will be useful for unpacking later # Add varnames, as they will be useful for unpacking later
disk.__varname__ = "disk" disk.__varname__ = "disk"
@@ -38,7 +76,11 @@ def main(page: ft.Page) -> None:
swarm_token.__varname__ = "swarm_token" swarm_token.__varname__ = "swarm_token"
# This wrapper validates the value of the field before passing it to the function # This wrapper validates the value of the field before passing it to the function
def validate_value[F](func: Callable[[], F]) -> Callable[[], Optional[F]]: # mypy PEP 695 support can't come quickly enough # noqa def validate_value[
F
](func: Callable[[], F]) -> Callable[
[], Optional[F]
]: # mypy PEP 695 support can't come quickly enough # noqa
#! It is important that bool(F) evaluates to False if the value is invalid #! It is important that bool(F) evaluates to False if the value is invalid
@wraps(func) @wraps(func)
def wrapped() -> Optional[F]: def wrapped() -> Optional[F]:
@@ -125,7 +167,7 @@ def main(page: ft.Page) -> None:
field.border_color = None field.border_color = None
field.update() field.update()
typed_val = target_type(value) typed_val = target_type(value)
vals[varname] = typed_val # type: ignore #! This is a false positive, an invalid literal would have been caught by the if statement above vals[varname] = typed_val # type: ignore #! This is a false positive, an invalid literal would have been caught by the if statement above
if invalid_values: if invalid_values:
return return
@@ -143,6 +185,11 @@ def main(page: ft.Page) -> None:
# side-effects here though... # side-effects here though...
def close_dlg(*_) -> None: def close_dlg(*_) -> None:
dlg.open = False dlg.open = False
if "Y" in HOTKEY_MAP.keys():
del HOTKEY_MAP["Y"]
if "N" in HOTKEY_MAP.keys():
del HOTKEY_MAP["N"]
HOTKEY_MAP["Enter"] = confirm_disk_creation
page.update() page.update()
# This closure is called when the confirm disk creation button is pressed # This closure is called when the confirm disk creation button is pressed
@@ -182,14 +229,20 @@ def main(page: ft.Page) -> None:
actions_alignment=ft.MainAxisAlignment.END, actions_alignment=ft.MainAxisAlignment.END,
) )
# Finally, we open the dialog popup # Finally, we open the dialog popup and switch the hotkeys over
page.dialog = dlg page.dialog = dlg
dlg.open = True dlg.open = True
if "Enter" in HOTKEY_MAP.keys():
del HOTKEY_MAP["Enter"]
HOTKEY_MAP["Y"] = trigger_disk_creation
HOTKEY_MAP["N"] = close_dlg
page.update() page.update()
disk_creation_dialog = ft.FilledButton( disk_creation_dialog = ft.FilledButton(
text="Create Ignition Disk", text="Create Ignition Disk",
on_click=confirm_disk_creation, on_click=confirm_disk_creation,
width=page.window_width // 3,
) )
# Then, we arrange the fields into rows and columns # Then, we arrange the fields into rows and columns
@@ -217,34 +270,32 @@ def main(page: ft.Page) -> None:
alignment=ft.MainAxisAlignment.CENTER, alignment=ft.MainAxisAlignment.CENTER,
) )
swarm_token_row = ft.Row(
controls=[
swarm_token,
],
alignment=ft.MainAxisAlignment.CENTER,
)
stacked_rows = ft.Column( stacked_rows = ft.Column(
[disk_row, node_row, switch_row, swarm_token], [disk_row, node_row, switch_row, swarm_token_row],
alignment=ft.MainAxisAlignment.CENTER, alignment=ft.MainAxisAlignment.CENTER,
) )
# Finally, we finish constructing the UI by adding the rows to the page # Finally, we finish constructing the UI by adding the rows to the page
page.add(stacked_rows) page.add(stacked_rows)
# As a final task, we define the hotkey events # As a final task, we define the opening screen hotkey events
# We do this using a dict to avoid a big, slow if-elif-else chain HOTKEY_MAP["Enter"] = confirm_disk_creation
def no_hotkey() -> Callable[[ft.KeyboardEvent], None]:
def dummy_func(e: ft.KeyboardEvent) -> None:
pass
return dummy_func
def quit_app(e: ft.KeyboardEvent) -> None: def quit_app(e: ft.KeyboardEvent) -> None:
if e.ctrl: if e.ctrl:
page.window_close() page.window_close()
hotkey_map: DefaultDict[str, Callable[[ft.KeyboardEvent], None]] = defaultdict( HOTKEY_MAP["Q"] = quit_app
no_hotkey,
Enter=confirm_disk_creation,
Q=quit_app,
)
def on_key_press(e: ft.KeyboardEvent) -> None: def on_key_press(e: ft.KeyboardEvent) -> None:
print(e.key) HOTKEY_MAP[e.key](e)
hotkey_map[e.key](e)
page.on_keyboard_event = on_key_press page.on_keyboard_event = on_key_press