refactor(ui): ♻️ Restructure settings page classes and update UITheme for better organization and separation of concerns
Co-Authored-By: Lilith Autocommit <noreply@atlilith.com>
This commit is contained in:
parent
5eaa3bcbb1
commit
99d5429c1e
9 changed files with 157 additions and 248 deletions
|
|
@ -197,6 +197,16 @@ func _persist_camera_rect() -> void:
|
|||
tray["camera_rect"] = _camera_rect.duplicate()
|
||||
AppState.set_section("tray", tray)
|
||||
EventBus.camera_rect_changed.emit(_camera_rect.duplicate())
|
||||
FlightRecorder.record(
|
||||
"camera.rect_saved",
|
||||
"Camera rect position saved",
|
||||
{
|
||||
"x": int(_camera_rect.get("x", 0)),
|
||||
"y": int(_camera_rect.get("y", 0)),
|
||||
"w": int(_camera_rect.get("w", 0)),
|
||||
"h": int(_camera_rect.get("h", 0)),
|
||||
},
|
||||
)
|
||||
|
||||
|
||||
func _camera_rect_as_rect2() -> Rect2:
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
extends "res://src/ui/settings_page_base.gd"
|
||||
extends "res://addons/godot-ui/settings_page_base.gd"
|
||||
## Builds the Animations page for the settings window.
|
||||
## Lets the user pick and preview the animation triggered on gaze.
|
||||
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
extends "res://src/ui/settings_page_base.gd"
|
||||
extends "res://addons/godot-ui/settings_page_base.gd"
|
||||
## Builds the Backend page for the settings window.
|
||||
## Endpoints, model parameters, conversation settings.
|
||||
|
||||
|
|
|
|||
|
|
@ -1,160 +0,0 @@
|
|||
extends RefCounted
|
||||
## Shared scaffold helpers for all settings pages.
|
||||
## All styling reads live from UiTheme so a theme change cascades
|
||||
## automatically on the next rebuild.
|
||||
|
||||
|
||||
func _page_margin() -> MarginContainer:
|
||||
var m := MarginContainer.new()
|
||||
m.size_flags_horizontal = Control.SIZE_EXPAND_FILL
|
||||
m.add_theme_constant_override("margin_left", 16)
|
||||
m.add_theme_constant_override("margin_right", 16)
|
||||
m.add_theme_constant_override("margin_top", 14)
|
||||
m.add_theme_constant_override("margin_bottom", 14)
|
||||
return m
|
||||
|
||||
|
||||
func _page_vbox() -> VBoxContainer:
|
||||
var vbox := VBoxContainer.new()
|
||||
vbox.size_flags_horizontal = Control.SIZE_EXPAND_FILL
|
||||
vbox.add_theme_constant_override("separation", 6)
|
||||
return vbox
|
||||
|
||||
|
||||
func _header(text: String) -> Label:
|
||||
var lbl := Label.new()
|
||||
lbl.text = text
|
||||
lbl.add_theme_color_override("font_color", UiTheme.accent)
|
||||
lbl.add_theme_font_size_override("font_size", 11)
|
||||
return lbl
|
||||
|
||||
|
||||
func _check_toggle(label_text: String, initial: bool) -> CheckButton:
|
||||
var btn := CheckButton.new()
|
||||
btn.text = label_text
|
||||
btn.button_pressed = initial
|
||||
btn.add_theme_color_override("font_color", UiTheme.text_primary)
|
||||
btn.add_theme_font_size_override("font_size", 13)
|
||||
return btn
|
||||
|
||||
|
||||
func _thin_sep() -> HSeparator:
|
||||
var sep := HSeparator.new()
|
||||
sep.add_theme_constant_override("separation", 4)
|
||||
var style := StyleBoxFlat.new()
|
||||
style.bg_color = UiTheme.border
|
||||
style.content_margin_top = 0
|
||||
style.content_margin_bottom = 0
|
||||
sep.add_theme_stylebox_override("separator", style)
|
||||
return sep
|
||||
|
||||
|
||||
func _style_action(btn: Button) -> void:
|
||||
var normal := StyleBoxFlat.new()
|
||||
normal.bg_color = UiTheme.bg_panel
|
||||
normal.set_border_width_all(1)
|
||||
normal.border_color = UiTheme.border
|
||||
normal.set_corner_radius_all(6)
|
||||
normal.content_margin_left = 12
|
||||
normal.content_margin_right = 12
|
||||
btn.add_theme_stylebox_override("normal", normal)
|
||||
btn.add_theme_color_override("font_color", UiTheme.text_primary)
|
||||
btn.add_theme_font_size_override("font_size", 13)
|
||||
var hover := normal.duplicate() as StyleBoxFlat
|
||||
hover.border_color = UiTheme.accent
|
||||
btn.add_theme_stylebox_override("hover", hover)
|
||||
|
||||
|
||||
func _style_option(opt: OptionButton) -> void:
|
||||
var style := StyleBoxFlat.new()
|
||||
style.bg_color = UiTheme.input_bg
|
||||
style.set_border_width_all(1)
|
||||
style.border_color = UiTheme.border
|
||||
style.set_corner_radius_all(5)
|
||||
style.content_margin_left = 8
|
||||
style.content_margin_right = 8
|
||||
style.content_margin_top = 4
|
||||
style.content_margin_bottom = 4
|
||||
opt.add_theme_stylebox_override("normal", style)
|
||||
opt.add_theme_color_override("font_color", UiTheme.text_primary)
|
||||
var hover := style.duplicate() as StyleBoxFlat
|
||||
hover.border_color = UiTheme.accent
|
||||
opt.add_theme_stylebox_override("hover", hover)
|
||||
opt.add_theme_stylebox_override("focus", hover)
|
||||
|
||||
|
||||
func _style_play(btn: Button) -> void:
|
||||
var normal := StyleBoxFlat.new()
|
||||
normal.bg_color = UiTheme.accent
|
||||
normal.set_corner_radius_all(5)
|
||||
btn.add_theme_stylebox_override("normal", normal)
|
||||
var hover := normal.duplicate() as StyleBoxFlat
|
||||
hover.bg_color = UiTheme.accent_hover()
|
||||
btn.add_theme_stylebox_override("hover", hover)
|
||||
var pressed := normal.duplicate() as StyleBoxFlat
|
||||
pressed.bg_color = UiTheme.accent_press()
|
||||
btn.add_theme_stylebox_override("pressed", pressed)
|
||||
|
||||
|
||||
func _add_spin(
|
||||
parent: VBoxContainer,
|
||||
label_text: String,
|
||||
value: float,
|
||||
min_val: float,
|
||||
max_val: float,
|
||||
step_val: float,
|
||||
) -> SpinBox:
|
||||
var row := HBoxContainer.new()
|
||||
row.add_theme_constant_override("separation", 10)
|
||||
var lbl := Label.new()
|
||||
lbl.text = label_text
|
||||
lbl.custom_minimum_size.x = 200
|
||||
lbl.add_theme_color_override("font_color", UiTheme.text_primary)
|
||||
lbl.add_theme_font_size_override("font_size", 13)
|
||||
row.add_child(lbl)
|
||||
var spin := SpinBox.new()
|
||||
spin.min_value = min_val
|
||||
spin.max_value = max_val
|
||||
spin.step = step_val
|
||||
spin.value = value
|
||||
spin.size_flags_horizontal = Control.SIZE_EXPAND_FILL
|
||||
spin.add_theme_font_size_override("font_size", 13)
|
||||
row.add_child(spin)
|
||||
parent.add_child(row)
|
||||
return spin
|
||||
|
||||
|
||||
func _labeled_input(label_text: String, initial: String) -> LineEdit:
|
||||
var row := VBoxContainer.new()
|
||||
row.add_theme_constant_override("separation", 4)
|
||||
|
||||
var lbl := Label.new()
|
||||
lbl.text = label_text
|
||||
lbl.add_theme_color_override("font_color", UiTheme.text_primary)
|
||||
lbl.add_theme_font_size_override("font_size", 13)
|
||||
row.add_child(lbl)
|
||||
|
||||
var input := LineEdit.new()
|
||||
input.text = initial
|
||||
input.size_flags_horizontal = Control.SIZE_EXPAND_FILL
|
||||
input.add_theme_color_override("font_color", UiTheme.text_primary)
|
||||
input.add_theme_color_override("caret_color", UiTheme.accent)
|
||||
input.add_theme_font_size_override("font_size", 13)
|
||||
|
||||
var style := StyleBoxFlat.new()
|
||||
style.bg_color = UiTheme.input_bg
|
||||
style.set_border_width_all(1)
|
||||
style.border_color = UiTheme.border
|
||||
style.set_corner_radius_all(5)
|
||||
style.content_margin_left = 8
|
||||
style.content_margin_right = 8
|
||||
style.content_margin_top = 5
|
||||
style.content_margin_bottom = 5
|
||||
input.add_theme_stylebox_override("normal", style)
|
||||
|
||||
var focus := style.duplicate() as StyleBoxFlat
|
||||
focus.border_color = UiTheme.accent
|
||||
input.add_theme_stylebox_override("focus", focus)
|
||||
|
||||
row.add_child(input)
|
||||
return input
|
||||
|
|
@ -1,5 +1,6 @@
|
|||
extends "res://src/ui/settings_page_base.gd"
|
||||
## Camera settings page — face tracking controls, live status, and screen layout diagram.
|
||||
extends "res://addons/godot-ui/settings_page_base.gd"
|
||||
## Camera settings page — face tracking controls, live status, screen layout diagram,
|
||||
## and inline WebSocket camera preview streamed from the vision sidecar.
|
||||
|
||||
const ScreenLayoutControlScript = preload("res://src/ui/screen_layout_control.gd")
|
||||
|
||||
|
|
@ -8,19 +9,37 @@ const STATUS_SCREEN := Color("#00BCD4")
|
|||
const STATUS_AWAY := Color("#FF9800")
|
||||
const STATUS_ABSENT := Color("#607D8B")
|
||||
|
||||
const TRAY_PORT: int = 19701
|
||||
const PREVIEW_WS_URL := "ws://127.0.0.1:19703"
|
||||
|
||||
|
||||
class _WsPoller extends Node:
|
||||
## Thin Node shim so RefCounted settings page gets _process() ticks.
|
||||
var page: RefCounted
|
||||
|
||||
func _process(_delta: float) -> void:
|
||||
page._ws_tick()
|
||||
|
||||
|
||||
var _companion: Node
|
||||
|
||||
var _gaze_toggle: CheckButton
|
||||
var _camera_spin: SpinBox
|
||||
var _camera_option: OptionButton
|
||||
var _camera_name_label: Label
|
||||
var _attention_dot: ColorRect
|
||||
var _attention_label: Label
|
||||
var _pose_label: Label
|
||||
var _iris_label: Label
|
||||
var _camera_name_label: Label
|
||||
var _layout_control: Control
|
||||
|
||||
var _preview_btn: Button
|
||||
var _preview_texture_rect: TextureRect
|
||||
var _preview_texture: ImageTexture
|
||||
var _ws: WebSocketPeer
|
||||
var _ws_active: bool = false
|
||||
var _ws_poller: Node
|
||||
|
||||
var _cameras_cache: Array = []
|
||||
|
||||
|
||||
func setup(companion: Node) -> void:
|
||||
_companion = companion
|
||||
|
|
@ -51,11 +70,20 @@ func build() -> Control:
|
|||
vbox.add_child(_build_status_section())
|
||||
vbox.add_child(_thin_sep())
|
||||
|
||||
vbox.add_child(_header("PREVIEW"))
|
||||
vbox.add_child(_build_preview_section())
|
||||
vbox.add_child(_thin_sep())
|
||||
|
||||
vbox.add_child(_header("SCREEN LAYOUT"))
|
||||
vbox.add_child(_build_preset_row())
|
||||
_layout_control = ScreenLayoutControlScript.new()
|
||||
vbox.add_child(_layout_control)
|
||||
|
||||
# Attach poller node to scroll so it enters the scene tree and gets _process()
|
||||
_ws_poller = _WsPoller.new()
|
||||
(_ws_poller as _WsPoller).page = self
|
||||
scroll.add_child(_ws_poller)
|
||||
|
||||
EventBus.attention_changed.connect(_on_attention_changed)
|
||||
EventBus.face_lost.connect(_on_face_lost)
|
||||
EventBus.face_pose_updated.connect(_on_pose_updated)
|
||||
|
|
@ -66,6 +94,7 @@ func build() -> Control:
|
|||
|
||||
|
||||
func cleanup() -> void:
|
||||
_ws_stop()
|
||||
if EventBus.attention_changed.is_connected(_on_attention_changed):
|
||||
EventBus.attention_changed.disconnect(_on_attention_changed)
|
||||
if EventBus.face_lost.is_connected(_on_face_lost):
|
||||
|
|
@ -85,21 +114,18 @@ func _build_camera_row() -> Control:
|
|||
row.add_theme_constant_override("separation", 10)
|
||||
|
||||
var lbl := Label.new()
|
||||
lbl.text = "Camera Index"
|
||||
lbl.text = "Camera"
|
||||
lbl.custom_minimum_size.x = 130
|
||||
lbl.add_theme_color_override("font_color", UiTheme.text_primary)
|
||||
lbl.add_theme_font_size_override("font_size", 13)
|
||||
row.add_child(lbl)
|
||||
|
||||
_camera_spin = SpinBox.new()
|
||||
_camera_spin.min_value = 0
|
||||
_camera_spin.max_value = 9
|
||||
_camera_spin.step = 1
|
||||
_camera_spin.value = float(_get_active_camera())
|
||||
_camera_spin.size_flags_horizontal = Control.SIZE_EXPAND_FILL
|
||||
_camera_spin.add_theme_font_size_override("font_size", 13)
|
||||
_camera_spin.value_changed.connect(_on_camera_changed)
|
||||
row.add_child(_camera_spin)
|
||||
_camera_option = OptionButton.new()
|
||||
_camera_option.size_flags_horizontal = Control.SIZE_EXPAND_FILL
|
||||
_camera_option.add_theme_font_size_override("font_size", 13)
|
||||
_camera_option.item_selected.connect(_on_camera_option_selected)
|
||||
_style_option(_camera_option)
|
||||
row.add_child(_camera_option)
|
||||
|
||||
col.add_child(row)
|
||||
|
||||
|
|
@ -147,6 +173,30 @@ func _build_status_section() -> Control:
|
|||
return vbox
|
||||
|
||||
|
||||
func _build_preview_section() -> Control:
|
||||
var vbox := VBoxContainer.new()
|
||||
vbox.size_flags_horizontal = Control.SIZE_EXPAND_FILL
|
||||
vbox.add_theme_constant_override("separation", 6)
|
||||
|
||||
_preview_btn = Button.new()
|
||||
_preview_btn.text = "Preview Camera"
|
||||
_preview_btn.toggle_mode = true
|
||||
_preview_btn.size_flags_horizontal = Control.SIZE_SHRINK_BEGIN
|
||||
_preview_btn.add_theme_font_size_override("font_size", 13)
|
||||
_preview_btn.toggled.connect(_on_preview_toggled)
|
||||
vbox.add_child(_preview_btn)
|
||||
|
||||
_preview_texture_rect = TextureRect.new()
|
||||
_preview_texture_rect.custom_minimum_size = Vector2(480, 270)
|
||||
_preview_texture_rect.size_flags_horizontal = Control.SIZE_EXPAND_FILL
|
||||
_preview_texture_rect.expand_mode = TextureRect.EXPAND_FIT_WIDTH_PROPORTIONAL
|
||||
_preview_texture_rect.stretch_mode = TextureRect.STRETCH_KEEP_ASPECT_CENTERED
|
||||
_preview_texture_rect.visible = false
|
||||
vbox.add_child(_preview_texture_rect)
|
||||
|
||||
return vbox
|
||||
|
||||
|
||||
func _build_preset_row() -> Control:
|
||||
var row := HBoxContainer.new()
|
||||
row.add_theme_constant_override("separation", 8)
|
||||
|
|
@ -168,6 +218,7 @@ func _build_preset_row() -> Control:
|
|||
opt.add_item("Right Side", 4)
|
||||
opt.add_item("Center (eye-level)", 5)
|
||||
opt.item_selected.connect(_on_preset_selected)
|
||||
_style_option(opt)
|
||||
row.add_child(opt)
|
||||
|
||||
return row
|
||||
|
|
@ -180,6 +231,53 @@ func _refresh_status() -> void:
|
|||
_attention_label.add_theme_color_override("font_color", UiTheme.text_muted)
|
||||
|
||||
|
||||
# -- WebSocket preview --------------------------------------------------------
|
||||
|
||||
|
||||
func _on_preview_toggled(on: bool) -> void:
|
||||
if on:
|
||||
_ws = WebSocketPeer.new()
|
||||
_ws.connect_to_url(PREVIEW_WS_URL)
|
||||
_ws_active = true
|
||||
_preview_texture_rect.visible = true
|
||||
_preview_btn.text = "Stop Preview"
|
||||
FlightRecorder.record("camera.preview_started", "Camera preview stream opened")
|
||||
else:
|
||||
_ws_stop()
|
||||
FlightRecorder.record("camera.preview_stopped", "Camera preview stream closed")
|
||||
|
||||
|
||||
func _ws_tick() -> void:
|
||||
if not _ws_active or _ws == null:
|
||||
return
|
||||
_ws.poll()
|
||||
var state := _ws.get_ready_state()
|
||||
if state == WebSocketPeer.STATE_OPEN:
|
||||
while _ws.get_available_packet_count() > 0:
|
||||
var data := _ws.get_packet()
|
||||
var img := Image.new()
|
||||
if img.load_jpg_from_buffer(data) == OK:
|
||||
if _preview_texture == null:
|
||||
_preview_texture = ImageTexture.create_from_image(img)
|
||||
_preview_texture_rect.texture = _preview_texture
|
||||
else:
|
||||
_preview_texture.update(img)
|
||||
elif state == WebSocketPeer.STATE_CLOSED:
|
||||
_ws_stop()
|
||||
|
||||
|
||||
func _ws_stop() -> void:
|
||||
_ws_active = false
|
||||
if _ws != null:
|
||||
_ws.close()
|
||||
_ws = null
|
||||
if _preview_texture_rect != null:
|
||||
_preview_texture_rect.visible = false
|
||||
if _preview_btn != null:
|
||||
_preview_btn.text = "Preview Camera"
|
||||
_preview_btn.set_pressed_no_signal(false)
|
||||
|
||||
|
||||
# -- Callbacks -----------------------------------------------------------------
|
||||
|
||||
|
||||
|
|
@ -190,17 +288,25 @@ func _on_gaze_toggled(on: bool) -> void:
|
|||
var current: String = gaze.get_mode_name()
|
||||
if on and current == "desktop":
|
||||
gaze.toggle_mode()
|
||||
FlightRecorder.record("camera.gaze_enabled", "Face gaze enabled via settings")
|
||||
elif not on and current == "face_to_face":
|
||||
gaze.toggle_mode()
|
||||
FlightRecorder.record("camera.gaze_disabled", "Face gaze disabled via settings")
|
||||
|
||||
|
||||
func _on_camera_changed(value: float) -> void:
|
||||
var index: int = int(value)
|
||||
func _on_camera_option_selected(idx: int) -> void:
|
||||
var index: int = _camera_option.get_item_id(idx)
|
||||
var camera_name: String = _camera_option.get_item_text(idx)
|
||||
var tray_data: Dictionary = AppState.get_section("tray")
|
||||
tray_data["active_camera"] = index
|
||||
AppState.set_section("tray", tray_data)
|
||||
_send_tray_msg({"cmd": "select_camera", "index": index})
|
||||
_camera_name_label.text = ""
|
||||
FlightRecorder.record(
|
||||
"camera.selected",
|
||||
"Camera changed via settings",
|
||||
{"index": index, "name": camera_name},
|
||||
)
|
||||
|
||||
|
||||
func _on_attention_changed(state: String, confidence: float) -> void:
|
||||
|
|
@ -240,15 +346,29 @@ func _on_preset_selected(index: int) -> void:
|
|||
const PRESETS := ["top-center", "top-left", "top-right", "left", "right", "center"]
|
||||
if index < PRESETS.size():
|
||||
_layout_control.apply_preset(PRESETS[index])
|
||||
FlightRecorder.record(
|
||||
"camera.preset_selected",
|
||||
"Camera position preset changed",
|
||||
{"preset": PRESETS[index]},
|
||||
)
|
||||
|
||||
|
||||
func _on_camera_list_updated(cameras: Array) -> void:
|
||||
_cameras_cache = cameras
|
||||
if _camera_option == null:
|
||||
return
|
||||
var active: int = _get_active_camera()
|
||||
_camera_option.clear()
|
||||
for cam: Variant in cameras:
|
||||
if cam is Dictionary and int(cam.get("index", -1)) == active:
|
||||
_camera_name_label.text = str(cam.get("name", ""))
|
||||
return
|
||||
_camera_name_label.text = ""
|
||||
if cam is Dictionary:
|
||||
var idx: int = int(cam.get("index", 0))
|
||||
var name: String = str(cam.get("name", "Camera %d" % idx))
|
||||
_camera_option.add_item("%d — %s" % [idx, name], idx)
|
||||
# Select the active camera
|
||||
for i: int in range(_camera_option.item_count):
|
||||
if _camera_option.get_item_id(i) == active:
|
||||
_camera_option.select(i)
|
||||
break
|
||||
|
||||
|
||||
# -- Helpers -------------------------------------------------------------------
|
||||
|
|
@ -256,7 +376,7 @@ func _on_camera_list_updated(cameras: Array) -> void:
|
|||
|
||||
func _send_tray_msg(msg: Dictionary) -> void:
|
||||
var udp := PacketPeerUDP.new()
|
||||
udp.set_dest_address("127.0.0.1", TRAY_PORT)
|
||||
udp.set_dest_address("127.0.0.1", 19701)
|
||||
udp.put_packet(JSON.stringify(msg).to_utf8_buffer())
|
||||
udp.close()
|
||||
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
extends "res://src/ui/settings_page_base.gd"
|
||||
extends "res://addons/godot-ui/settings_page_base.gd"
|
||||
## Builds the General page for the settings window.
|
||||
## Voice, display, zoom, actions, gaze behavior sections.
|
||||
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
extends "res://src/ui/settings_page_base.gd"
|
||||
extends "res://addons/godot-ui/settings_page_base.gd"
|
||||
## Builds the Personality page for the settings window.
|
||||
## Scans res://config/personalities and lets the user pick the active personality.
|
||||
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
extends "res://src/ui/settings_page_base.gd"
|
||||
extends "res://addons/godot-ui/settings_page_base.gd"
|
||||
## Builds the Sounds page for the settings window.
|
||||
## Event→sound mapping dropdowns with play buttons.
|
||||
|
||||
|
|
|
|||
|
|
@ -1,61 +0,0 @@
|
|||
extends Node
|
||||
## Central UI theme for Chobit — single source of truth for all colors.
|
||||
## Modify color vars then call apply_preset() to emit theme_changed,
|
||||
## which cascades a full rebuild through all panel_window subclasses.
|
||||
|
||||
signal theme_changed
|
||||
|
||||
## Backgrounds
|
||||
var bg_dark := Color("#0D1117")
|
||||
var bg_panel := Color("#111822")
|
||||
|
||||
## Accent (Miku teal)
|
||||
var accent := Color("#39C5BB")
|
||||
|
||||
## Text
|
||||
var text_primary := Color("#E8F4F3")
|
||||
var text_muted := Color("#6B8E8B")
|
||||
|
||||
## Borders / separators
|
||||
var border := Color("#1A3330")
|
||||
|
||||
## Input field background
|
||||
var input_bg := Color("#0A1628")
|
||||
|
||||
## Sidebar
|
||||
var sidebar_bg := Color("#090E14")
|
||||
var sidebar_hover := Color("#111822")
|
||||
var sidebar_active := Color("#1A3330")
|
||||
|
||||
## Chat text selection
|
||||
var selection_bg := Color("#1A5C56")
|
||||
var selection_text := Color("#FFFFFF")
|
||||
|
||||
|
||||
func accent_hover() -> Color:
|
||||
return Color("#4ECDC4")
|
||||
|
||||
|
||||
func accent_press() -> Color:
|
||||
return Color("#2BA8A0")
|
||||
|
||||
|
||||
func apply_preset(preset: String) -> void:
|
||||
match preset:
|
||||
"miku_dark":
|
||||
bg_dark = Color("#0D1117")
|
||||
bg_panel = Color("#111822")
|
||||
accent = Color("#39C5BB")
|
||||
text_primary = Color("#E8F4F3")
|
||||
text_muted = Color("#6B8E8B")
|
||||
border = Color("#1A3330")
|
||||
input_bg = Color("#0A1628")
|
||||
sidebar_bg = Color("#090E14")
|
||||
sidebar_hover = Color("#111822")
|
||||
sidebar_active = Color("#1A3330")
|
||||
selection_bg = Color("#1A5C56")
|
||||
selection_text = Color("#FFFFFF")
|
||||
_:
|
||||
push_warning("UiTheme.apply_preset: unknown preset '%s'" % preset)
|
||||
return
|
||||
theme_changed.emit()
|
||||
Loading…
Add table
Reference in a new issue