feat(dtmm): Implement gruvbox dark theme

This commit is contained in:
Lucas Schwiderski 2023-03-14 17:25:09 +01:00
parent db37e5fa07
commit 4c33741b03
Signed by: lucas
GPG key ID: AA12679AAA6DF4D8
13 changed files with 627 additions and 46 deletions

3
.gitmodules vendored
View file

@ -4,3 +4,6 @@
[submodule "lib/steamlocate-rs"]
path = lib/steamlocate-rs
url = git@github.com:sclu1034/steamlocate-rs.git
[submodule "crates/dtmm/assets/icons"]
path = crates/dtmm/assets/icons
url = https://github.com/tabler/tabler-icons

312
Cargo.lock generated
View file

@ -44,6 +44,12 @@ version = "1.0.69"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "224afbd727c3d6e4b90103ece64b8d1b67fbb1973b1046c2281eed3f3803f800"
[[package]]
name = "arrayref"
version = "0.3.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a4c527152e37cf757a3f78aae5a06fbeefdb07ccc535c980a3208ee3060dd544"
[[package]]
name = "arrayvec"
version = "0.5.2"
@ -118,6 +124,12 @@ dependencies = [
"rustc-demangle",
]
[[package]]
name = "base64"
version = "0.13.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9e1b586273c5702936fe7b7d6896644d8be71e6314cfe09d3167c95f712589e8"
[[package]]
name = "base64ct"
version = "1.6.0"
@ -411,6 +423,12 @@ version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3d7b894f5411737b7867f4827955924d7c254fc9f4d91a6aad6b097804b1018b"
[[package]]
name = "colors-transform"
version = "0.2.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9226dbc05df4fb986f48d730b001532580883c4c06c5d1c213f4b34c1c157178"
[[package]]
name = "confy"
version = "0.5.1"
@ -580,6 +598,12 @@ dependencies = [
"memchr",
]
[[package]]
name = "data-url"
version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8d7439c3735f405729d52c3fbbe4de140eaf938a1fe47d227c27f8254d4302a5"
[[package]]
name = "digest"
version = "0.10.6"
@ -666,11 +690,14 @@ dependencies = [
"fnv",
"im",
"instant",
"resvg",
"tiny-skia",
"tracing",
"tracing-subscriber",
"tracing-wasm",
"unic-langid",
"unicode-segmentation",
"usvg",
"xi-unicode",
]
@ -723,6 +750,7 @@ dependencies = [
"bitflags",
"clap",
"color-eyre",
"colors-transform",
"confy",
"druid",
"dtmt-shared",
@ -911,6 +939,12 @@ dependencies = [
"miniz_oxide",
]
[[package]]
name = "float-cmp"
version = "0.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "98de4bbd547a563b716d8dfa9aad1cb19bfab00f4fa09a6a4ed21dbcf44ce9c4"
[[package]]
name = "fluent-bundle"
version = "0.15.2"
@ -951,6 +985,27 @@ version = "1.0.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1"
[[package]]
name = "fontconfig-parser"
version = "0.5.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4ab2e12762761366dcb876ab8b6e0cfa4797ddcd890575919f008b5ba655672a"
dependencies = [
"roxmltree 0.18.0",
]
[[package]]
name = "fontdb"
version = "0.9.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d52186a39c335aa6f79fc0bf1c3cf854870b6ad4e50a7bb8a59b4ba1331f478a"
dependencies = [
"fontconfig-parser",
"log",
"memmap2",
"ttf-parser",
]
[[package]]
name = "foreign-types"
version = "0.3.2"
@ -1150,6 +1205,16 @@ dependencies = [
"wasi",
]
[[package]]
name = "gif"
version = "0.11.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3edd93c6756b4dfaf2709eafcc345ba2636565295c198a9cfbf75fa5e3e00b06"
dependencies = [
"color_quant",
"weezl",
]
[[package]]
name = "gimli"
version = "0.27.2"
@ -1533,6 +1598,15 @@ dependencies = [
"libc",
]
[[package]]
name = "kurbo"
version = "0.8.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7a53776d271cfb873b17c618af0298445c88afc52837f3e948fa3fafd131f449"
dependencies = [
"arrayvec 0.7.2",
]
[[package]]
name = "kurbo"
version = "0.9.1"
@ -1621,6 +1695,15 @@ version = "2.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d"
[[package]]
name = "memmap2"
version = "0.5.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "83faa42c0a078c393f6b29d5db232d8be22776a891f8f56e5284faee4a20b327"
dependencies = [
"libc",
]
[[package]]
name = "memoffset"
version = "0.6.5"
@ -2053,6 +2136,12 @@ dependencies = [
"sha2",
]
[[package]]
name = "pico-args"
version = "0.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5be167a7af36ee22fe3115051bc51f6e6c7054c9348e28deb4f49bd6f705a315"
[[package]]
name = "piet"
version = "0.6.2"
@ -2060,7 +2149,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e381186490a3e2017a506d62b759ea8eaf4be14666b13ed53973e8ae193451b1"
dependencies = [
"image",
"kurbo",
"kurbo 0.9.1",
"unic-bidi",
]
@ -2256,6 +2345,12 @@ dependencies = [
"rand_core",
]
[[package]]
name = "rctree"
version = "0.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3b42e27ef78c35d3998403c1d26f3efd9e135d3e5121b0a4845cc5cc27547f4f"
[[package]]
name = "redox_syscall"
version = "0.2.16"
@ -2302,6 +2397,51 @@ version = "0.6.28"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "456c603be3e8d448b072f410900c09faf164fbce2d480456f50eea6e25f9c848"
[[package]]
name = "resvg"
version = "0.25.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8ea0337740f86c70141e7596d81c2e76c0cd3726dbcee053ac18d0ec45101f8e"
dependencies = [
"gif",
"jpeg-decoder",
"log",
"pico-args",
"png",
"rgb",
"svgfilters",
"svgtypes",
"tiny-skia",
"usvg",
]
[[package]]
name = "rgb"
version = "0.8.36"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "20ec2d3e3fc7a92ced357df9cebd5a10b6fb2aa1ee797bf7e9ce2f17dffc8f59"
dependencies = [
"bytemuck",
]
[[package]]
name = "roxmltree"
version = "0.15.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6b9de9831a129b122e7e61f242db509fa9d0838008bf0b29bb0624669edfe48a"
dependencies = [
"xmlparser",
]
[[package]]
name = "roxmltree"
version = "0.18.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d8f595a457b6b8c6cda66a48503e92ee8d19342f905948f29c383200ec9eb1d8"
dependencies = [
"xmlparser",
]
[[package]]
name = "rustc-demangle"
version = "0.1.21"
@ -2337,6 +2477,22 @@ dependencies = [
"windows-sys 0.45.0",
]
[[package]]
name = "rustybuzz"
version = "0.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ab9e34ecf6900625412355a61bda0bd68099fe674de707c67e5e4aed2c05e489"
dependencies = [
"bitflags",
"bytemuck",
"smallvec",
"ttf-parser",
"unicode-bidi-mirroring",
"unicode-ccc",
"unicode-general-category",
"unicode-script",
]
[[package]]
name = "rustyline"
version = "9.1.2"
@ -2490,6 +2646,21 @@ dependencies = [
"libc",
]
[[package]]
name = "simplecss"
version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a11be7c62927d9427e9f40f3444d5499d868648e2edbc4e2116de69e7ec0e89d"
dependencies = [
"log",
]
[[package]]
name = "siphasher"
version = "0.3.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7bd3e3206899af3f8b12af284fafc038cc1dc2b41d1b89dd17297221c5d225de"
[[package]]
name = "sized-chunks"
version = "0.6.5"
@ -2557,6 +2728,15 @@ version = "1.0.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9e08d8363704e6c71fc928674353e6b7c23dcea9d82d7012c8faf2a3a025f8d0"
[[package]]
name = "strict-num"
version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9df65f20698aeed245efdde3628a6b559ea1239bbb871af1b6e3b58c413b2bd1"
dependencies = [
"float-cmp",
]
[[package]]
name = "string_template"
version = "0.2.1"
@ -2587,6 +2767,25 @@ version = "2.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6bdef32e8150c2a081110b42772ffe7d7c9032b606bc226c8260fd97e0976601"
[[package]]
name = "svgfilters"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "639abcebc15fdc2df179f37d6f5463d660c1c79cd552c12343a4600827a04bce"
dependencies = [
"float-cmp",
"rgb",
]
[[package]]
name = "svgtypes"
version = "0.8.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "22975e8a2bac6a76bb54f898a6b18764633b00e780330f0b689f65afb3975564"
dependencies = [
"siphasher",
]
[[package]]
name = "syn"
version = "1.0.109"
@ -2692,6 +2891,31 @@ dependencies = [
"time-core",
]
[[package]]
name = "tiny-skia"
version = "0.8.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bfef3412c6975196fdfac41ef232f910be2bb37b9dd3313a49a1a6bc815a5bdb"
dependencies = [
"arrayref",
"arrayvec 0.7.2",
"bytemuck",
"cfg-if",
"png",
"tiny-skia-path",
]
[[package]]
name = "tiny-skia-path"
version = "0.8.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a4b5edac058fc98f51c935daea4d805b695b38e2f151241cad125ade2a2ac20d"
dependencies = [
"arrayref",
"bytemuck",
"strict-num",
]
[[package]]
name = "tinystr"
version = "0.7.1"
@ -2851,6 +3075,12 @@ dependencies = [
"wasm-bindgen",
]
[[package]]
name = "ttf-parser"
version = "0.17.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "375812fa44dab6df41c195cd2f7fecb488f6c09fbaafb62807488cefab642bff"
[[package]]
name = "type-map"
version = "0.4.0"
@ -2950,24 +3180,86 @@ dependencies = [
"version_check",
]
[[package]]
name = "unicode-bidi"
version = "0.3.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "524b68aca1d05e03fdf03fcdce2c6c94b6daf6d16861ddaa7e4f2b6638a9052c"
[[package]]
name = "unicode-bidi-mirroring"
version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "56d12260fb92d52f9008be7e4bca09f584780eb2266dc8fecc6a192bec561694"
[[package]]
name = "unicode-ccc"
version = "0.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cc2520efa644f8268dce4dcd3050eaa7fc044fca03961e9998ac7e2e92b77cf1"
[[package]]
name = "unicode-general-category"
version = "0.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2281c8c1d221438e373249e065ca4989c4c36952c211ff21a0ee91c44a3869e7"
[[package]]
name = "unicode-ident"
version = "1.0.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e5464a87b239f13a63a501f2701565754bae92d243d4bb7eb12f6d57d2269bf4"
[[package]]
name = "unicode-script"
version = "0.5.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7d817255e1bed6dfd4ca47258685d14d2bdcfbc64fdc9e3819bd5848057b8ecc"
[[package]]
name = "unicode-segmentation"
version = "1.10.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1dd624098567895118886609431a7c3b8f516e41d30e0643f03d94592a147e36"
[[package]]
name = "unicode-vo"
version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b1d386ff53b415b7fe27b50bb44679e2cc4660272694b7b6f3326d8480823a94"
[[package]]
name = "unicode-width"
version = "0.1.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c0edd1e5b14653f783770bce4a4dabb4a5108a5370a5f5d8cfe8710c361f6c8b"
[[package]]
name = "usvg"
version = "0.25.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "585bb2d87c8fd6041a479dea01479dcf9094e61b5f9af221606927e61a2bd939"
dependencies = [
"base64",
"data-url",
"flate2",
"fontdb",
"kurbo 0.8.3",
"log",
"pico-args",
"rctree",
"roxmltree 0.15.1",
"rustybuzz",
"simplecss",
"siphasher",
"strict-num",
"svgtypes",
"unicode-bidi",
"unicode-script",
"unicode-vo",
"xmlwriter",
]
[[package]]
name = "utf16_lit"
version = "2.0.2"
@ -3100,6 +3392,12 @@ dependencies = [
"wasm-bindgen",
]
[[package]]
name = "weezl"
version = "0.1.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9193164d4de03a926d909d3bc7c30543cecb35400c02114792c2cae20d5e2dbb"
[[package]]
name = "winapi"
version = "0.3.9"
@ -3245,6 +3543,18 @@ version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a67300977d3dc3f8034dae89778f502b6ba20b269527b3223ba59c0cf393bb8a"
[[package]]
name = "xmlparser"
version = "0.13.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4d25c75bf9ea12c4040a97f829154768bbbce366287e2dc044af160cd79a13fd"
[[package]]
name = "xmlwriter"
version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ec7a2a501ed189703dba8b08142f057e887dfc4b2cc4db2d343ac6376ba3e0b9"
[[package]]
name = "zip"
version = "0.6.4"

View file

@ -10,7 +10,7 @@ bitflags = "1.3.2"
clap = { version = "4.0.15", features = ["color", "derive", "std", "cargo", "string", "unicode"] }
color-eyre = "0.6.2"
confy = "0.5.1"
druid = { git = "https://github.com/linebender/druid.git", features = ["im", "serde", "image", "png", "jpeg", "bmp", "webp"] }
druid = { git = "https://github.com/linebender/druid.git", features = ["im", "serde", "image", "png", "jpeg", "bmp", "webp", "svg"] }
dtmt-shared = { path = "../../lib/dtmt-shared", version = "*" }
futures = "0.3.25"
oodle-sys = { path = "../../lib/oodle-sys", version = "*" }
@ -27,3 +27,4 @@ path-slash = "0.2.1"
time = { version = "0.3.20", features = ["serde", "serde-well-known", "local-offset"] }
strip-ansi-escapes = "0.1.1"
lazy_static = "1.4.0"
colors-transform = "0.2.11"

@ -0,0 +1 @@
Subproject commit 74838ded9980b6f134bb6f7edcf916cca4a2d97f

View file

@ -20,6 +20,7 @@ use crate::controller::app::load_mods;
use crate::controller::worker::work_thread;
use crate::state::ACTION_SHOW_ERROR_DIALOG;
use crate::state::{Delegate, State};
use crate::ui::theme;
mod controller;
mod state;
@ -64,7 +65,9 @@ fn main() -> Result<()> {
let (action_tx, action_rx) = tokio::sync::mpsc::unbounded_channel();
let delegate = Delegate::new(action_tx);
let launcher = AppLauncher::with_window(ui::window::main::new()).delegate(delegate);
let launcher = AppLauncher::with_window(ui::window::main::new())
.delegate(delegate)
.configure_env(theme::set_theme_env);
let event_sink = launcher.get_external_handle();

View file

@ -1,4 +0,0 @@
use druid::{Color, Insets};
pub const TOP_BAR_BACKGROUND_COLOR: Color = Color::rgba8(255, 255, 255, 50);
pub const TOP_BAR_INSETS: Insets = Insets::uniform(5.0);

View file

@ -0,0 +1,99 @@
use colors_transform::Color as _;
use colors_transform::Rgb;
use druid::Color;
pub use gruvbox_dark::*;
macro_rules! make_color {
($name:ident, $r:literal, $g:literal, $b:literal, $a:literal) => {
pub const $name: Color = Color::rgba8($r, $g, $b, $a);
};
($name:ident, $r:literal, $g:literal, $b:literal) => {
pub const $name: Color = Color::rgb8($r, $g, $b);
};
($name:ident, $col:expr) => {
pub const $name: Color = $col;
};
}
make_color!(TOP_BAR_BACKGROUND_COLOR, COLOR_BG1);
#[allow(dead_code)]
pub mod gruvbox_dark {
use druid::Color;
make_color!(COLOR_BG0_H, 0x1d, 0x20, 0x21);
make_color!(COLOR_BG0_S, 0x32, 0x20, 0x2f);
make_color!(COLOR_BG0, 0x28, 0x28, 0x28);
make_color!(COLOR_BG1, 0x3c, 0x38, 0x36);
make_color!(COLOR_BG2, 0x50, 0x49, 0x45);
make_color!(COLOR_BG3, 0x66, 0x5c, 0x54);
make_color!(COLOR_BG4, 0x7c, 0x6f, 0x64);
make_color!(COLOR_FG0, 0xfb, 0xf1, 0xc7);
make_color!(COLOR_FG1, 0xeb, 0xdb, 0xb2);
make_color!(COLOR_FG2, 0xd5, 0xc4, 0xa1);
make_color!(COLOR_FG3, 0xbd, 0xae, 0x93);
make_color!(COLOR_FG4, 0xa8, 0x99, 0x84);
make_color!(COLOR_BG, COLOR_BG0);
make_color!(COLOR_GRAY_LIGHT, 0x92, 0x83, 0x74);
make_color!(COLOR_RED_DARK, 0xcc, 0x24, 0x1d);
make_color!(COLOR_RED_LIGHT, 0xfb, 0x49, 0x34);
make_color!(COLOR_GREEN_DARK, 0x98, 0x97, 0x1a);
make_color!(COLOR_GREEN_LIGHT, 0xb8, 0xbb, 0x26);
make_color!(COLOR_YELLOW_DARK, 0xd7, 0x99, 0x21);
make_color!(COLOR_YELLOW_LIGHT, 0xfa, 0xbd, 0x2f);
make_color!(COLOR_BLUE_DARK, 0x45, 0x85, 0x88);
make_color!(COLOR_BLUE_LIGHT, 0x83, 0xa5, 0x98);
make_color!(COLOR_PURPLE_DARK, 0xb1, 0x26, 0x86);
make_color!(COLOR_PURPLE_LIGHT, 0xd3, 0x86, 0x9b);
make_color!(COLOR_AQUA_DARK, 0x68, 0x9d, 0x6a);
make_color!(COLOR_AQUA_LIGHT, 0x8e, 0xc0, 0x7c);
make_color!(COLOR_GRAY_DARK, 0xa8, 0x99, 0x84);
make_color!(COLOR_FG, COLOR_FG1);
make_color!(COLOR_ORANGE_DARK, 0xd6, 0x5d, 0x0e);
make_color!(COLOR_ORANGE_LIGHT, 0xfe, 0x80, 0x19);
make_color!(COLOR_ACCENT, COLOR_RED_LIGHT);
make_color!(COLOR_ACCENT_FG, COLOR_BG0_H);
}
pub trait ColorExt {
fn lighten(&self, fac: f32) -> Self;
fn darken(&self, fac: f32) -> Self;
}
impl ColorExt for Color {
fn lighten(&self, fac: f32) -> Self {
let (r, g, b, a) = self.as_rgba();
let rgb = Rgb::from(r as f32, g as f32, b as f32);
let rgb = rgb.lighten(fac);
Self::rgba(
rgb.get_red() as f64,
rgb.get_green() as f64,
rgb.get_blue() as f64,
a,
)
}
fn darken(&self, fac: f32) -> Self {
let (r, g, b, a) = self.as_rgba();
let rgb = Rgb::from(r as f32, g as f32, b as f32);
let rgb = rgb.lighten(-1. * fac);
Self::rgba(
rgb.get_red() as f64,
rgb.get_green() as f64,
rgb.get_blue() as f64,
a,
)
}
}

View file

@ -0,0 +1 @@
pub static ALERT_CIRCLE: &str = include_str!("../../../assets/icons/icons/alert-circle.svg");

View file

@ -0,0 +1,13 @@
use druid::{Color, Insets, Key};
pub const KEY_BUTTON_BG: Key<Color> = Key::new("dtmm.button.bg");
pub const KEY_BUTTON_BG_HOT: Key<Color> = Key::new("dtmm.button.bg-hot");
pub const KEY_BUTTON_BG_ACTIVE: Key<Color> = Key::new("dtmm.button.bg-active");
pub const KEY_BUTTON_BG_DISABLED: Key<Color> = Key::new("dtmm.button.bg-disabled");
pub const KEY_BUTTON_FG: Key<Color> = Key::new("dtmm.button.fg");
pub const KEY_BUTTON_FG_DISABLED: Key<Color> = Key::new("dtmm.button.fg-disabled");
pub const KEY_BUTTON_PADDING: Key<Insets> = Key::new("dtmm.button.padding");
pub const KEY_MOD_LIST_ITEM_BG_COLOR: Key<Color> = Key::new("dtmm.mod-list.item.background-color");

View file

@ -0,0 +1,30 @@
use druid::{Env, Insets};
use crate::state::State;
mod colors;
pub mod icons;
pub mod keys;
pub use colors::*;
pub const TOP_BAR_INSETS: Insets = Insets::uniform(5.0);
pub const DISABLED_ALPHA: f64 = 0.65;
pub(crate) fn set_theme_env(env: &mut Env, _: &State) {
env.set(druid::theme::TEXT_COLOR, COLOR_FG);
env.set(druid::theme::BUTTON_BORDER_RADIUS, 2.);
env.set(keys::KEY_BUTTON_BG, COLOR_ACCENT);
env.set(keys::KEY_BUTTON_BG_HOT, COLOR_ACCENT.darken(0.03));
env.set(keys::KEY_BUTTON_BG_ACTIVE, COLOR_ACCENT.darken(0.1));
env.set(
keys::KEY_BUTTON_BG_DISABLED,
COLOR_ACCENT.with_alpha(DISABLED_ALPHA),
);
env.set(keys::KEY_BUTTON_FG, COLOR_ACCENT_FG);
env.set(
keys::KEY_BUTTON_FG_DISABLED,
COLOR_ACCENT_FG.with_alpha(DISABLED_ALPHA),
);
env.set(keys::KEY_BUTTON_PADDING, Insets::uniform_xy(8., 2.));
}

View file

@ -0,0 +1,113 @@
use druid::widget::prelude::*;
use druid::widget::{Click, ControllerHost, Label, LabelText};
use druid::WidgetPod;
use druid::{Affine, WidgetExt};
use crate::ui::theme;
pub struct Button<T> {
inner: WidgetPod<T, Box<dyn Widget<T>>>,
inner_size: Size,
}
impl<T: Data> Button<T> {
pub fn new(inner: impl Widget<T> + 'static) -> Self {
let inner = inner.env_scope(|env, _| {
env.set(
druid::theme::TEXT_COLOR,
env.get(theme::keys::KEY_BUTTON_FG),
);
env.set(
druid::theme::DISABLED_TEXT_COLOR,
env.get(theme::keys::KEY_BUTTON_FG_DISABLED),
);
});
let inner = WidgetPod::new(inner).boxed();
Self {
inner,
inner_size: Size::ZERO,
}
}
pub fn with_label(text: impl Into<LabelText<T>>) -> Self {
let inner = Label::new(text);
Self::new(inner)
}
pub fn on_click(
self,
f: impl Fn(&mut EventCtx, &mut T, &Env) + 'static,
) -> ControllerHost<Self, Click<T>> {
ControllerHost::new(self, Click::new(f))
}
}
impl<T: Data> Widget<T> for Button<T> {
fn event(&mut self, ctx: &mut EventCtx, event: &Event, _: &mut T, _: &Env) {
match event {
Event::MouseDown(_) if !ctx.is_disabled() => {
ctx.set_active(true);
ctx.request_paint();
}
Event::MouseUp(_) => {
if ctx.is_active() && !ctx.is_disabled() {
ctx.request_paint();
}
ctx.set_active(false);
}
_ => {}
}
}
fn lifecycle(&mut self, ctx: &mut LifeCycleCtx, event: &LifeCycle, data: &T, env: &Env) {
if let LifeCycle::HotChanged(_) | LifeCycle::DisabledChanged(_) = event {
ctx.request_paint();
}
self.inner.lifecycle(ctx, event, data, env);
}
fn update(&mut self, ctx: &mut UpdateCtx, _: &T, data: &T, env: &Env) {
self.inner.update(ctx, data, env);
}
fn layout(&mut self, ctx: &mut LayoutCtx, bc: &BoxConstraints, data: &T, env: &Env) -> Size {
bc.debug_check("Button");
let padding = env.get(theme::keys::KEY_BUTTON_PADDING).size();
let inner_bc = bc.shrink(padding).loosen();
self.inner_size = self.inner.layout(ctx, &inner_bc, data, env);
bc.constrain(Size::new(
self.inner_size.width + padding.width,
self.inner_size.height + padding.height,
))
}
fn paint(&mut self, ctx: &mut PaintCtx, data: &T, env: &Env) {
let size = ctx.size();
let bg_color = if ctx.is_disabled() {
env.get(theme::keys::KEY_BUTTON_BG_DISABLED)
} else if ctx.is_hot() {
env.get(theme::keys::KEY_BUTTON_BG_HOT)
} else if ctx.is_active() {
env.get(theme::keys::KEY_BUTTON_BG_ACTIVE)
} else {
env.get(theme::keys::KEY_BUTTON_BG)
};
ctx.fill(
size.to_rect()
.to_rounded_rect(env.get(druid::theme::BUTTON_BORDER_RADIUS)),
&bg_color,
);
let inner_pos = (size.to_vec2() - self.inner_size.to_vec2()) / 2.;
ctx.with_save(|ctx| {
ctx.transform(Affine::translate(inner_pos));
self.inner.paint(ctx, data, env);
});
}
}

View file

@ -4,6 +4,7 @@ use std::sync::Arc;
use druid::text::Formatter;
use druid::{Data, Widget};
pub mod button;
pub mod controller;
pub trait ExtraWidgetExt<T: Data>: Widget<T> + Sized + 'static {}

View file

@ -1,14 +1,15 @@
use std::str::FromStr;
use std::sync::Arc;
use druid::im::Vector;
use druid::widget::{
Button, Checkbox, CrossAxisAlignment, Flex, Image, Label, LineBreaking, List,
MainAxisAlignment, Maybe, Scroll, SizedBox, Split, TextBox, ViewSwitcher,
Checkbox, CrossAxisAlignment, Either, Flex, Image, Label, LineBreaking, List,
MainAxisAlignment, Maybe, Scroll, SizedBox, Split, Svg, SvgData, TextBox, ViewSwitcher,
};
use druid::{lens, Data, ImageBuf, LifeCycleCtx};
use druid::{
Color, FileDialogOptions, FileSpec, FontDescriptor, FontFamily, Key, LensExt, SingleUse,
Widget, WidgetExt, WindowDesc, WindowId,
Color, FileDialogOptions, FileSpec, FontDescriptor, FontFamily, LensExt, SingleUse, Widget,
WidgetExt, WindowDesc, WindowId,
};
use lazy_static::lazy_static;
@ -18,6 +19,7 @@ use crate::state::{
ACTION_START_DEPLOY, ACTION_START_RESET_DEPLOYMENT,
};
use crate::ui::theme;
use crate::ui::widget::button::Button;
use crate::ui::widget::controller::{
AutoScrollController, DirtyStateController, ImageLensController,
};
@ -31,8 +33,6 @@ const TITLE: &str = "Darktide Mod Manager";
const WINDOW_SIZE: (f64, f64) = (1080., 720.);
const MOD_DETAILS_MIN_WIDTH: f64 = 325.;
const KEY_MOD_LIST_ITEM_BG_COLOR: Key<Color> = Key::new("dtmm.mod-list.item.background-color");
pub(crate) fn new() -> WindowDesc<State> {
WindowDesc::new(build_window())
.title(TITLE)
@ -40,29 +40,34 @@ pub(crate) fn new() -> WindowDesc<State> {
}
fn build_top_bar() -> impl Widget<State> {
let mods_button = Button::new("Mods")
let mods_button = Button::with_label("Mods")
.on_click(|_ctx, state: &mut State, _env| state.current_view = View::Mods);
let settings_button = Button::new("Settings").on_click(|_ctx, state: &mut State, _env| {
let settings_button =
Button::with_label("Settings").on_click(|_ctx, state: &mut State, _env| {
state.current_view = View::Settings;
});
let deploy_button = {
Button::dynamic(|state: &State, _| {
let mut s = String::new();
if state.dirty {
s.push_str("! ");
}
s.push_str("Deploy Mods");
s
})
let icon = Svg::new(SvgData::from_str(theme::icons::ALERT_CIRCLE).expect("invalid SVG"))
.fix_height(druid::theme::TEXT_SIZE_NORMAL);
let inner = Either::new(
|state: &State, _| state.dirty,
Flex::row()
.with_child(icon)
.with_spacer(1.)
.with_child(Label::new("Deploy Mods")),
Label::new("Deploy Mods"),
);
Button::new(inner)
.on_click(|ctx, _state: &mut State, _env| {
ctx.submit_command(ACTION_START_DEPLOY);
})
.disabled_if(|data, _| data.is_deployment_in_progress || data.is_reset_in_progress)
};
let reset_button = Button::new("Reset Game")
let reset_button = Button::with_label("Reset Game")
.on_click(|ctx, _state: &mut State, _env| {
ctx.submit_command(ACTION_START_RESET_DEPLOYMENT);
})
@ -102,15 +107,19 @@ fn build_mod_list() -> impl Widget<State> {
.with_child(checkbox)
.with_child(name)
.padding((5.0, 4.0))
.background(KEY_MOD_LIST_ITEM_BG_COLOR)
.background(theme::keys::KEY_MOD_LIST_ITEM_BG_COLOR)
.on_click(|ctx, (i, _, _), _env| ctx.submit_command(ACTION_SELECT_MOD.with(*i)))
.env_scope(|env, (i, _, selected)| {
if *selected {
env.set(KEY_MOD_LIST_ITEM_BG_COLOR, Color::NAVY);
} else if (i % 2) == 1 {
env.set(KEY_MOD_LIST_ITEM_BG_COLOR, Color::WHITE.with_alpha(0.05));
env.set(theme::keys::KEY_MOD_LIST_ITEM_BG_COLOR, theme::COLOR_ACCENT);
env.set(druid::theme::TEXT_COLOR, theme::COLOR_ACCENT_FG);
} else {
env.set(KEY_MOD_LIST_ITEM_BG_COLOR, Color::TRANSPARENT);
env.set(druid::theme::TEXT_COLOR, theme::COLOR_FG);
if (i % 2) == 1 {
env.set(theme::keys::KEY_MOD_LIST_ITEM_BG_COLOR, theme::COLOR_BG1);
} else {
env.set(theme::keys::KEY_MOD_LIST_ITEM_BG_COLOR, Color::TRANSPARENT);
}
}
})
});
@ -140,35 +149,36 @@ fn build_mod_list() -> impl Widget<State> {
}
fn build_mod_details_buttons() -> impl Widget<State> {
let button_move_up = Button::new("Move Up")
let button_move_up = Button::with_label("Move Up")
.on_click(|ctx, _state, _env| ctx.submit_command(ACTION_SELECTED_MOD_UP))
.disabled_if(|state: &State, _env: &druid::Env| !state.can_move_mod_up());
let button_move_down = Button::new("Move Down")
let button_move_down = Button::with_label("Move Down")
.on_click(|ctx, _state, _env| ctx.submit_command(ACTION_SELECTED_MOD_DOWN))
.disabled_if(|state: &State, _env: &druid::Env| !state.can_move_mod_down());
let button_toggle_mod = Maybe::new(
|| {
Button::dynamic(|enabled, _env| {
let inner = Label::dynamic(|enabled, _env| {
if *enabled {
"Disable Mod".into()
} else {
"Enable Mod".into()
}
})
});
Button::new(inner)
.on_click(|_ctx, enabled: &mut bool, _env| {
*enabled = !(*enabled);
})
.lens(ModInfo::enabled.in_arc())
},
// TODO: Gray out
|| Button::new("Enable Mod"),
|| Button::with_label("Enable Mod"),
)
.disabled_if(|info: &Option<Arc<ModInfo>>, _env: &druid::Env| info.is_none())
.lens(State::selected_mod);
let button_add_mod = Button::new("Add Mod").on_click(|ctx, _state: &mut State, _env| {
let button_add_mod = Button::with_label("Add Mod").on_click(|ctx, _state: &mut State, _env| {
let zip = FileSpec::new("Zip file", &["zip"]);
let opts = FileDialogOptions::new()
.allowed_types(vec![zip])
@ -179,7 +189,7 @@ fn build_mod_details_buttons() -> impl Widget<State> {
ctx.submit_command(druid::commands::SHOW_OPEN_PANEL.with(opts))
});
let button_delete_mod = Button::new("Delete Mod")
let button_delete_mod = Button::with_label("Delete Mod")
.on_click(|ctx, data: &mut Option<Arc<ModInfo>>, _env| {
if let Some(info) = data {
ctx.submit_command(