"""Тёмная и светлая темы для Dear PyGui.""" from __future__ import annotations import dearpygui.dearpygui as dpg from ..core.model import Risk DARK = { "bg": (27, 29, 34), "bg2": (34, 37, 43), "bg3": (42, 46, 54), "fg": (230, 232, 236), "fg_dim": (154, 161, 173), "accent": (74, 144, 217), "accent_hi": (95, 164, 232), "ok": (62, 181, 110), "warn": (217, 154, 43), "danger": (217, 83, 79), "border": (52, 57, 67), "sel": (47, 58, 74), } LIGHT = { "bg": (244, 245, 247), "bg2": (255, 255, 255), "bg3": (233, 235, 239), "fg": (29, 33, 39), "fg_dim": (95, 103, 115), "accent": (47, 111, 181), "accent_hi": (61, 130, 207), "ok": (30, 122, 69), "warn": (165, 113, 15), "danger": (184, 51, 47), "border": (207, 212, 220), "sel": (214, 228, 245), } def risk_color(risk: Risk, palette: dict) -> tuple[int, int, int]: return { Risk.SAFE: palette["ok"], Risk.LOW: palette["accent_hi"], Risk.MEDIUM: palette["warn"], Risk.HIGH: palette["danger"], }[risk] def _rgba(c: tuple[int, int, int], a: int = 255) -> tuple[int, int, int, int]: return (c[0], c[1], c[2], a) def build_theme(p: dict) -> int: """Глобальная тема под палитру ``p``.""" with dpg.theme() as theme: with dpg.theme_component(dpg.mvAll): col = dpg.add_theme_color col(dpg.mvThemeCol_WindowBg, _rgba(p["bg"])) col(dpg.mvThemeCol_ChildBg, _rgba(p["bg"])) col(dpg.mvThemeCol_PopupBg, _rgba(p["bg2"])) col(dpg.mvThemeCol_Border, _rgba(p["border"])) col(dpg.mvThemeCol_BorderShadow, (0, 0, 0, 0)) col(dpg.mvThemeCol_Text, _rgba(p["fg"])) col(dpg.mvThemeCol_TextDisabled, _rgba(p["fg_dim"])) col(dpg.mvThemeCol_FrameBg, _rgba(p["bg3"])) col(dpg.mvThemeCol_FrameBgHovered, _rgba(p["border"])) col(dpg.mvThemeCol_FrameBgActive, _rgba(p["sel"])) col(dpg.mvThemeCol_TitleBg, _rgba(p["bg2"])) col(dpg.mvThemeCol_TitleBgActive, _rgba(p["bg2"])) col(dpg.mvThemeCol_TitleBgCollapsed, _rgba(p["bg2"])) col(dpg.mvThemeCol_MenuBarBg, _rgba(p["bg2"])) col(dpg.mvThemeCol_Button, _rgba(p["bg3"])) col(dpg.mvThemeCol_ButtonHovered, _rgba(p["border"])) col(dpg.mvThemeCol_ButtonActive, _rgba(p["sel"])) col(dpg.mvThemeCol_Header, _rgba(p["bg2"])) col(dpg.mvThemeCol_HeaderHovered, _rgba(p["sel"])) col(dpg.mvThemeCol_HeaderActive, _rgba(p["sel"])) col(dpg.mvThemeCol_Tab, _rgba(p["bg"])) col(dpg.mvThemeCol_TabHovered, _rgba(p["bg3"])) col(dpg.mvThemeCol_TabActive, _rgba(p["bg2"])) col(dpg.mvThemeCol_TabUnfocused, _rgba(p["bg"])) col(dpg.mvThemeCol_TabUnfocusedActive, _rgba(p["bg2"])) col(dpg.mvThemeCol_CheckMark, _rgba(p["accent_hi"])) col(dpg.mvThemeCol_SliderGrab, _rgba(p["accent"])) col(dpg.mvThemeCol_SliderGrabActive, _rgba(p["accent_hi"])) col(dpg.mvThemeCol_PlotHistogram, _rgba(p["accent"])) col(dpg.mvThemeCol_PlotHistogramHovered, _rgba(p["accent_hi"])) col(dpg.mvThemeCol_ScrollbarBg, _rgba(p["bg"])) col(dpg.mvThemeCol_ScrollbarGrab, _rgba(p["bg3"])) col(dpg.mvThemeCol_ScrollbarGrabHovered, _rgba(p["border"])) col(dpg.mvThemeCol_ScrollbarGrabActive, _rgba(p["accent"])) col(dpg.mvThemeCol_Separator, _rgba(p["border"])) col(dpg.mvThemeCol_TableHeaderBg, _rgba(p["bg3"])) col(dpg.mvThemeCol_TableBorderStrong, _rgba(p["border"])) col(dpg.mvThemeCol_TableBorderLight, _rgba(p["border"])) col(dpg.mvThemeCol_TableRowBg, _rgba(p["bg"])) col(dpg.mvThemeCol_TableRowBgAlt, _rgba(p["bg2"], 120)) st = dpg.add_theme_style st(dpg.mvStyleVar_FrameRounding, 4) st(dpg.mvStyleVar_ChildRounding, 6) st(dpg.mvStyleVar_PopupRounding, 6) st(dpg.mvStyleVar_WindowRounding, 6) st(dpg.mvStyleVar_GrabRounding, 4) st(dpg.mvStyleVar_TabRounding, 4) st(dpg.mvStyleVar_ScrollbarRounding, 6) st(dpg.mvStyleVar_FramePadding, 10, 6) st(dpg.mvStyleVar_ItemSpacing, 9, 7) st(dpg.mvStyleVar_ItemInnerSpacing, 7, 5) st(dpg.mvStyleVar_WindowPadding, 14, 12) st(dpg.mvStyleVar_CellPadding, 7, 5) st(dpg.mvStyleVar_ScrollbarSize, 13) st(dpg.mvStyleVar_WindowBorderSize, 0) st(dpg.mvStyleVar_FrameBorderSize, 0) return theme def build_accent_button_theme(p: dict, base: str = "accent") -> int: """Тема для выделенной кнопки (основное действие / опасное действие).""" color = p[base] hi = tuple(min(255, v + 22) for v in color) with dpg.theme() as theme: with dpg.theme_component(dpg.mvAll): dpg.add_theme_color(dpg.mvThemeCol_Button, _rgba(color)) dpg.add_theme_color(dpg.mvThemeCol_ButtonHovered, _rgba(hi)) dpg.add_theme_color(dpg.mvThemeCol_ButtonActive, _rgba(color)) dpg.add_theme_color(dpg.mvThemeCol_Text, (255, 255, 255, 255)) dpg.add_theme_style(dpg.mvStyleVar_FramePadding, 14, 8) return theme def build_card_theme(p: dict) -> int: """Тема для «карточки» — дочернего окна с более светлым фоном.""" with dpg.theme() as theme: with dpg.theme_component(dpg.mvAll): dpg.add_theme_color(dpg.mvThemeCol_ChildBg, _rgba(p["bg2"])) dpg.add_theme_style(dpg.mvStyleVar_ChildRounding, 8) dpg.add_theme_style(dpg.mvStyleVar_WindowPadding, 16, 14) return theme def build_bar_theme(p: dict, color_key: str) -> int: """Тема полосы заполнения (диски, категории).""" with dpg.theme() as theme: with dpg.theme_component(dpg.mvAll): dpg.add_theme_color(dpg.mvThemeCol_PlotHistogram, _rgba(p[color_key])) dpg.add_theme_color(dpg.mvThemeCol_FrameBg, _rgba(p["bg3"])) dpg.add_theme_style(dpg.mvStyleVar_FrameRounding, 3) return theme