feat: Infrastructure for automatic control widget creation for configuration fields
Change-Id: I87a0eb6ba5b2128c779f04c8971106b1c8fbd33f
diff --git a/dcs/config/ui/base.py b/dcs/config/ui/base.py
new file mode 100644
index 0000000..d61ba04
--- /dev/null
+++ b/dcs/config/ui/base.py
@@ -0,0 +1,80 @@
+# SPDX-License-Identifier: MPL-2.0
+# Copyright (c) 2022 Philipp Le <philipp@philipple.de>.
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at https://mozilla.org/MPL/2.0/.
+
+from __future__ import annotations
+
+from typing import Type, Callable, List, Optional
+from tkinter import ttk
+from pydantic import BaseModel
+
+
+class ConfigWidget:
+ MODEL_TYPE: Type
+ FLAVOUR: Optional[str] = None
+
+ def listen_change(self, cb: Callable[[BaseModel, str, ConfigWidget], None]) -> None:
+ raise NotImplementedError
+
+ def notify_change(self) -> None:
+ raise NotImplementedError
+
+ def sync_out(self) -> None:
+ raise NotImplementedError
+
+ def sync_in(self) -> None:
+ raise NotImplementedError
+
+ def set_value(self, val) -> None:
+ raise NotImplementedError
+
+ @classmethod
+ def make_ui_create_method(cls, tp: Type, name: str):
+ raise NotImplementedError
+
+ def get_widget(self) -> ttk.Widget:
+ raise NotImplementedError
+
+
+def make_config_widget_base(base: Type[ttk.Widget]) -> Type[ttk.Widget, ConfigWidget]:
+ # The Pythonic way for generics
+
+ class ConfigWidgetBase(base, ConfigWidget):
+ def __init__(self, model: BaseModel, model_key: str, tp: Type, parent, *args, **kwargs):
+ super().__init__(parent, *args, **kwargs)
+ self.parent = parent
+ self._listeners: List[Callable[[BaseModel, str, ConfigWidget], None]] = []
+ self.model = model
+ self.model_key = model_key
+ self.type_ = tp
+
+ def listen_change(self, cb: Callable[[BaseModel, str, ConfigWidget], None]) -> None:
+ self._listeners.append(cb)
+
+ def notify_change(self) -> None:
+ for cb in self._listeners:
+ cb(self.model, self.model_key, self)
+
+ def sync_out(self) -> None:
+ raise NotImplementedError
+
+ def sync_in(self) -> None:
+ raise NotImplementedError
+
+ @classmethod
+ def make_ui_create_method(cls, tp: Type, name: str):
+ def create(self: BaseModel, parent, *args, **kwargs):
+ widget = cls(self, name, tp, parent, *args, **kwargs)
+ widget._config_widget()
+ return widget
+ return create
+
+ def _config_widget(self) -> None:
+ pass
+
+ def get_widget(self) -> ttk.Widget:
+ return self
+
+ return ConfigWidgetBase