Compare commits

..

5 Commits

Author SHA1 Message Date
aclist
fc28d419bf fix: change bool state
Some checks are pending
Mirror to Codeberg / mirror-to-codeberg (push) Waiting to run
2026-08-11 22:23:49 +09:00
aclist
095af395f9 feat: dynamic radio sensitivity on steam deck 2026-08-11 22:23:18 +09:00
aclist
4ada06d305 chore: test html escaping in descriptions 2026-08-11 21:44:47 +09:00
aclist
5a0ab92985 chore: add tech debt comment 2026-08-11 21:44:08 +09:00
aclist
f2501871ee feat: update dialog on cancel action 2026-08-11 21:43:53 +09:00
4 changed files with 88 additions and 37 deletions

View File

@ -107,7 +107,7 @@ class WaitDialog(GenericDialog):
self.cur_job = 1 self.cur_job = 1
self.cancel = Gtk.Button(label="Cancel", halign=Gtk.Align.CENTER) self.cancel = Gtk.Button(label="Cancel", halign=Gtk.Align.CENTER)
self.cancel.connect("clicked", lambda _: self.controller.set_cancel_event()) self.cancel.connect("clicked", self._on_cancel_clicked)
self.connect("delete-event", lambda widget, event: True) self.connect("delete-event", lambda widget, event: True)
content = self.get_content_area() content = self.get_content_area()
@ -138,6 +138,11 @@ class WaitDialog(GenericDialog):
self.prog.set_fraction(fraction) self.prog.set_fraction(fraction)
self.cur_job += 1 self.cur_job += 1
def _on_cancel_clicked(self, button: Gtk.Button) -> None:
# TODO: strings
GLib.idle_add(self.update_text, "Caught cancel signal, cleaning up")
self.controller.set_cancel_event()
def show_cancel(self, state: bool) -> None: def show_cancel(self, state: bool) -> None:
self.cancel.set_visible(state) self.cancel.set_visible(state)

View File

@ -66,6 +66,7 @@ class ServerDialog(GenericDialog):
class ServerDetailsDialog(ServerDialog): class ServerDetailsDialog(ServerDialog):
def __init__(self, controller: "Controller", details: "Details"): def __init__(self, controller: "Controller", details: "Details"):
# TODO: server name should also be packed in details struct
name = controller.get_server_name() name = controller.get_server_name()
super().__init__(controller, strings.server_details, name, menu=None) super().__init__(controller, strings.server_details, name, menu=None)

View File

@ -21,6 +21,7 @@ from dzgui.strings import errors, options
from dzgui.util import strings, css, open_links from dzgui.util import strings, css, open_links
from dzgui.views.components.box import VBox from dzgui.views.components.box import VBox
from dzgui.views.components.eventbox import InfoEventBox
from dzgui.views.components.labels import LeftLabel from dzgui.views.components.labels import LeftLabel
from dzgui.views.components.buttons import WebButton from dzgui.views.components.buttons import WebButton
from dzgui.views.components.frame import HeadingFrame from dzgui.views.components.frame import HeadingFrame
@ -39,6 +40,37 @@ if TYPE_CHECKING:
from dzgui.controllers.emitter import Emitter from dzgui.controllers.emitter import Emitter
class ToggleField(Gtk.Box):
def __init__(
self,
controller: "Controller",
first_option: str,
second_option: str,
context: Preferences,
) -> None:
super().__init__(spacing=5, halign=Gtk.Align.START)
self.controller = controller
self.context = context
self.radio1 = Gtk.RadioButton.new_with_label(None, first_option)
self.radio2 = Gtk.RadioButton.new_from_widget(self.radio1)
self.radio2.set_label(second_option)
self.pack_start(self.radio1, NO_EXPAND, NO_FILL, NO_PADDING)
self.pack_start(self.radio2, NO_EXPAND, NO_FILL, NO_PADDING)
def set_suboption_active(self, state: bool) -> None:
# NOTE: defer connection of signal until after state is set
self.radio2.set_active(state)
self.radio1.connect("toggled", self._on_radio_toggled, self.context)
def _on_radio_toggled(self, button: Gtk.RadioButton, context: Preferences) -> None:
self.controller.toggle_config(context)
def set_sensitive(self, state: bool) -> None:
for el in self.radio1, self.radio2:
el.set_sensitive(state)
class ShortHBox(Gtk.Box): class ShortHBox(Gtk.Box):
def __init__(self, widget: Gtk.Widget) -> None: def __init__(self, widget: Gtk.Widget) -> None:
super().__init__(spacing=5, halign=Gtk.Align.START) super().__init__(spacing=5, halign=Gtk.Align.START)
@ -80,19 +112,26 @@ class Options(ScrollableMixin, Gtk.ScrolledWindow): # type: ignore
# TODO: make submit field a standalone class # TODO: make submit field a standalone class
self.player_box.get_children()[0].set_width_chars(30) # type: ignore self.player_box.get_children()[0].set_width_chars(30) # type: ignore
self.fullscreen_toggle = self.make_binary_radio( self.fullscreen_toggle = ToggleField(
self.controller,
strings.options.last_used, strings.options.last_used,
strings.options.always_fs, strings.options.always_fs,
Preferences.WINDOW, Preferences.WINDOW,
) )
# TODO: strings
eb = InfoEventBox(
"This option is not available on Steam Deck.", self.controller
)
self.fullscreen_toggle.add(eb)
self.client_combo = ClientCombo() self.client_combo = ClientCombo()
self.client_combo.connect("changed", self._on_client_changed) self.client_combo.connect("changed", self._on_client_changed)
client_hbox = ShortHBox(self.client_combo) client_hbox = ShortHBox(self.client_combo)
self.distance_toggle = self.make_binary_radio( self.distance_toggle = ToggleField(
strings.options.km, strings.options.mi, Preferences.DIST self.controller, strings.options.km, strings.options.mi, Preferences.DIST
) )
combo_store = Gtk.ListStore(str, object) combo_store = Gtk.ListStore(str, object)
@ -293,14 +332,6 @@ class Options(ScrollableMixin, Gtk.ScrolledWindow): # type: ignore
real_cmd = combo.get_model()[_iter][1] real_cmd = combo.get_model()[_iter][1]
self.controller.update_config(Preferences.CLIENT, real_cmd) self.controller.update_config(Preferences.CLIENT, real_cmd)
def _on_radio_toggled(self, button: Gtk.RadioButton, context: Preferences) -> None:
try:
self.controller.toggle_config(context)
except Exception:
button.handler_block_by_func(self._on_radio_toggled)
self.populate_settings()
button.handler_unblock_by_func(self._on_radio_toggled)
def _is_valid_text(self, text: str, context: Preferences) -> bool: def _is_valid_text(self, text: str, context: Preferences) -> bool:
if text.isspace(): if text.isspace():
return False return False
@ -344,23 +375,6 @@ class Options(ScrollableMixin, Gtk.ScrolledWindow): # type: ignore
state = self._is_valid_text(text, context) state = self._is_valid_text(text, context)
button.set_sensitive(state) button.set_sensitive(state)
def make_binary_radio(
self,
first_option: str,
second_option: str,
context: Preferences,
) -> Gtk.Box:
hbox = Gtk.Box(spacing=5, halign=Gtk.Align.START)
radio1 = Gtk.RadioButton.new_with_label(None, first_option)
radio2 = Gtk.RadioButton.new_from_widget(radio1)
radio2.set_label(second_option)
radio1.connect("toggled", self._on_radio_toggled, context)
hbox.pack_start(radio1, NO_EXPAND, NO_FILL, NO_PADDING)
hbox.pack_start(radio2, NO_EXPAND, NO_FILL, NO_PADDING)
return hbox
def populate_settings(self) -> None: def populate_settings(self) -> None:
prefs = self.controller.get_prefs() prefs = self.controller.get_prefs()
# NOTE: re-check in case file was removed by user between runs # NOTE: re-check in case file was removed by user between runs
@ -386,14 +400,15 @@ class Options(ScrollableMixin, Gtk.ScrolledWindow): # type: ignore
if hasattr(p, "set_text"): if hasattr(p, "set_text"):
p.set_text(name) p.set_text(name)
# NOTE: suppress toggle signal until radios are built fs = config["fullscreen"]
self._suppress_toggles(True) miles = config["use_miles"]
for el, conf_state in [
(self.fullscreen_toggle, config["fullscreen"]), if prefs.is_steam_deck:
(self.distance_toggle, config["use_miles"]), self.fullscreen_toggle.set_sensitive(False)
]: fs = True
el.get_children()[conf_state].set_active(True)
self._suppress_toggles(False) self.fullscreen_toggle.set_suboption_active(fs)
self.distance_toggle.set_suboption_active(miles)
# NOTE: disable buttons if no text is set # NOTE: disable buttons if no text is set
for field in ( for field in (

View File

@ -0,0 +1,30 @@
from dzgui.api.servers import Details
from dzgui.views.dialogs.servers import ServerDetailsDialog
import gi
gi.require_version("Gtk", "3.0")
from gi.repository import Gtk # noqa E402
class MockController:
def __init__(self) -> None:
self.window = Gtk.Window()
def get_window(self) -> Gtk.Window:
return self.window
def get_server_name(self) -> str:
return "My server"
def get_emitter(self) -> None:
return None
def test_html_escaping() -> None:
text = "<Server description> Game & mods"
details = Details([["0", "1"]], text, True)
controller = MockController()
dialog = ServerDetailsDialog(controller, details)
assert dialog.description.get_text() == text