]> skyeroc.xyz Git - obs-ctl/commitdiff
Rename and clean up bool_control
authorsbkelley <sb24kelley@gmail.com>
Sun, 30 Nov 2025 05:53:23 +0000 (00:53 -0500)
committersbkelley <sb24kelley@gmail.com>
Sun, 30 Nov 2025 05:53:23 +0000 (00:53 -0500)
src/obs_ctl/__init__.py
src/obs_ctl/data/layout.tcss
src/obs_ctl/widget/element/bool_control.py [new file with mode: 0644]
src/obs_ctl/widget/element/single_status_control.py [deleted file]

index 0350709edee01298a68b787e67ec3195c10a01aa..c409d33047b31ac31cc6bdc7a77430fcbc8568d6 100644 (file)
@@ -13,7 +13,7 @@ from textual.css.query import NoMatches
 from . import log, data
 from .widget import LabeledLabel, OBSSettingsPane
 
-from .widget.element.single_status_control import SingleStatusControl
+from .widget.element.bool_control import BoolControl
 
 logger = log.getLogger(__name__)
 obs_req_client = obs.ReqClient()
@@ -70,9 +70,9 @@ class OBSCtlApp(App):
             with Vertical(id="blocks_v_container", classes="blocks_containers"):
                 with Horizontal(id="blocks_h_container1", classes="blocks_h_containers"):
                     with Grid(id="controls_container", classes="blocks gridblocks"):
-                        yield SingleStatusControl(label="Recording", id="record_control", classes="controls")
-                        yield SingleStatusControl(label="Paused", on_label="Pause", off_label="Unpause", swap_red=True, id="pause_control", classes="controls")
-                        yield SingleStatusControl(label="Streaming", id="stream_control", classes="controls")
+                        yield BoolControl(False, label="Recording", id="record_control", classes="controls")
+                        yield BoolControl(False, label="Paused", true_label="Pause", false_label="Unpause", swap_red=True, id="pause_control", classes="controls")
+                        yield BoolControl(False, label="Streaming", id="stream_control", classes="controls")
                     with TabbedContent(id="settings_container", classes="blocks"):
                         with TabPane("OBS Settings", id="settings-tab"):
                             yield OBSSettingsPane(id="settings-content")
@@ -92,7 +92,7 @@ class OBSCtlApp(App):
                         yield LabeledLabel("Encdng frames dropped", "-/- (-.-%)", id="encoding_lag", classes="stats")
 
 
-    @on(SingleStatusControl.TurnedOff, "#stream_control")
+    @on(BoolControl.SetFalse, "#stream_control")
     def stream_off(self, message):
         logger.debug(f"Stop message rcv'd from {message.control}")
         try:
@@ -103,7 +103,7 @@ class OBSCtlApp(App):
             else:
                 raise
 
-    @on(SingleStatusControl.TurnedOn, "#stream_control")
+    @on(BoolControl.SetTrue, "#stream_control")
     def stream_on(self, message):
         logger.debug("Stream Started message rcv'd")
         try:
@@ -114,7 +114,7 @@ class OBSCtlApp(App):
             else:
                 raise
 
-    @on(SingleStatusControl.TurnedOff, "#record_control")
+    @on(BoolControl.SetFalse, "#record_control")
     def record_off(self, message):
         logger.debug("Record Stopped message rcv'd")
         try:
@@ -125,7 +125,7 @@ class OBSCtlApp(App):
             else:
                 raise
 
-    @on(SingleStatusControl.TurnedOn, "#record_control")
+    @on(BoolControl.SetTrue, "#record_control")
     def record_on(self, message):
         logger.debug("Record Started message rcv'd")
         try:
@@ -137,7 +137,7 @@ class OBSCtlApp(App):
                 raise
 
 
-    @on(SingleStatusControl.TurnedOn, "#pause_control")
+    @on(BoolControl.SetTrue, "#pause_control")
     def pause_on(self, message):
         logger.debug("Record Paused message rcv'd")
         try:
@@ -149,7 +149,7 @@ class OBSCtlApp(App):
                 raise
 
 
-    @on(SingleStatusControl.TurnedOff, "#pause_control")
+    @on(BoolControl.SetFalse, "#pause_control")
     def pause_off(self, message):
         logger.debug("Record Unpaused message rcv'd")
         try:
@@ -207,9 +207,9 @@ class OBSCtlApp(App):
     
 
     def update_monitors(self, obs_stats: OBSStats):
-        stream_ctl: SingleStatusControl = self.query_exactly_one("#stream_control")
-        record_ctl: SingleStatusControl = self.query_exactly_one("#record_control")
-        pause_ctl: SingleStatusControl = self.query_exactly_one("#pause_control")
+        stream_ctl: BoolControl = self.query_exactly_one("#stream_control")
+        record_ctl: BoolControl = self.query_exactly_one("#record_control")
+        pause_ctl: BoolControl = self.query_exactly_one("#pause_control")
         cpu_usage: LabeledLabel = self.query_exactly_one("#cpu_usage")
         memory_usage: LabeledLabel = self.query_exactly_one("#memory_usage")
         active_fp: LabeledLabel = self.query_exactly_one("#fps")
index ef662b201f8b05f1178dcc64e3f6438cd9ba211c..b2d87d9dbc020878c617becf71a701d4480ab403 100644 (file)
@@ -35,7 +35,7 @@
     min-height:8;
 }
 
-SingleStatusControl {
+BoolControl {
     background:$surface;
     border:blank;
     max-height:5;
diff --git a/src/obs_ctl/widget/element/bool_control.py b/src/obs_ctl/widget/element/bool_control.py
new file mode 100644 (file)
index 0000000..7a03158
--- /dev/null
@@ -0,0 +1,167 @@
+from textual import on
+from textual.app import ComposeResult
+from textual.containers import Container
+from textual.css import query
+from textual.message import Message
+from textual.reactive import reactive
+from textual.widgets import Button, RadioButton
+
+from ...log import getLogger
+
+logger = getLogger(__name__)
+
+class BoolControl(Container):
+    """BoolControl manages a boolean that belongs to something else.
+    
+    In other words, it can display true or false and this can be set
+    on it directly, but its buttons do nothing other than post a
+    message. Designed as i/o for an API"""
+    DEFAULT_CSS = """
+        .bool_control--light {
+            background:$panel;
+            text-align:center;
+            align:center bottom;
+            width:1fr;
+            height:1;
+        }
+        .bool_control--true_button {
+            width:1fr;
+            height:1fr;
+        }
+        .bool_control--false_button {
+            width:1fr;
+            height:1fr;
+        }
+    """
+    COMPONENT_CLASSES = {
+        "bool_control--light",
+        "bool_control--true_button",
+        "bool_control--false_button",
+    }
+    value = reactive(False)
+    label = reactive("Status")
+    true_label = reactive("Start")
+    false_label = reactive("Stop")
+    swap_red = reactive(False)
+    compact = reactive(True)
+    control_layout = reactive("vertical")
+
+    class MsgBase(Message):
+        control: "BoolControl" = None
+        def __init__(self, control):
+            super().__init__()
+            self.control = control
+    class SetTrue(MsgBase): ...
+    class SetFalse(MsgBase): ...
+
+
+    def __init__(
+            self,
+            value,
+            label: str = "Boolean",
+            true_label = "Start",
+            false_label = "Stop",
+            swap_red = False,
+            compact = True,
+            *children,
+            layout = "vertical",
+            name = None,
+            id = None,
+            classes = None,
+            disabled = False,
+            markup = True):
+        super().__init__(*children, name=name, id=id, classes=classes, disabled=disabled, markup=markup)
+        self.set_reactive(BoolControl.value, value)
+        self.set_reactive(BoolControl.label, label)
+        self.set_reactive(BoolControl.true_label, true_label)
+        self.set_reactive(BoolControl.false_label, false_label)
+        self.set_reactive(BoolControl.swap_red, swap_red)
+        self.set_reactive(BoolControl.compact, compact)
+        self.set_reactive(BoolControl.control_layout, layout)
+
+    def compose(self) -> ComposeResult:
+        with Container(classes="bool_control--light"):
+            yield RadioButton(
+                label="",
+                value=False,
+                button_first=False,
+                id="light",
+                compact=True,
+                disabled=True,
+                classes="bool_control--light",
+            )
+        yield Button(
+                label="Start",
+                id="true",
+                variant="success",
+                compact=True,
+                classes="bool_control--true_button"
+        )
+        yield Button(
+                label="Stop",
+                id="false",
+                variant="error",
+                compact=True,
+                classes = "bool_control--false_button"
+        )
+
+
+    def _on_mount(self, event):
+        # super()._on_mount(event)
+        self.watch_value(self.value)
+        self.watch_label(self.label)
+        self.watch_true_label(self.true_label)
+        self.watch_false_label(self.false_label)
+        self.watch_swap_red(self.swap_red)
+        self.watch_compact(self.compact)
+        self.watch_control_layout(self.control_layout)
+
+
+    @on(Button.Pressed, "#false")
+    def false_pressed(self, event: Button.Pressed):
+        self.post_message(self.SetFalse(self))
+
+
+    @on(Button.Pressed, "#true")
+    def true_pressed(self):
+        self.post_message(self.SetTrue(self))
+
+
+    def watch_value(self, value: bool) -> None:
+        light: RadioButton = self.query_exactly_one("#light")
+        light.value = value
+
+
+    def watch_label(self, value):
+        light: RadioButton = self.query_exactly_one("#light")
+        light.label = value
+
+
+    def watch_true_label(self, value):
+        true: Button = self.query_exactly_one("#true")
+        true.label = value
+
+
+    def watch_false_label(self, value):
+        false: Button = self.query_exactly_one("#false")
+        false.label = value
+
+
+    def watch_swap_red(self, value):
+        true: Button = self.query_exactly_one("#true")
+        false: Button = self.query_exactly_one("#false")
+        false.variant = "success" if value else "error"
+        true.variant = "error" if value else "success"
+    
+    
+    def watch_compact(self, value):
+        light: RadioButton = self.query_exactly_one("#light")
+        true: Button = self.query_exactly_one("#true")
+        false: Button = self.query_exactly_one("#false")
+        light.compact = value
+        true.compact = value
+        false.compact = value
+    
+
+    def watch_control_layout(self, value):
+        self.set_styles(layout=value)
\ No newline at end of file
diff --git a/src/obs_ctl/widget/element/single_status_control.py b/src/obs_ctl/widget/element/single_status_control.py
deleted file mode 100644 (file)
index bb3f4e1..0000000
+++ /dev/null
@@ -1,158 +0,0 @@
-from textual.containers import Container
-from textual.message import Message
-from textual.reactive import reactive
-from textual.css import query
-from textual.widget import ComposeResult
-from textual.widgets import Button, RadioButton
-
-from ...log import getLogger
-
-logger = getLogger()
-
-class SingleStatusControl(Container):
-    DEFAULT_CSS = """
-        .singlestatuscontrol--light {
-            background:$panel;
-            text-align:center;
-            align:center bottom;
-            width:1fr;
-            height:1fr;
-        }
-        .singlestatuscontrol--on_button {
-            width:1fr;
-            height:1fr;
-        }
-        .singlestatuscontrol--off_button {
-            width:1fr;
-            height:1fr;
-        }
-    """
-    COMPONENT_CLASSES = {
-        "singlestatuscontrol--light",
-        "singlesttauscontrol--on_button",
-        "singlestatuscontrol--off_button",
-    }
-    status = reactive(False)
-    label = reactive("Status")
-    on_label = reactive("Start")
-    off_label = reactive("Stop")
-    swap_red = reactive(False)
-
-    class MsgBase(Message):
-        control: "SingleStatusControl" = None
-        def __init__(self, control):
-            super().__init__()
-            self.control = control
-    class TurnedOn(MsgBase): ...
-    class TurnedOff(MsgBase): ...
-
-
-    def __init__(
-            self,
-            *children,
-            label: str = "Status",
-            on_label = "Start",
-            off_label = "Stop",
-            swap_red = False,
-            layout = "vertical",
-            name = None,
-            id = None,
-            classes = None,
-            disabled = False,
-            markup = True):
-        super().__init__(*children, name=name, id=id, classes=classes, disabled=disabled, markup=markup)
-        self.status = False
-        self.label = label
-        self.on_label = on_label
-        self.off_label = off_label
-        self.swap_red = swap_red
-        self.set_styles(layout=layout)
-
-    def compose(self) -> ComposeResult:
-        logger.debug("Composing SingleStatusControl")
-        with Container(classes="singlestatuscontrol--light"):
-            yield RadioButton(
-                label="",
-                value=False,
-                button_first=False,
-                id="light",
-                compact=True,
-                disabled=True,
-            )
-        yield Button(
-                label="Start",
-                id="on",
-                variant="success",
-                compact=True,
-                classes="singlestatuscontrol--on_button"
-        )
-        yield Button(
-                label="Stop",
-                id="off",
-                variant="error",
-                compact=True,
-                classes = "singlestatuscontrol--off_button"
-        )
-
-
-    def _on_mount(self, event):
-        # super()._on_mount(event)
-        self.watch_status(self.status)
-        self.watch_label(self.label)
-        self.watch_on_label(self.on_label)
-        self.watch_off_label(self.off_label)
-        self.watch_swap_red(self.swap_red)
-        self.refresh()
-
-
-    def on_button_pressed(self, event: Button.Pressed):
-        if event.button.id == "on":
-            logger.debug(f"'On' pressed on {event.control}")
-            self.post_message(self.TurnedOn(self))
-        elif event.button.id == "off":
-            logger.debug(f"'Off' pressed on {event.control}")
-            self.post_message(self.TurnedOff(self))
-
-
-    def watch_status(self, value: bool) -> None:
-        try:
-            light: RadioButton = self.query_exactly_one("#light")
-            light.value = value
-        except query.NoMatches:
-            if self.is_mounted:
-                raise
-
-
-    def watch_label(self, value):
-        try:
-            light: RadioButton = self.query_exactly_one("#light")
-            light.label = value
-        except query.NoMatches:
-            if self.is_mounted:
-                raise
-
-    def watch_on_label(self, value):
-        try:
-            on: Button = self.query_exactly_one("#on")
-            on.label = value
-        except query.NoMatches:
-            if self.is_mounted:
-                raise
-
-    def watch_off_label(self, value):
-        try:
-            off: Button = self.query_exactly_one("#off")
-            off.label = value
-        except query.NoMatches:
-            if self.is_mounted:
-                raise
-    
-    def watch_swap_red(self, value):
-        try:
-            on: Button = self.query_exactly_one("#on")
-            off: Button = self.query_exactly_one("#off")
-            off.variant = "success" if value else "error"
-            on.variant = "error" if value else "success"
-        except query.NoMatches:
-            if self.is_mounted:
-                raise
\ No newline at end of file