chore: revamp theme
This commit is contained in:
117
Cargo.lock
generated
117
Cargo.lock
generated
@@ -499,9 +499,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "backtrace"
|
||||
version = "0.3.74"
|
||||
version = "0.3.75"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8d82cb332cdfaed17ae235a638438ac4d4839913cc2af585c3c6746e8f8bee1a"
|
||||
checksum = "6806a6321ec58106fea15becdad98371e28d92ccbc7c8f1b3b6dd724fe8f1002"
|
||||
dependencies = [
|
||||
"addr2line",
|
||||
"cfg-if",
|
||||
@@ -1136,7 +1136,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "collections"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/zed-industries/zed#45fe158bc9179d1357617f9fa2cc6c3c203daa6b"
|
||||
source = "git+https://github.com/zed-industries/zed#ab3e5cdc6cf416181fe009d13476ed5e779f7c24"
|
||||
dependencies = [
|
||||
"indexmap",
|
||||
"rustc-hash 2.1.1",
|
||||
@@ -1240,6 +1240,7 @@ dependencies = [
|
||||
"serde_json",
|
||||
"smallvec",
|
||||
"smol",
|
||||
"theme",
|
||||
"tracing-subscriber",
|
||||
"ui",
|
||||
"webbrowser",
|
||||
@@ -1526,7 +1527,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "derive_refineable"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/zed-industries/zed#45fe158bc9179d1357617f9fa2cc6c3c203daa6b"
|
||||
source = "git+https://github.com/zed-industries/zed#ab3e5cdc6cf416181fe009d13476ed5e779f7c24"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
@@ -2317,7 +2318,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "gpui"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/zed-industries/zed#45fe158bc9179d1357617f9fa2cc6c3c203daa6b"
|
||||
source = "git+https://github.com/zed-industries/zed#ab3e5cdc6cf416181fe009d13476ed5e779f7c24"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"as-raw-xcb-connection",
|
||||
@@ -2409,7 +2410,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "gpui_macros"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/zed-industries/zed#45fe158bc9179d1357617f9fa2cc6c3c203daa6b"
|
||||
source = "git+https://github.com/zed-industries/zed#ab3e5cdc6cf416181fe009d13476ed5e779f7c24"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
@@ -2425,9 +2426,9 @@ checksum = "d196ffc1627db18a531359249b2bf8416178d84b729f3cebeb278f285fb9b58c"
|
||||
|
||||
[[package]]
|
||||
name = "h2"
|
||||
version = "0.4.9"
|
||||
version = "0.4.10"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "75249d144030531f8dee69fe9cea04d3edf809a017ae445e2abdff6629e86633"
|
||||
checksum = "a9421a676d1b147b16b82c9225157dc629087ef8ec4d5e2960f9437a90dac0a5"
|
||||
dependencies = [
|
||||
"atomic-waker",
|
||||
"bytes",
|
||||
@@ -2633,7 +2634,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "http_client"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/zed-industries/zed#45fe158bc9179d1357617f9fa2cc6c3c203daa6b"
|
||||
source = "git+https://github.com/zed-industries/zed#ab3e5cdc6cf416181fe009d13476ed5e779f7c24"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"bytes",
|
||||
@@ -2650,7 +2651,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "http_client_tls"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/zed-industries/zed#45fe158bc9179d1357617f9fa2cc6c3c203daa6b"
|
||||
source = "git+https://github.com/zed-industries/zed#ab3e5cdc6cf416181fe009d13476ed5e779f7c24"
|
||||
dependencies = [
|
||||
"rustls",
|
||||
"rustls-platform-verifier",
|
||||
@@ -2699,7 +2700,7 @@ dependencies = [
|
||||
"tokio",
|
||||
"tokio-rustls",
|
||||
"tower-service",
|
||||
"webpki-roots",
|
||||
"webpki-roots 0.26.11",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -3214,9 +3215,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "libm"
|
||||
version = "0.2.14"
|
||||
version = "0.2.15"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a25169bd5913a4b437588a7e3d127cd6e90127b60e0ffbd834a38f1599e016b8"
|
||||
checksum = "f9fbbcab51052fe104eb5e5d351cf728d30a5be1fe14d9be8a3b097481fb97de"
|
||||
|
||||
[[package]]
|
||||
name = "libredox"
|
||||
@@ -3391,7 +3392,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "media"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/zed-industries/zed#45fe158bc9179d1357617f9fa2cc6c3c203daa6b"
|
||||
source = "git+https://github.com/zed-industries/zed#ab3e5cdc6cf416181fe009d13476ed5e779f7c24"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"bindgen 0.71.1",
|
||||
@@ -3589,7 +3590,7 @@ checksum = "0676bb32a98c1a483ce53e500a81ad9c3d5b3f7c920c28c24e9cb0980d0b5bc8"
|
||||
[[package]]
|
||||
name = "nostr"
|
||||
version = "0.41.0"
|
||||
source = "git+https://github.com/rust-nostr/nostr?branch=nip46#63609d9c7e62b16c26fa59b255284f3a292bea24"
|
||||
source = "git+https://github.com/rust-nostr/nostr#677cb7fb242fd59f861561203cd7b9502e222642"
|
||||
dependencies = [
|
||||
"aes",
|
||||
"base64",
|
||||
@@ -3614,7 +3615,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "nostr-connect"
|
||||
version = "0.41.0"
|
||||
source = "git+https://github.com/rust-nostr/nostr?branch=nip46#63609d9c7e62b16c26fa59b255284f3a292bea24"
|
||||
source = "git+https://github.com/rust-nostr/nostr#677cb7fb242fd59f861561203cd7b9502e222642"
|
||||
dependencies = [
|
||||
"async-utility",
|
||||
"nostr",
|
||||
@@ -3626,7 +3627,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "nostr-database"
|
||||
version = "0.41.0"
|
||||
source = "git+https://github.com/rust-nostr/nostr?branch=nip46#63609d9c7e62b16c26fa59b255284f3a292bea24"
|
||||
source = "git+https://github.com/rust-nostr/nostr#677cb7fb242fd59f861561203cd7b9502e222642"
|
||||
dependencies = [
|
||||
"flatbuffers",
|
||||
"lru",
|
||||
@@ -3637,7 +3638,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "nostr-lmdb"
|
||||
version = "0.41.0"
|
||||
source = "git+https://github.com/rust-nostr/nostr?branch=nip46#63609d9c7e62b16c26fa59b255284f3a292bea24"
|
||||
source = "git+https://github.com/rust-nostr/nostr#677cb7fb242fd59f861561203cd7b9502e222642"
|
||||
dependencies = [
|
||||
"async-utility",
|
||||
"heed",
|
||||
@@ -3650,7 +3651,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "nostr-relay-pool"
|
||||
version = "0.41.0"
|
||||
source = "git+https://github.com/rust-nostr/nostr?branch=nip46#63609d9c7e62b16c26fa59b255284f3a292bea24"
|
||||
source = "git+https://github.com/rust-nostr/nostr#677cb7fb242fd59f861561203cd7b9502e222642"
|
||||
dependencies = [
|
||||
"async-utility",
|
||||
"async-wsocket",
|
||||
@@ -3666,7 +3667,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "nostr-sdk"
|
||||
version = "0.41.0"
|
||||
source = "git+https://github.com/rust-nostr/nostr?branch=nip46#63609d9c7e62b16c26fa59b255284f3a292bea24"
|
||||
source = "git+https://github.com/rust-nostr/nostr#677cb7fb242fd59f861561203cd7b9502e222642"
|
||||
dependencies = [
|
||||
"async-utility",
|
||||
"nostr",
|
||||
@@ -4842,7 +4843,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "refineable"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/zed-industries/zed#45fe158bc9179d1357617f9fa2cc6c3c203daa6b"
|
||||
source = "git+https://github.com/zed-industries/zed#ab3e5cdc6cf416181fe009d13476ed5e779f7c24"
|
||||
dependencies = [
|
||||
"derive_refineable",
|
||||
"workspace-hack",
|
||||
@@ -4926,7 +4927,7 @@ dependencies = [
|
||||
"wasm-bindgen-futures",
|
||||
"wasm-streams",
|
||||
"web-sys",
|
||||
"webpki-roots",
|
||||
"webpki-roots 0.26.11",
|
||||
"windows-registry 0.4.0",
|
||||
]
|
||||
|
||||
@@ -4981,7 +4982,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "reqwest_client"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/zed-industries/zed#45fe158bc9179d1357617f9fa2cc6c3c203daa6b"
|
||||
source = "git+https://github.com/zed-industries/zed#ab3e5cdc6cf416181fe009d13476ed5e779f7c24"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"bytes",
|
||||
@@ -5042,9 +5043,9 @@ checksum = "6c20b6793b5c2fa6553b250154b78d6d0db37e72700ae35fad9387a46f487c97"
|
||||
|
||||
[[package]]
|
||||
name = "rust-embed"
|
||||
version = "8.7.0"
|
||||
version = "8.7.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e5fbc0ee50fcb99af7cebb442e5df7b5b45e9460ffa3f8f549cd26b862bec49d"
|
||||
checksum = "60e425e204264b144d4c929d126d0de524b40a961686414bab5040f7465c71be"
|
||||
dependencies = [
|
||||
"rust-embed-impl",
|
||||
"rust-embed-utils",
|
||||
@@ -5130,9 +5131,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "rustls"
|
||||
version = "0.23.26"
|
||||
version = "0.23.27"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "df51b5869f3a441595eac5e8ff14d486ff285f7b8c0df8770e49c3b56351f0f0"
|
||||
checksum = "730944ca083c1c233a75c09f199e973ca499344a2b7ba9e755c457e86fb4a321"
|
||||
dependencies = [
|
||||
"aws-lc-rs",
|
||||
"log",
|
||||
@@ -5176,9 +5177,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "rustls-platform-verifier"
|
||||
version = "0.5.2"
|
||||
version = "0.5.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4937d110d34408e9e5ad30ba0b0ca3b6a8a390f8db3636db60144ac4fa792750"
|
||||
checksum = "19787cda76408ec5404443dc8b31795c87cd8fec49762dc75fa727740d34acc1"
|
||||
dependencies = [
|
||||
"core-foundation 0.10.0",
|
||||
"core-foundation-sys",
|
||||
@@ -5191,7 +5192,7 @@ dependencies = [
|
||||
"rustls-webpki",
|
||||
"security-framework 3.2.0",
|
||||
"security-framework-sys",
|
||||
"webpki-root-certs",
|
||||
"webpki-root-certs 0.26.11",
|
||||
"windows-sys 0.59.0",
|
||||
]
|
||||
|
||||
@@ -5203,9 +5204,9 @@ checksum = "f87165f0995f63a9fbeea62b64d10b4d9d8e78ec6d7d51fb2125fda7bb36788f"
|
||||
|
||||
[[package]]
|
||||
name = "rustls-webpki"
|
||||
version = "0.103.1"
|
||||
version = "0.103.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fef8b8769aaccf73098557a87cd1816b4f9c7c16811c9c77142aa695c16f2c03"
|
||||
checksum = "7149975849f1abb3832b246010ef62ccc80d3a76169517ada7188252b9cfb437"
|
||||
dependencies = [
|
||||
"aws-lc-rs",
|
||||
"ring",
|
||||
@@ -5451,7 +5452,7 @@ checksum = "0f7d95a54511e0c7be3f51e8867aa8cf35148d7b9445d44de2f943e2b206e749"
|
||||
[[package]]
|
||||
name = "semantic_version"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/zed-industries/zed#45fe158bc9179d1357617f9fa2cc6c3c203daa6b"
|
||||
source = "git+https://github.com/zed-industries/zed#ab3e5cdc6cf416181fe009d13476ed5e779f7c24"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"serde",
|
||||
@@ -5774,7 +5775,7 @@ checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292"
|
||||
[[package]]
|
||||
name = "sum_tree"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/zed-industries/zed#45fe158bc9179d1357617f9fa2cc6c3c203daa6b"
|
||||
source = "git+https://github.com/zed-industries/zed#ab3e5cdc6cf416181fe009d13476ed5e779f7c24"
|
||||
dependencies = [
|
||||
"arrayvec",
|
||||
"log",
|
||||
@@ -6065,6 +6066,15 @@ dependencies = [
|
||||
"winapi-util",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "theme"
|
||||
version = "0.1.5"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"gpui",
|
||||
"log",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "thiserror"
|
||||
version = "1.0.69"
|
||||
@@ -6199,9 +6209,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "tokio"
|
||||
version = "1.44.2"
|
||||
version = "1.45.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e6b88822cbe49de4185e3a4cbf8321dd487cf5fe0c5c65695fef6346371e9c48"
|
||||
checksum = "2513ca694ef9ede0fb23fe71a4ee4107cb102b9dc1930f6d0fd77aae068ae165"
|
||||
dependencies = [
|
||||
"backtrace",
|
||||
"bytes",
|
||||
@@ -6269,7 +6279,7 @@ dependencies = [
|
||||
"tokio",
|
||||
"tokio-rustls",
|
||||
"tungstenite",
|
||||
"webpki-roots",
|
||||
"webpki-roots 0.26.11",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -6499,6 +6509,7 @@ dependencies = [
|
||||
"serde_json",
|
||||
"smallvec",
|
||||
"smol",
|
||||
"theme",
|
||||
"unicode-segmentation",
|
||||
"uuid",
|
||||
]
|
||||
@@ -6684,7 +6695,7 @@ checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821"
|
||||
[[package]]
|
||||
name = "util"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/zed-industries/zed#45fe158bc9179d1357617f9fa2cc6c3c203daa6b"
|
||||
source = "git+https://github.com/zed-industries/zed#ab3e5cdc6cf416181fe009d13476ed5e779f7c24"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"async-fs",
|
||||
@@ -7064,18 +7075,36 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "webpki-root-certs"
|
||||
version = "0.26.10"
|
||||
version = "0.26.11"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c99403924bc5f23afefc319b8ac67ed0e50669f6e52a413314cccb1fdbc93ba0"
|
||||
checksum = "75c7f0ef91146ebfb530314f5f1d24528d7f0767efbfd31dce919275413e393e"
|
||||
dependencies = [
|
||||
"webpki-root-certs 1.0.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "webpki-root-certs"
|
||||
version = "1.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "01a83f7e1a9f8712695c03eabe9ed3fbca0feff0152f33f12593e5a6303cb1a4"
|
||||
dependencies = [
|
||||
"rustls-pki-types",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "webpki-roots"
|
||||
version = "0.26.10"
|
||||
version = "0.26.11"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "37493cadf42a2a939ed404698ded7fb378bf301b5011f973361779a3a74f8c93"
|
||||
checksum = "521bc38abb08001b01866da9f51eb7c5d647a19260e00054a8c7fd5f9e57f7a9"
|
||||
dependencies = [
|
||||
"webpki-roots 1.0.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "webpki-roots"
|
||||
version = "1.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2853738d1cc4f2da3a225c18ec6c3721abb31961096e9dbf5ab35fa88b19cfdb"
|
||||
dependencies = [
|
||||
"rustls-pki-types",
|
||||
]
|
||||
@@ -7620,9 +7649,9 @@ checksum = "271414315aff87387382ec3d271b52d7ae78726f5d44ac98b4f4030c91880486"
|
||||
|
||||
[[package]]
|
||||
name = "winnow"
|
||||
version = "0.7.9"
|
||||
version = "0.7.10"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d9fb597c990f03753e08d3c29efbfcf2019a003b4bf4ba19225c158e1549f0f3"
|
||||
checksum = "c06928c8748d81b05c9be96aad92e1b6ff01833332f281e8cfca3be4b35fc9ec"
|
||||
dependencies = [
|
||||
"memchr",
|
||||
]
|
||||
|
||||
@@ -16,10 +16,10 @@ gpui = { git = "https://github.com/zed-industries/zed" }
|
||||
reqwest_client = { git = "https://github.com/zed-industries/zed" }
|
||||
|
||||
# Nostr
|
||||
nostr = { git = "https://github.com/rust-nostr/nostr", branch = "nip46", features = ["parser"] }
|
||||
nostr-sdk = { git = "https://github.com/rust-nostr/nostr", branch = "nip46", features = ["lmdb", "nip96", "nip59", "nip49", "nip44", "nip05"] }
|
||||
nostr-connect = { git = "https://github.com/rust-nostr/nostr", branch = "nip46" }
|
||||
nostr-keyring = { git = "https://github.com/rust-nostr/nostr", branch = "nip46" }
|
||||
nostr = { git = "https://github.com/rust-nostr/nostr", features = ["parser"] }
|
||||
nostr-sdk = { git = "https://github.com/rust-nostr/nostr", features = ["lmdb", "nip96", "nip59", "nip49", "nip44", "nip05"] }
|
||||
nostr-connect = { git = "https://github.com/rust-nostr/nostr" }
|
||||
nostr-keyring = { git = "https://github.com/rust-nostr/nostr" }
|
||||
|
||||
# Others
|
||||
emojis = "0.6.4"
|
||||
|
||||
@@ -10,6 +10,7 @@ path = "src/main.rs"
|
||||
|
||||
[dependencies]
|
||||
ui = { path = "../ui" }
|
||||
theme = { path = "../theme" }
|
||||
common = { path = "../common" }
|
||||
global = { path = "../global" }
|
||||
chats = { path = "../chats" }
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
use std::sync::Arc;
|
||||
|
||||
use account::Account;
|
||||
use anyhow::Error;
|
||||
use global::get_client;
|
||||
@@ -9,11 +11,10 @@ use gpui::{
|
||||
use nostr_sdk::prelude::*;
|
||||
use serde::Deserialize;
|
||||
use smallvec::{smallvec, SmallVec};
|
||||
use std::sync::Arc;
|
||||
use theme::{ActiveTheme, Theme, ThemeMode};
|
||||
use ui::{
|
||||
button::{Button, ButtonVariants},
|
||||
dock_area::{dock::DockPlacement, panel::PanelView, DockArea, DockItem},
|
||||
theme::{ActiveTheme, Appearance, Theme},
|
||||
ContextModal, IconName, Root, Sizable, TitleBar,
|
||||
};
|
||||
|
||||
@@ -324,7 +325,7 @@ impl Render for ChatSpace {
|
||||
.xsmall()
|
||||
.ghost()
|
||||
.map(|this| {
|
||||
if cx.theme().appearance.is_dark() {
|
||||
if cx.theme().mode.is_dark() {
|
||||
this.icon(IconName::Sun)
|
||||
} else {
|
||||
this.icon(IconName::Moon)
|
||||
@@ -332,15 +333,15 @@ impl Render for ChatSpace {
|
||||
})
|
||||
.on_click(cx.listener(
|
||||
|_, _, window, cx| {
|
||||
if cx.theme().appearance.is_dark() {
|
||||
if cx.theme().mode.is_dark() {
|
||||
Theme::change(
|
||||
Appearance::Light,
|
||||
ThemeMode::Light,
|
||||
Some(window),
|
||||
cx,
|
||||
);
|
||||
} else {
|
||||
Theme::change(
|
||||
Appearance::Dark,
|
||||
ThemeMode::Dark,
|
||||
Some(window),
|
||||
cx,
|
||||
);
|
||||
|
||||
@@ -27,7 +27,8 @@ use nostr_sdk::{
|
||||
};
|
||||
use smol::Timer;
|
||||
use std::{collections::HashSet, mem, sync::Arc, time::Duration};
|
||||
use ui::{theme::Theme, Root};
|
||||
use theme::Theme;
|
||||
use ui::Root;
|
||||
|
||||
pub(crate) mod asset;
|
||||
pub(crate) mod chatspace;
|
||||
@@ -286,7 +287,6 @@ fn main() {
|
||||
// Open a window with default options
|
||||
cx.open_window(opts, |window, cx| {
|
||||
// Automatically sync theme with system appearance
|
||||
#[cfg(not(target_os = "linux"))]
|
||||
window
|
||||
.observe_window_appearance(|window, cx| {
|
||||
Theme::sync_system_appearance(Some(window), cx);
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
use std::{collections::HashMap, sync::Arc};
|
||||
|
||||
use anyhow::{anyhow, Error};
|
||||
use async_utility::task::spawn;
|
||||
use chats::{
|
||||
@@ -8,7 +10,7 @@ use chats::{
|
||||
use common::{nip96_upload, profile::SharedProfile};
|
||||
use global::{constants::IMAGE_SERVICE, get_client};
|
||||
use gpui::{
|
||||
div, img, impl_internal_actions, list, prelude::FluentBuilder, px, relative, svg, white,
|
||||
div, img, impl_internal_actions, list, prelude::FluentBuilder, px, red, relative, svg, white,
|
||||
AnyElement, App, AppContext, Context, Element, Empty, Entity, EventEmitter, Flatten,
|
||||
FocusHandle, Focusable, InteractiveElement, IntoElement, ListAlignment, ListState, ObjectFit,
|
||||
ParentElement, PathPromptOptions, Render, SharedString, StatefulInteractiveElement, Styled,
|
||||
@@ -18,7 +20,7 @@ use nostr_sdk::prelude::*;
|
||||
use serde::Deserialize;
|
||||
use smallvec::{smallvec, SmallVec};
|
||||
use smol::fs;
|
||||
use std::{collections::HashMap, sync::Arc};
|
||||
use theme::ActiveTheme;
|
||||
use ui::{
|
||||
button::{Button, ButtonVariants},
|
||||
dock_area::panel::{Panel, PanelEvent},
|
||||
@@ -27,7 +29,6 @@ use ui::{
|
||||
notification::Notification,
|
||||
popup_menu::PopupMenu,
|
||||
text::RichText,
|
||||
theme::{scale::ColorScaleStep, ActiveTheme},
|
||||
v_flex, ContextModal, Disableable, Icon, IconName, Sizable, Size, StyledExt,
|
||||
};
|
||||
|
||||
@@ -388,7 +389,7 @@ impl Chat {
|
||||
.entry(item.id)
|
||||
.or_insert_with(|| RichText::new(item.content.to_owned(), &item.mentions));
|
||||
|
||||
this.hover(|this| this.bg(cx.theme().accent.step(cx, ColorScaleStep::ONE)))
|
||||
this.hover(|this| this.bg(cx.theme().surface_background))
|
||||
.text_sm()
|
||||
.child(
|
||||
div()
|
||||
@@ -397,10 +398,8 @@ impl Chat {
|
||||
.top_0()
|
||||
.w(px(2.))
|
||||
.h_full()
|
||||
.bg(cx.theme().transparent)
|
||||
.group_hover("", |this| {
|
||||
this.bg(cx.theme().accent.step(cx, ColorScaleStep::NINE))
|
||||
}),
|
||||
.bg(cx.theme().border_transparent)
|
||||
.group_hover("", |this| this.bg(cx.theme().element_active)),
|
||||
)
|
||||
.child(img(item.author.shared_avatar()).size_8().flex_shrink_0())
|
||||
.child(
|
||||
@@ -419,9 +418,7 @@ impl Chat {
|
||||
)
|
||||
.child(
|
||||
div()
|
||||
.text_color(
|
||||
cx.theme().base.step(cx, ColorScaleStep::NINE),
|
||||
)
|
||||
.text_color(cx.theme().text_placeholder)
|
||||
.child(item.ago()),
|
||||
),
|
||||
)
|
||||
@@ -437,12 +434,12 @@ impl Chat {
|
||||
.top_0()
|
||||
.w(px(2.))
|
||||
.h_full()
|
||||
.bg(cx.theme().transparent)
|
||||
.group_hover("", |this| this.bg(cx.theme().danger)),
|
||||
.bg(cx.theme().border_transparent)
|
||||
.group_hover("", |this| this.bg(red())),
|
||||
)
|
||||
.child(img("brand/avatar.png").size_8().flex_shrink_0())
|
||||
.text_sm()
|
||||
.text_color(cx.theme().danger)
|
||||
.text_color(red())
|
||||
.child(content.clone()),
|
||||
RoomMessage::Announcement => this
|
||||
.w_full()
|
||||
@@ -453,13 +450,13 @@ impl Chat {
|
||||
.justify_center()
|
||||
.text_center()
|
||||
.text_xs()
|
||||
.text_color(cx.theme().base.step(cx, ColorScaleStep::NINE))
|
||||
.text_color(cx.theme().text_placeholder)
|
||||
.line_height(relative(1.3))
|
||||
.child(
|
||||
svg()
|
||||
.path("brand/coop.svg")
|
||||
.size_10()
|
||||
.text_color(cx.theme().base.step(cx, ColorScaleStep::THREE)),
|
||||
.text_color(cx.theme().elevated_surface_background),
|
||||
)
|
||||
.child(DESC),
|
||||
})
|
||||
@@ -492,10 +489,12 @@ impl Panel for Chat {
|
||||
.items_center()
|
||||
.size_5()
|
||||
.rounded_full()
|
||||
.bg(cx.theme().accent.step(cx, ColorScaleStep::THREE))
|
||||
.child(Icon::new(IconName::UsersThreeFill).xsmall().text_color(
|
||||
cx.theme().accent.step(cx, ColorScaleStep::TWELVE),
|
||||
)),
|
||||
.bg(cx.theme().element_disabled)
|
||||
.child(
|
||||
Icon::new(IconName::UsersThreeFill)
|
||||
.xsmall()
|
||||
.text_color(cx.theme().icon_accent),
|
||||
),
|
||||
)
|
||||
}
|
||||
})
|
||||
@@ -567,7 +566,7 @@ impl Render for Chat {
|
||||
))
|
||||
.size_16()
|
||||
.shadow_lg()
|
||||
.rounded(px(cx.theme().radius))
|
||||
.rounded(cx.theme().radius)
|
||||
.object_fit(ObjectFit::ScaleDown),
|
||||
)
|
||||
.child(
|
||||
@@ -580,7 +579,7 @@ impl Render for Chat {
|
||||
.items_center()
|
||||
.justify_center()
|
||||
.rounded_full()
|
||||
.bg(cx.theme().danger)
|
||||
.bg(red())
|
||||
.child(
|
||||
Icon::new(IconName::Close)
|
||||
.size_2()
|
||||
@@ -603,9 +602,7 @@ impl Render for Chat {
|
||||
.flex()
|
||||
.items_center()
|
||||
.gap_1()
|
||||
.text_color(
|
||||
cx.theme().base.step(cx, ColorScaleStep::ELEVEN),
|
||||
)
|
||||
.text_color(cx.theme().text_muted)
|
||||
.child(
|
||||
Button::new("upload")
|
||||
.icon(Icon::new(IconName::Upload))
|
||||
|
||||
@@ -1,9 +1,14 @@
|
||||
use std::{
|
||||
collections::{BTreeSet, HashSet},
|
||||
time::Duration,
|
||||
};
|
||||
|
||||
use anyhow::Error;
|
||||
use chats::ChatRegistry;
|
||||
use common::{profile::SharedProfile, random_name};
|
||||
use global::get_client;
|
||||
use gpui::{
|
||||
div, img, impl_internal_actions, prelude::FluentBuilder, px, relative, uniform_list, App,
|
||||
div, img, impl_internal_actions, prelude::FluentBuilder, px, red, relative, uniform_list, App,
|
||||
AppContext, Context, Entity, FocusHandle, InteractiveElement, IntoElement, ParentElement,
|
||||
Render, SharedString, StatefulInteractiveElement, Styled, Subscription, Task, TextAlign,
|
||||
Window,
|
||||
@@ -12,15 +17,11 @@ use nostr_sdk::prelude::*;
|
||||
use serde::Deserialize;
|
||||
use smallvec::{smallvec, SmallVec};
|
||||
use smol::Timer;
|
||||
use std::{
|
||||
collections::{BTreeSet, HashSet},
|
||||
time::Duration,
|
||||
};
|
||||
use theme::ActiveTheme;
|
||||
use ui::{
|
||||
button::{Button, ButtonVariants},
|
||||
dock_area::dock::DockPlacement,
|
||||
input::{InputEvent, TextInput},
|
||||
theme::{scale::ColorScaleStep, ActiveTheme},
|
||||
ContextModal, Disableable, Icon, IconName, Sizable, Size, StyledExt,
|
||||
};
|
||||
|
||||
@@ -335,24 +336,18 @@ impl Render for Compose {
|
||||
div()
|
||||
.px_3()
|
||||
.text_sm()
|
||||
.text_color(cx.theme().base.step(cx, ColorScaleStep::ELEVEN))
|
||||
.text_color(cx.theme().text_muted)
|
||||
.child(DESCRIPTION),
|
||||
)
|
||||
.when_some(self.error_message.read(cx).as_ref(), |this, msg| {
|
||||
this.child(
|
||||
div()
|
||||
.px_3()
|
||||
.text_xs()
|
||||
.text_color(cx.theme().danger)
|
||||
.child(msg.clone()),
|
||||
)
|
||||
this.child(div().px_3().text_xs().text_color(red()).child(msg.clone()))
|
||||
})
|
||||
.child(
|
||||
div().px_3().flex().flex_col().child(
|
||||
div()
|
||||
.h_10()
|
||||
.border_b_1()
|
||||
.border_color(cx.theme().base.step(cx, ColorScaleStep::FIVE))
|
||||
.border_color(cx.theme().border)
|
||||
.flex()
|
||||
.items_center()
|
||||
.gap_1()
|
||||
@@ -399,9 +394,7 @@ impl Render for Compose {
|
||||
.child(
|
||||
div()
|
||||
.text_xs()
|
||||
.text_color(
|
||||
cx.theme().base.step(cx, ColorScaleStep::ELEVEN),
|
||||
)
|
||||
.text_color(cx.theme().text_muted)
|
||||
.child("Your recently contacts will appear here."),
|
||||
),
|
||||
)
|
||||
@@ -445,19 +438,13 @@ impl Render for Compose {
|
||||
this.child(
|
||||
Icon::new(IconName::CheckCircleFill)
|
||||
.small()
|
||||
.text_color(
|
||||
cx.theme().accent.step(
|
||||
cx,
|
||||
ColorScaleStep::NINE,
|
||||
),
|
||||
),
|
||||
.text_color(cx.theme().icon_accent),
|
||||
)
|
||||
})
|
||||
.hover(|this| {
|
||||
this.bg(cx
|
||||
.theme()
|
||||
.base
|
||||
.step(cx, ColorScaleStep::THREE))
|
||||
.elevated_surface_background)
|
||||
})
|
||||
.on_click(move |_, window, cx| {
|
||||
window.dispatch_action(
|
||||
|
||||
@@ -4,19 +4,19 @@ use account::Account;
|
||||
use common::create_qr;
|
||||
use global::get_client_keys;
|
||||
use gpui::{
|
||||
div, img, prelude::FluentBuilder, relative, AnyElement, App, AppContext, Context, Entity,
|
||||
div, img, prelude::FluentBuilder, red, relative, AnyElement, App, AppContext, Context, Entity,
|
||||
EventEmitter, FocusHandle, Focusable, Image, IntoElement, ParentElement, Render, SharedString,
|
||||
Styled, Subscription, Window,
|
||||
};
|
||||
use nostr_connect::prelude::*;
|
||||
use smallvec::{smallvec, SmallVec};
|
||||
use theme::ActiveTheme;
|
||||
use ui::{
|
||||
button::{Button, ButtonVariants},
|
||||
dock_area::panel::{Panel, PanelEvent},
|
||||
input::{InputEvent, TextInput},
|
||||
notification::Notification,
|
||||
popup_menu::PopupMenu,
|
||||
theme::{scale::ColorScaleStep, ActiveTheme},
|
||||
ContextModal, Disableable, Sizable, Size, StyledExt,
|
||||
};
|
||||
|
||||
@@ -307,9 +307,7 @@ impl Render for Login {
|
||||
)
|
||||
.child(
|
||||
div()
|
||||
.text_color(
|
||||
cx.theme().base.step(cx, ColorScaleStep::ELEVEN),
|
||||
)
|
||||
.text_color(cx.theme().text_muted)
|
||||
.child("Continue with Private Key or Bunker"),
|
||||
),
|
||||
)
|
||||
@@ -334,7 +332,7 @@ impl Render for Login {
|
||||
div()
|
||||
.text_xs()
|
||||
.text_center()
|
||||
.text_color(cx.theme().danger)
|
||||
.text_color(red())
|
||||
.child(error),
|
||||
)
|
||||
}),
|
||||
@@ -348,7 +346,7 @@ impl Render for Login {
|
||||
.flex()
|
||||
.items_center()
|
||||
.justify_center()
|
||||
.bg(cx.theme().base.step(cx, ColorScaleStep::TWO))
|
||||
.bg(cx.theme().surface_background)
|
||||
.child(
|
||||
div()
|
||||
.flex()
|
||||
@@ -364,17 +362,13 @@ impl Render for Login {
|
||||
div()
|
||||
.font_semibold()
|
||||
.line_height(relative(1.2))
|
||||
.text_color(
|
||||
cx.theme().base.step(cx, ColorScaleStep::TWELVE),
|
||||
)
|
||||
.text_color(cx.theme().text)
|
||||
.child("Continue with Nostr Connect"),
|
||||
)
|
||||
.child(
|
||||
div()
|
||||
.text_sm()
|
||||
.text_color(
|
||||
cx.theme().base.step(cx, ColorScaleStep::ELEVEN),
|
||||
)
|
||||
.text_color(cx.theme().text_muted)
|
||||
.child("Use Nostr Connect apps to scan the code"),
|
||||
),
|
||||
)
|
||||
@@ -391,10 +385,10 @@ impl Render for Login {
|
||||
.gap_2()
|
||||
.rounded_2xl()
|
||||
.shadow_md()
|
||||
.when(cx.theme().appearance.is_dark(), |this| {
|
||||
this.shadow_none().border_1().border_color(
|
||||
cx.theme().base.step(cx, ColorScaleStep::SIX),
|
||||
)
|
||||
.when(cx.theme().mode.is_dark(), |this| {
|
||||
this.shadow_none()
|
||||
.border_1()
|
||||
.border_color(cx.theme().border)
|
||||
})
|
||||
.bg(cx.theme().background)
|
||||
.child(img(qr).h_64()),
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
use std::str::FromStr;
|
||||
|
||||
use account::Account;
|
||||
use async_utility::task::spawn;
|
||||
use common::nip96_upload;
|
||||
@@ -9,13 +11,12 @@ use gpui::{
|
||||
};
|
||||
use nostr_sdk::prelude::*;
|
||||
use smol::fs;
|
||||
use std::str::FromStr;
|
||||
use theme::ActiveTheme;
|
||||
use ui::{
|
||||
button::{Button, ButtonVariants},
|
||||
dock_area::panel::{Panel, PanelEvent},
|
||||
input::TextInput,
|
||||
popup_menu::PopupMenu,
|
||||
theme::{scale::ColorScaleStep, ActiveTheme},
|
||||
Disableable, Icon, IconName, Sizable, Size, StyledExt,
|
||||
};
|
||||
|
||||
@@ -290,7 +291,7 @@ impl Render for NewAccount {
|
||||
.my_2()
|
||||
.w_full()
|
||||
.h_px()
|
||||
.bg(cx.theme().base.step(cx, ColorScaleStep::THREE)),
|
||||
.bg(cx.theme().elevated_surface_background),
|
||||
)
|
||||
.child(
|
||||
Button::new("submit")
|
||||
|
||||
@@ -2,11 +2,11 @@ use gpui::{
|
||||
div, relative, svg, AnyElement, App, AppContext, Context, Entity, EventEmitter, FocusHandle,
|
||||
Focusable, IntoElement, ParentElement, Render, SharedString, Styled, Window,
|
||||
};
|
||||
use theme::ActiveTheme;
|
||||
use ui::{
|
||||
button::{Button, ButtonVariants},
|
||||
dock_area::panel::{Panel, PanelEvent},
|
||||
popup_menu::PopupMenu,
|
||||
theme::{scale::ColorScaleStep, ActiveTheme},
|
||||
Icon, IconName, StyledExt,
|
||||
};
|
||||
|
||||
@@ -91,7 +91,7 @@ impl Render for Onboarding {
|
||||
svg()
|
||||
.path("brand/coop.svg")
|
||||
.size_16()
|
||||
.text_color(cx.theme().base.step(cx, ColorScaleStep::THREE)),
|
||||
.text_color(cx.theme().elevated_surface_background),
|
||||
)
|
||||
.child(
|
||||
div()
|
||||
@@ -103,11 +103,7 @@ impl Render for Onboarding {
|
||||
.line_height(relative(1.3))
|
||||
.child(TITLE),
|
||||
)
|
||||
.child(
|
||||
div()
|
||||
.text_color(cx.theme().base.step(cx, ColorScaleStep::ELEVEN))
|
||||
.child(SUBTITLE),
|
||||
),
|
||||
.child(div().text_color(cx.theme().text_muted).child(SUBTITLE)),
|
||||
),
|
||||
)
|
||||
.child(
|
||||
|
||||
@@ -2,16 +2,16 @@ use async_utility::task::spawn;
|
||||
use common::nip96_upload;
|
||||
use global::{constants::IMAGE_SERVICE, get_client};
|
||||
use gpui::{
|
||||
div, img, prelude::FluentBuilder, px, App, AppContext, Context, Entity, Flatten, IntoElement,
|
||||
div, img, prelude::FluentBuilder, App, AppContext, Context, Entity, Flatten, IntoElement,
|
||||
ParentElement, PathPromptOptions, Render, Styled, Task, Window,
|
||||
};
|
||||
use nostr_sdk::prelude::*;
|
||||
use smol::fs;
|
||||
use std::{str::FromStr, time::Duration};
|
||||
use theme::ActiveTheme;
|
||||
use ui::{
|
||||
button::{Button, ButtonVariants},
|
||||
input::TextInput,
|
||||
theme::{scale::ColorScaleStep, ActiveTheme},
|
||||
ContextModal, Disableable, IconName, Sizable, Size,
|
||||
};
|
||||
|
||||
@@ -249,8 +249,8 @@ impl Render for Profile {
|
||||
div()
|
||||
.w_full()
|
||||
.h_32()
|
||||
.bg(cx.theme().base.step(cx, ColorScaleStep::TWO))
|
||||
.rounded(px(cx.theme().radius))
|
||||
.bg(cx.theme().surface_background)
|
||||
.rounded(cx.theme().radius)
|
||||
.flex()
|
||||
.flex_col()
|
||||
.items_center()
|
||||
|
||||
@@ -7,10 +7,10 @@ use gpui::{
|
||||
};
|
||||
use nostr_sdk::prelude::*;
|
||||
use smallvec::{smallvec, SmallVec};
|
||||
use theme::ActiveTheme;
|
||||
use ui::{
|
||||
button::{Button, ButtonVariants},
|
||||
input::{InputEvent, TextInput},
|
||||
theme::{scale::ColorScaleStep, ActiveTheme},
|
||||
ContextModal, Disableable, IconName, Sizable,
|
||||
};
|
||||
|
||||
@@ -241,8 +241,8 @@ impl Relays {
|
||||
.flex()
|
||||
.items_center()
|
||||
.justify_between()
|
||||
.rounded(px(cx.theme().radius))
|
||||
.bg(cx.theme().base.step(cx, ColorScaleStep::THREE))
|
||||
.rounded(cx.theme().radius)
|
||||
.bg(cx.theme().elevated_surface_background)
|
||||
.text_xs()
|
||||
.child(item)
|
||||
.child(
|
||||
@@ -298,7 +298,7 @@ impl Render for Relays {
|
||||
.child(
|
||||
div()
|
||||
.text_sm()
|
||||
.text_color(cx.theme().base.step(cx, ColorScaleStep::ELEVEN))
|
||||
.text_color(cx.theme().text_muted)
|
||||
.child(MESSAGE),
|
||||
)
|
||||
.child(
|
||||
@@ -320,7 +320,7 @@ impl Render for Relays {
|
||||
.label("Add")
|
||||
.small()
|
||||
.ghost()
|
||||
.rounded(px(cx.theme().radius))
|
||||
.rounded_md()
|
||||
.on_click(cx.listener(|this, _, window, cx| {
|
||||
this.add(window, cx)
|
||||
})),
|
||||
|
||||
@@ -6,19 +6,19 @@ use chats::ChatRegistry;
|
||||
use common::profile::SharedProfile;
|
||||
use global::{constants::SEARCH_RELAYS, get_client};
|
||||
use gpui::{
|
||||
div, img, prelude::FluentBuilder, px, relative, uniform_list, App, AppContext, Context, Entity,
|
||||
InteractiveElement, IntoElement, ParentElement, Render, SharedString, Styled, Subscription,
|
||||
Task, Window,
|
||||
div, img, prelude::FluentBuilder, px, red, relative, uniform_list, App, AppContext, Context,
|
||||
Entity, InteractiveElement, IntoElement, ParentElement, Render, SharedString, Styled,
|
||||
Subscription, Task, Window,
|
||||
};
|
||||
use itertools::Itertools;
|
||||
use nostr_sdk::prelude::*;
|
||||
use smallvec::{smallvec, SmallVec};
|
||||
use theme::ActiveTheme;
|
||||
use ui::{
|
||||
button::{Button, ButtonVariants},
|
||||
dock_area::dock::DockPlacement,
|
||||
indicator::Indicator,
|
||||
input::{InputEvent, TextInput},
|
||||
theme::{scale::ColorScaleStep, ActiveTheme},
|
||||
ContextModal, Disableable, IconName, Sizable,
|
||||
};
|
||||
|
||||
@@ -202,8 +202,6 @@ impl Search {
|
||||
|
||||
impl Render for Search {
|
||||
fn render(&mut self, _window: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
|
||||
let mute_color = cx.theme().base.step(cx, ColorScaleStep::NINE);
|
||||
|
||||
div()
|
||||
.size_full()
|
||||
.flex()
|
||||
@@ -233,7 +231,7 @@ impl Render for Search {
|
||||
div()
|
||||
.px_3()
|
||||
.text_xs()
|
||||
.text_color(cx.theme().danger)
|
||||
.text_color(red())
|
||||
.child(error.clone()),
|
||||
)
|
||||
})
|
||||
@@ -254,7 +252,7 @@ impl Render for Search {
|
||||
.items_center()
|
||||
.justify_center()
|
||||
.text_sm()
|
||||
.text_color(mute_color)
|
||||
.text_color(cx.theme().text_muted)
|
||||
.child("No one with that query could be found.")
|
||||
} else {
|
||||
this.child(
|
||||
@@ -278,7 +276,7 @@ impl Render for Search {
|
||||
.flex()
|
||||
.items_center()
|
||||
.justify_between()
|
||||
.rounded(px(cx.theme().radius))
|
||||
.rounded(cx.theme().radius)
|
||||
.child(
|
||||
div()
|
||||
.flex()
|
||||
@@ -305,7 +303,10 @@ impl Render for Search {
|
||||
this.child(
|
||||
div()
|
||||
.text_xs()
|
||||
.text_color(mute_color)
|
||||
.text_color(
|
||||
cx.theme()
|
||||
.text_muted,
|
||||
)
|
||||
.child(nip05),
|
||||
)
|
||||
},
|
||||
@@ -335,10 +336,7 @@ impl Render for Search {
|
||||
),
|
||||
)
|
||||
.hover(|this| {
|
||||
this.bg(cx
|
||||
.theme()
|
||||
.base
|
||||
.step(cx, ColorScaleStep::THREE))
|
||||
this.bg(cx.theme().elevated_surface_background)
|
||||
}),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,13 +1,11 @@
|
||||
use std::rc::Rc;
|
||||
|
||||
use gpui::{
|
||||
div, prelude::FluentBuilder, px, App, ClickEvent, Div, InteractiveElement, IntoElement,
|
||||
div, prelude::FluentBuilder, App, ClickEvent, Div, InteractiveElement, IntoElement,
|
||||
ParentElement, RenderOnce, SharedString, StatefulInteractiveElement, Styled, Window,
|
||||
};
|
||||
use ui::{
|
||||
theme::{scale::ColorScaleStep, ActiveTheme},
|
||||
Icon,
|
||||
};
|
||||
use theme::ActiveTheme;
|
||||
use ui::Icon;
|
||||
|
||||
type Handler = Rc<dyn Fn(&ClickEvent, &mut Window, &mut App)>;
|
||||
|
||||
@@ -49,16 +47,12 @@ impl RenderOnce for SidebarButton {
|
||||
|
||||
self.base
|
||||
.id(self.label.clone())
|
||||
.rounded(px(cx.theme().radius))
|
||||
.rounded(cx.theme().radius)
|
||||
.when_some(self.icon, |this, icon| {
|
||||
this.child(
|
||||
div()
|
||||
.text_color(cx.theme().base.step(cx, ColorScaleStep::ELEVEN))
|
||||
.child(icon),
|
||||
)
|
||||
this.child(div().text_color(cx.theme().text_muted).child(icon))
|
||||
})
|
||||
.child(self.label.clone())
|
||||
.hover(|this| this.bg(cx.theme().base.step(cx, ColorScaleStep::THREE)))
|
||||
.hover(|this| this.bg(cx.theme().elevated_surface_background))
|
||||
.on_click(move |ev, window, cx| handler(ev, window, cx))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,15 +1,12 @@
|
||||
use std::rc::Rc;
|
||||
|
||||
use gpui::{
|
||||
div, percentage, prelude::FluentBuilder, px, App, ClickEvent, Div, Img, InteractiveElement,
|
||||
div, percentage, prelude::FluentBuilder, App, ClickEvent, Div, Img, InteractiveElement,
|
||||
IntoElement, ParentElement as _, RenderOnce, SharedString, StatefulInteractiveElement, Styled,
|
||||
Window,
|
||||
};
|
||||
use ui::{
|
||||
theme::{scale::ColorScaleStep, ActiveTheme},
|
||||
tooltip::Tooltip,
|
||||
Collapsible, Icon, IconName, Sizable, StyledExt,
|
||||
};
|
||||
use theme::ActiveTheme;
|
||||
use ui::{tooltip::Tooltip, Collapsible, Icon, IconName, Sizable, StyledExt};
|
||||
|
||||
type Handler = Rc<dyn Fn(&ClickEvent, &mut Window, &mut App)>;
|
||||
|
||||
@@ -96,9 +93,9 @@ impl RenderOnce for Parent {
|
||||
.gap_2()
|
||||
.px_2()
|
||||
.h_8()
|
||||
.rounded(px(cx.theme().radius))
|
||||
.rounded(cx.theme().radius)
|
||||
.text_sm()
|
||||
.text_color(cx.theme().base.step(cx, ColorScaleStep::ELEVEN))
|
||||
.text_color(cx.theme().text_muted)
|
||||
.font_medium()
|
||||
.child(
|
||||
Icon::new(IconName::CaretDown)
|
||||
@@ -118,7 +115,7 @@ impl RenderOnce for Parent {
|
||||
Tooltip::new(tooltip.clone(), window, cx).into()
|
||||
})
|
||||
})
|
||||
.hover(|this| this.bg(cx.theme().base.step(cx, ColorScaleStep::THREE)))
|
||||
.hover(|this| this.bg(cx.theme().elevated_surface_background))
|
||||
.on_click(move |ev, window, cx| handler(ev, window, cx)),
|
||||
)
|
||||
.when(!self.collapsed, |this| {
|
||||
@@ -204,9 +201,9 @@ impl RenderOnce for Folder {
|
||||
.gap_2()
|
||||
.px_2()
|
||||
.h_8()
|
||||
.rounded(px(cx.theme().radius))
|
||||
.rounded(cx.theme().radius)
|
||||
.text_sm()
|
||||
.text_color(cx.theme().base.step(cx, ColorScaleStep::ELEVEN))
|
||||
.text_color(cx.theme().text_muted)
|
||||
.font_medium()
|
||||
.child(
|
||||
Icon::new(IconName::CaretDown)
|
||||
@@ -226,7 +223,7 @@ impl RenderOnce for Folder {
|
||||
Tooltip::new(tooltip.clone(), window, cx).into()
|
||||
})
|
||||
})
|
||||
.hover(|this| this.bg(cx.theme().base.step(cx, ColorScaleStep::THREE)))
|
||||
.hover(|this| this.bg(cx.theme().elevated_surface_background))
|
||||
.on_click(move |ev, window, cx| handler(ev, window, cx)),
|
||||
)
|
||||
.when(!self.collapsed, |this| {
|
||||
@@ -291,7 +288,7 @@ impl RenderOnce for FolderItem {
|
||||
.items_center()
|
||||
.justify_between()
|
||||
.text_sm()
|
||||
.rounded(px(cx.theme().radius))
|
||||
.rounded(cx.theme().radius)
|
||||
.child(
|
||||
div()
|
||||
.flex_1()
|
||||
@@ -312,11 +309,11 @@ impl RenderOnce for FolderItem {
|
||||
.items_center()
|
||||
.size_5()
|
||||
.rounded_full()
|
||||
.bg(cx.theme().accent.step(cx, ColorScaleStep::THREE))
|
||||
.bg(cx.theme().element_disabled)
|
||||
.child(
|
||||
Icon::new(IconName::UsersThreeFill).xsmall().text_color(
|
||||
cx.theme().accent.step(cx, ColorScaleStep::TWELVE),
|
||||
),
|
||||
Icon::new(IconName::UsersThreeFill)
|
||||
.xsmall()
|
||||
.text_color(cx.theme().text_accent),
|
||||
),
|
||||
)
|
||||
}
|
||||
@@ -328,11 +325,11 @@ impl RenderOnce for FolderItem {
|
||||
div()
|
||||
.flex_shrink_0()
|
||||
.text_xs()
|
||||
.text_color(cx.theme().base.step(cx, ColorScaleStep::TEN))
|
||||
.text_color(cx.theme().text_placeholder)
|
||||
.child(description),
|
||||
)
|
||||
})
|
||||
.hover(|this| this.bg(cx.theme().base.step(cx, ColorScaleStep::THREE)))
|
||||
.hover(|this| this.bg(cx.theme().elevated_surface_background))
|
||||
.on_click(move |ev, window, cx| handler(ev, window, cx))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,6 +15,7 @@ use gpui::{
|
||||
ScrollHandle, SharedString, StatefulInteractiveElement, Styled, Task, Window,
|
||||
};
|
||||
use itertools::Itertools;
|
||||
use theme::ActiveTheme;
|
||||
use ui::{
|
||||
button::{Button, ButtonCustomVariant, ButtonVariants},
|
||||
dock_area::{
|
||||
@@ -23,7 +24,6 @@ use ui::{
|
||||
},
|
||||
popup_menu::{PopupMenu, PopupMenuExt},
|
||||
skeleton::Skeleton,
|
||||
theme::{scale::ColorScaleStep, ActiveTheme},
|
||||
IconName, Sizable, StyledExt,
|
||||
};
|
||||
|
||||
@@ -305,7 +305,7 @@ impl Render for Sidebar {
|
||||
.items_center()
|
||||
.text_xs()
|
||||
.font_semibold()
|
||||
.text_color(cx.theme().base.step(cx, ColorScaleStep::NINE))
|
||||
.text_color(cx.theme().text_placeholder)
|
||||
.child("Messages")
|
||||
.child(
|
||||
Button::new("menu")
|
||||
@@ -320,13 +320,10 @@ impl Render for Sidebar {
|
||||
.small()
|
||||
.custom(
|
||||
ButtonCustomVariant::new(window, cx)
|
||||
.foreground(
|
||||
cx.theme().base.step(cx, ColorScaleStep::NINE),
|
||||
)
|
||||
.color(cx.theme().transparent)
|
||||
.hover(cx.theme().transparent)
|
||||
.active(cx.theme().transparent)
|
||||
.border(cx.theme().transparent),
|
||||
.foreground(cx.theme().text_placeholder)
|
||||
.color(cx.theme().ghost_element_background)
|
||||
.hover(cx.theme().ghost_element_background)
|
||||
.active(cx.theme().ghost_element_background),
|
||||
)
|
||||
.on_click(cx.listener(move |this, _, _, cx| {
|
||||
this.split_into_folders(cx);
|
||||
|
||||
@@ -3,10 +3,10 @@ use gpui::{
|
||||
div, App, AppContext, Context, Entity, FocusHandle, InteractiveElement, IntoElement,
|
||||
ParentElement, Render, Styled, Window,
|
||||
};
|
||||
use theme::ActiveTheme;
|
||||
use ui::{
|
||||
button::{Button, ButtonVariants},
|
||||
input::TextInput,
|
||||
theme::{scale::ColorScaleStep, ActiveTheme},
|
||||
ContextModal, Size,
|
||||
};
|
||||
|
||||
@@ -90,7 +90,7 @@ impl Render for Subject {
|
||||
.child(
|
||||
div()
|
||||
.text_sm()
|
||||
.text_color(cx.theme().base.step(cx, ColorScaleStep::ELEVEN))
|
||||
.text_color(cx.theme().text_muted)
|
||||
.child("Subject:"),
|
||||
)
|
||||
.child(self.input.clone())
|
||||
@@ -98,7 +98,7 @@ impl Render for Subject {
|
||||
div()
|
||||
.text_xs()
|
||||
.italic()
|
||||
.text_color(cx.theme().base.step(cx, ColorScaleStep::NINE))
|
||||
.text_color(cx.theme().text_placeholder)
|
||||
.child(HELP_TEXT),
|
||||
),
|
||||
)
|
||||
|
||||
@@ -2,11 +2,11 @@ use gpui::{
|
||||
div, svg, AnyElement, App, AppContext, Context, Entity, EventEmitter, FocusHandle, Focusable,
|
||||
IntoElement, ParentElement, Render, SharedString, Styled, Window,
|
||||
};
|
||||
use theme::ActiveTheme;
|
||||
use ui::{
|
||||
button::Button,
|
||||
dock_area::panel::{Panel, PanelEvent},
|
||||
popup_menu::PopupMenu,
|
||||
theme::{scale::ColorScaleStep, ActiveTheme},
|
||||
StyledExt,
|
||||
};
|
||||
|
||||
@@ -87,12 +87,12 @@ impl Render for Welcome {
|
||||
svg()
|
||||
.path("brand/coop.svg")
|
||||
.size_12()
|
||||
.text_color(cx.theme().base.step(cx, ColorScaleStep::THREE)),
|
||||
.text_color(cx.theme().elevated_surface_background),
|
||||
)
|
||||
.child(
|
||||
div()
|
||||
.child("coop on nostr.")
|
||||
.text_color(cx.theme().base.step(cx, ColorScaleStep::NINE))
|
||||
.text_color(cx.theme().text_placeholder)
|
||||
.font_semibold()
|
||||
.text_sm(),
|
||||
),
|
||||
|
||||
10
crates/theme/Cargo.toml
Normal file
10
crates/theme/Cargo.toml
Normal file
@@ -0,0 +1,10 @@
|
||||
[package]
|
||||
name = "theme"
|
||||
version.workspace = true
|
||||
edition.workspace = true
|
||||
publish.workspace = true
|
||||
|
||||
[dependencies]
|
||||
gpui.workspace = true
|
||||
anyhow.workspace = true
|
||||
log.workspace = true
|
||||
1
crates/theme/LICENSE
Normal file
1
crates/theme/LICENSE
Normal file
@@ -0,0 +1 @@
|
||||
../../LICENSE
|
||||
1
crates/theme/README.md
Normal file
1
crates/theme/README.md
Normal file
@@ -0,0 +1 @@
|
||||
CREDITS: [zed/theme](https://github.com/zed-industries/zed/tree/main/crates/theme)
|
||||
@@ -1,7 +1,15 @@
|
||||
use super::scale::{ColorScaleSet, ColorScales};
|
||||
use crate::theme::scale::ColorScale;
|
||||
use gpui::{hsla, Hsla, Rgba};
|
||||
|
||||
use crate::scale::{ColorScale, ColorScaleSet, ColorScales};
|
||||
|
||||
pub(crate) fn neutral() -> ColorScaleSet {
|
||||
gray()
|
||||
}
|
||||
|
||||
pub(crate) fn brand() -> ColorScaleSet {
|
||||
yellow()
|
||||
}
|
||||
|
||||
/// Make a [gpui::Hsla] color.
|
||||
///
|
||||
/// - h: 0..360.0
|
||||
375
crates/theme/src/lib.rs
Normal file
375
crates/theme/src/lib.rs
Normal file
@@ -0,0 +1,375 @@
|
||||
use std::ops::{Deref, DerefMut};
|
||||
|
||||
use colors::{brand, hsl, neutral};
|
||||
use gpui::{black, px, white, App, Global, Hsla, Pixels, SharedString, Window, WindowAppearance};
|
||||
|
||||
mod colors;
|
||||
mod scale;
|
||||
|
||||
pub fn init(cx: &mut App) {
|
||||
Theme::sync_system_appearance(None, cx);
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, Default)]
|
||||
pub struct ThemeColor {
|
||||
/// Border color. Used for most borders, is usually a high contrast color.
|
||||
pub border: Hsla,
|
||||
/// Border color. Used for deemphasized borders, like a visual divider between two sections
|
||||
pub border_variant: Hsla,
|
||||
/// Border color. Used for focused elements, like keyboard focused list item.
|
||||
pub border_focused: Hsla,
|
||||
/// Border color. Used for selected elements, like an active search filter or selected checkbox.
|
||||
pub border_selected: Hsla,
|
||||
/// Border color. Used for transparent borders. Used for placeholder borders when an element gains a border on state change.
|
||||
pub border_transparent: Hsla,
|
||||
/// Border color. Used for disabled elements, like a disabled input or button.
|
||||
pub border_disabled: Hsla,
|
||||
/// Background color. Used for elevated surfaces, like a context menu, popup, or dialog.
|
||||
pub elevated_surface_background: Hsla,
|
||||
/// Background color. Used for grounded surfaces like a panel or tab.
|
||||
pub surface_background: Hsla,
|
||||
/// Background color. Used for the app background and blank panels or windows.
|
||||
pub background: Hsla,
|
||||
/// Text color. Used for the foreground of an element.
|
||||
pub element_foreground: Hsla,
|
||||
/// Background color. Used for the background of an element that should have a different background than the surface it's on.
|
||||
///
|
||||
/// Elements might include: Buttons, Inputs, Checkboxes, Radio Buttons...
|
||||
///
|
||||
/// For an element that should have the same background as the surface it's on, use `ghost_element_background`.
|
||||
pub element_background: Hsla,
|
||||
/// Background color. Used for the hover state of an element that should have a different background than the surface it's on.
|
||||
///
|
||||
/// Hover states are triggered by the mouse entering an element, or a finger touching an element on a touch screen.
|
||||
pub element_hover: Hsla,
|
||||
/// Background color. Used for the active state of an element that should have a different background than the surface it's on.
|
||||
///
|
||||
/// Active states are triggered by the mouse button being pressed down on an element, or the Return button or other activator being pressed.
|
||||
pub element_active: Hsla,
|
||||
/// Background color. Used for the selected state of an element that should have a different background than the surface it's on.
|
||||
///
|
||||
/// Selected states are triggered by the element being selected (or "activated") by the user.
|
||||
///
|
||||
/// This could include a selected checkbox, a toggleable button that is toggled on, etc.
|
||||
pub element_selected: Hsla,
|
||||
/// Background Color. Used for the disabled state of a element that should have a different background than the surface it's on.
|
||||
///
|
||||
/// Disabled states are shown when a user cannot interact with an element, like a disabled button or input.
|
||||
pub element_disabled: Hsla,
|
||||
/// Background color. Used for the area that shows where a dragged element will be dropped.
|
||||
pub drop_target_background: Hsla,
|
||||
/// Used for the background of a ghost element that should have the same background as the surface it's on.
|
||||
///
|
||||
/// Elements might include: Buttons, Inputs, Checkboxes, Radio Buttons...
|
||||
///
|
||||
/// For an element that should have a different background than the surface it's on, use `element_background`.
|
||||
pub ghost_element_background: Hsla,
|
||||
/// Background Color. Used for the hover state of a ghost element that should have the same background as the surface it's on.
|
||||
///
|
||||
/// Hover states are triggered by the mouse entering an element, or a finger touching an element on a touch screen.
|
||||
pub ghost_element_hover: Hsla,
|
||||
/// Background Color. Used for the active state of a ghost element that should have the same background as the surface it's on.
|
||||
///
|
||||
/// Active states are triggered by the mouse button being pressed down on an element, or the Return button or other activator being pressed.
|
||||
pub ghost_element_active: Hsla,
|
||||
/// Background Color. Used for the selected state of a ghost element that should have the same background as the surface it's on.
|
||||
///
|
||||
/// Selected states are triggered by the element being selected (or "activated") by the user.
|
||||
///
|
||||
/// This could include a selected checkbox, a toggleable button that is toggled on, etc.
|
||||
pub ghost_element_selected: Hsla,
|
||||
/// Background Color. Used for the disabled state of a ghost element that should have the same background as the surface it's on.
|
||||
///
|
||||
/// Disabled states are shown when a user cannot interact with an element, like a disabled button or input.
|
||||
pub ghost_element_disabled: Hsla,
|
||||
/// Text color. Default text color used for most text.
|
||||
pub text: Hsla,
|
||||
/// Text color. Color of muted or deemphasized text. It is a subdued version of the standard text color.
|
||||
pub text_muted: Hsla,
|
||||
/// Text color. Color of the placeholder text typically shown in input fields to guide the user to enter valid data.
|
||||
pub text_placeholder: Hsla,
|
||||
/// Text color. Color used for emphasis or highlighting certain text, like an active filter or a matched character in a search.
|
||||
pub text_accent: Hsla,
|
||||
/// Fill color. Used for the default fill color of an icon.
|
||||
pub icon: Hsla,
|
||||
/// Fill color. Used for the muted or deemphasized fill color of an icon.
|
||||
///
|
||||
/// This might be used to show an icon in an inactive pane, or to deemphasize a series of icons to give them less visual weight.
|
||||
pub icon_muted: Hsla,
|
||||
/// Fill color. Used for the accent fill color of an icon.
|
||||
///
|
||||
/// This might be used to show when a toggleable icon button is selected.
|
||||
pub icon_accent: Hsla,
|
||||
/// The color of the scrollbar thumb.
|
||||
pub scrollbar_thumb_background: Hsla,
|
||||
/// The color of the scrollbar thumb when hovered over.
|
||||
pub scrollbar_thumb_hover_background: Hsla,
|
||||
/// The border color of the scrollbar thumb.
|
||||
pub scrollbar_thumb_border: Hsla,
|
||||
/// The background color of the scrollbar track.
|
||||
pub scrollbar_track_background: Hsla,
|
||||
/// The border color of the scrollbar track.
|
||||
pub scrollbar_track_border: Hsla,
|
||||
/// Background color. Used for the background of a panel
|
||||
pub panel_background: Hsla,
|
||||
/// Border color. Used for outline border.
|
||||
pub ring: Hsla,
|
||||
/// Background color. Used for inactive tab.
|
||||
pub tab_inactive_background: Hsla,
|
||||
/// Background color. Used for hovered tab.
|
||||
pub tab_hover_background: Hsla,
|
||||
/// Background color. Used for active tab.
|
||||
pub tab_active_background: Hsla,
|
||||
/// Background color. Used for Title Bar.
|
||||
pub title_bar: Hsla,
|
||||
/// Border color. Used for Title Bar.
|
||||
pub title_bar_border: Hsla,
|
||||
/// Background color. Used for modal's overlay.
|
||||
pub overlay: Hsla,
|
||||
/// Window border color.
|
||||
///
|
||||
/// # Platform specific:
|
||||
///
|
||||
/// This is only works on Linux, other platforms we can't change the window border color.
|
||||
pub window_border: Hsla,
|
||||
}
|
||||
|
||||
/// The default colors for the theme.
|
||||
///
|
||||
/// Themes that do not specify all colors are refined off of these defaults.
|
||||
impl ThemeColor {
|
||||
/// Returns the default colors for light themes.
|
||||
///
|
||||
/// Themes that do not specify all colors are refined off of these defaults.
|
||||
pub fn light() -> Self {
|
||||
Self {
|
||||
border: neutral().light().step_6(),
|
||||
border_variant: neutral().light().step_5(),
|
||||
border_focused: brand().light().step_7(),
|
||||
border_selected: brand().light().step_7(),
|
||||
border_transparent: gpui::transparent_black(),
|
||||
border_disabled: neutral().light().step_3(),
|
||||
elevated_surface_background: neutral().light().step_3(),
|
||||
surface_background: neutral().light().step_2(),
|
||||
background: neutral().light().step_1(),
|
||||
element_foreground: brand().light().step_12(),
|
||||
element_background: brand().light().step_9(),
|
||||
element_hover: brand().light_alpha().step_10(),
|
||||
element_active: brand().light().step_10(),
|
||||
element_selected: brand().light().step_10(),
|
||||
element_disabled: brand().light_alpha().step_3(),
|
||||
drop_target_background: brand().light_alpha().step_2(),
|
||||
ghost_element_background: gpui::transparent_black(),
|
||||
ghost_element_hover: neutral().light_alpha().step_3(),
|
||||
ghost_element_active: neutral().light_alpha().step_4(),
|
||||
ghost_element_selected: neutral().light_alpha().step_5(),
|
||||
ghost_element_disabled: neutral().light_alpha().step_3(),
|
||||
text: neutral().light().step_12(),
|
||||
text_muted: neutral().light().step_11(),
|
||||
text_placeholder: neutral().light().step_10(),
|
||||
text_accent: brand().light().step_11(),
|
||||
icon: neutral().light().step_11(),
|
||||
icon_muted: neutral().light().step_10(),
|
||||
icon_accent: brand().light().step_11(),
|
||||
scrollbar_thumb_background: neutral().light_alpha().step_3(),
|
||||
scrollbar_thumb_hover_background: neutral().light_alpha().step_4(),
|
||||
scrollbar_thumb_border: gpui::transparent_black(),
|
||||
scrollbar_track_background: gpui::transparent_black(),
|
||||
scrollbar_track_border: neutral().light().step_5(),
|
||||
panel_background: white(),
|
||||
ring: brand().light().step_8(),
|
||||
tab_active_background: neutral().light().step_5(),
|
||||
tab_hover_background: neutral().light().step_4(),
|
||||
tab_inactive_background: neutral().light().step_3(),
|
||||
title_bar: gpui::transparent_black(),
|
||||
title_bar_border: gpui::transparent_black(),
|
||||
overlay: neutral().light_alpha().step_3(),
|
||||
window_border: hsl(240.0, 5.9, 78.0),
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the default colors for dark themes.
|
||||
///
|
||||
/// Themes that do not specify all colors are refined off of these defaults.
|
||||
pub fn dark() -> Self {
|
||||
Self {
|
||||
border: neutral().dark().step_6(),
|
||||
border_variant: neutral().dark().step_5(),
|
||||
border_focused: brand().dark().step_7(),
|
||||
border_selected: brand().dark().step_7(),
|
||||
border_transparent: gpui::transparent_black(),
|
||||
border_disabled: neutral().light().step_3(),
|
||||
elevated_surface_background: neutral().dark().step_3(),
|
||||
surface_background: neutral().dark().step_2(),
|
||||
background: neutral().dark().step_1(),
|
||||
element_foreground: brand().dark().step_12(),
|
||||
element_background: brand().dark().step_9(),
|
||||
element_hover: brand().dark_alpha().step_10(),
|
||||
element_active: brand().dark().step_10(),
|
||||
element_selected: brand().dark().step_10(),
|
||||
element_disabled: brand().dark_alpha().step_3(),
|
||||
drop_target_background: brand().dark_alpha().step_2(),
|
||||
ghost_element_background: gpui::transparent_black(),
|
||||
ghost_element_hover: neutral().dark_alpha().step_3(),
|
||||
ghost_element_active: neutral().dark_alpha().step_4(),
|
||||
ghost_element_selected: neutral().dark_alpha().step_5(),
|
||||
ghost_element_disabled: neutral().dark_alpha().step_3(),
|
||||
text: neutral().dark().step_12(),
|
||||
text_muted: neutral().dark().step_11(),
|
||||
text_placeholder: neutral().dark().step_10(),
|
||||
text_accent: brand().dark().step_11(),
|
||||
icon: neutral().dark().step_11(),
|
||||
icon_muted: neutral().dark().step_10(),
|
||||
icon_accent: brand().dark().step_11(),
|
||||
scrollbar_thumb_background: neutral().dark_alpha().step_3(),
|
||||
scrollbar_thumb_hover_background: neutral().dark_alpha().step_4(),
|
||||
scrollbar_thumb_border: gpui::transparent_black(),
|
||||
scrollbar_track_background: gpui::transparent_black(),
|
||||
scrollbar_track_border: neutral().dark().step_5(),
|
||||
panel_background: black(),
|
||||
ring: brand().dark().step_8(),
|
||||
tab_active_background: neutral().dark().step_5(),
|
||||
tab_hover_background: neutral().dark().step_4(),
|
||||
tab_inactive_background: neutral().dark().step_3(),
|
||||
title_bar: gpui::transparent_black(),
|
||||
title_bar_border: gpui::transparent_black(),
|
||||
overlay: neutral().dark_alpha().step_3(),
|
||||
window_border: hsl(240.0, 3.7, 28.0),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub trait ActiveTheme {
|
||||
fn theme(&self) -> &Theme;
|
||||
}
|
||||
|
||||
impl ActiveTheme for App {
|
||||
#[inline(always)]
|
||||
fn theme(&self) -> &Theme {
|
||||
Theme::global(self)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, Default, PartialEq, PartialOrd, Eq, Hash)]
|
||||
pub enum ThemeMode {
|
||||
Light,
|
||||
#[default]
|
||||
Dark,
|
||||
}
|
||||
|
||||
impl ThemeMode {
|
||||
pub fn is_dark(&self) -> bool {
|
||||
matches!(self, Self::Dark)
|
||||
}
|
||||
|
||||
/// Return lower_case theme name: `light`, `dark`.
|
||||
pub fn name(&self) -> &'static str {
|
||||
match self {
|
||||
ThemeMode::Light => "light",
|
||||
ThemeMode::Dark => "dark",
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<WindowAppearance> for ThemeMode {
|
||||
fn from(appearance: WindowAppearance) -> Self {
|
||||
match appearance {
|
||||
WindowAppearance::Dark | WindowAppearance::VibrantDark => Self::Dark,
|
||||
WindowAppearance::Light | WindowAppearance::VibrantLight => Self::Light,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct Theme {
|
||||
pub colors: ThemeColor,
|
||||
pub mode: ThemeMode,
|
||||
pub font_family: SharedString,
|
||||
pub font_size: Pixels,
|
||||
pub radius: Pixels,
|
||||
}
|
||||
|
||||
impl Deref for Theme {
|
||||
type Target = ThemeColor;
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
&self.colors
|
||||
}
|
||||
}
|
||||
|
||||
impl DerefMut for Theme {
|
||||
fn deref_mut(&mut self) -> &mut Self::Target {
|
||||
&mut self.colors
|
||||
}
|
||||
}
|
||||
|
||||
impl Global for Theme {}
|
||||
|
||||
impl Theme {
|
||||
/// Returns the global theme reference
|
||||
pub fn global(cx: &App) -> &Theme {
|
||||
cx.global::<Theme>()
|
||||
}
|
||||
|
||||
/// Returns the global theme mutable reference
|
||||
pub fn global_mut(cx: &mut App) -> &mut Theme {
|
||||
cx.global_mut::<Theme>()
|
||||
}
|
||||
|
||||
/// Returns true if the theme is dark.
|
||||
pub fn is_dark(&self) -> bool {
|
||||
self.mode.is_dark()
|
||||
}
|
||||
|
||||
/// Sync the theme with the system appearance
|
||||
pub fn sync_system_appearance(window: Option<&mut Window>, cx: &mut App) {
|
||||
let appearance = window
|
||||
.as_ref()
|
||||
.map(|window| window.appearance())
|
||||
.unwrap_or_else(|| cx.window_appearance());
|
||||
|
||||
Self::change(appearance, window, cx);
|
||||
}
|
||||
|
||||
pub fn change(mode: impl Into<ThemeMode>, window: Option<&mut Window>, cx: &mut App) {
|
||||
let mode = mode.into();
|
||||
let colors = match mode {
|
||||
ThemeMode::Light => ThemeColor::light(),
|
||||
ThemeMode::Dark => ThemeColor::dark(),
|
||||
};
|
||||
|
||||
if !cx.has_global::<Theme>() {
|
||||
let theme = Theme::from(colors);
|
||||
cx.set_global(theme);
|
||||
}
|
||||
|
||||
let theme = cx.global_mut::<Theme>();
|
||||
|
||||
theme.mode = mode;
|
||||
theme.colors = colors;
|
||||
|
||||
if let Some(window) = window {
|
||||
window.refresh();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<ThemeColor> for Theme {
|
||||
fn from(colors: ThemeColor) -> Self {
|
||||
let mode = ThemeMode::default();
|
||||
|
||||
Theme {
|
||||
font_size: px(15.),
|
||||
font_family: if cfg!(target_os = "macos") {
|
||||
".SystemUIFont".into()
|
||||
} else if cfg!(target_os = "windows") {
|
||||
"Segoe UI".into()
|
||||
} else {
|
||||
"FreeMono".into()
|
||||
},
|
||||
radius: px(5.),
|
||||
mode,
|
||||
colors,
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,4 @@
|
||||
use crate::theme::{ActiveTheme, Appearance};
|
||||
use gpui::{App, Hsla, SharedString};
|
||||
use gpui::{Hsla, SharedString};
|
||||
|
||||
/// A collection of colors that are used to style the UI.
|
||||
///
|
||||
@@ -278,25 +277,4 @@ impl ColorScaleSet {
|
||||
pub fn dark_alpha(&self) -> &ColorScale {
|
||||
&self.dark_alpha
|
||||
}
|
||||
|
||||
pub fn step(&self, cx: &App, step: ColorScaleStep) -> Hsla {
|
||||
match cx.theme().appearance {
|
||||
Appearance::Light => self.light().step(step),
|
||||
Appearance::Dark => self.dark().step(step),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn step_alpha(&self, cx: &App, step: ColorScaleStep) -> Hsla {
|
||||
match cx.theme().appearance {
|
||||
Appearance::Light => self.light_alpha.step(step),
|
||||
Appearance::Dark => self.dark_alpha.step(step),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn darken(&self, cx: &App) -> Hsla {
|
||||
match cx.theme().appearance {
|
||||
Appearance::Light => self.light.step_12(),
|
||||
Appearance::Dark => self.dark.step_1(),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -6,6 +6,7 @@ publish.workspace = true
|
||||
|
||||
[dependencies]
|
||||
common = { path = "../common" }
|
||||
theme = { path = "../theme" }
|
||||
|
||||
nostr-sdk.workspace = true
|
||||
gpui.workspace = true
|
||||
|
||||
@@ -1,35 +1,23 @@
|
||||
use crate::{
|
||||
indicator::Indicator,
|
||||
theme::{scale::ColorScaleStep, ActiveTheme},
|
||||
tooltip::Tooltip,
|
||||
Disableable, Icon, Selectable, Sizable, Size, StyledExt,
|
||||
};
|
||||
use gpui::{
|
||||
div, prelude::FluentBuilder as _, px, relative, AnyElement, App, ClickEvent, Corners, Div,
|
||||
Edges, ElementId, Hsla, InteractiveElement, IntoElement, MouseButton, ParentElement, Pixels,
|
||||
RenderOnce, SharedString, StatefulInteractiveElement as _, Styled, Window,
|
||||
div, prelude::FluentBuilder as _, relative, AnyElement, App, ClickEvent, Div, ElementId, Hsla,
|
||||
InteractiveElement, IntoElement, MouseButton, ParentElement, RenderOnce, SharedString,
|
||||
StatefulInteractiveElement as _, Styled, Window,
|
||||
};
|
||||
use theme::ActiveTheme;
|
||||
|
||||
use crate::{
|
||||
indicator::Indicator, tooltip::Tooltip, Disableable, Icon, Selectable, Sizable, Size, StyledExt,
|
||||
};
|
||||
|
||||
pub enum ButtonRounded {
|
||||
None,
|
||||
Small,
|
||||
Medium,
|
||||
Large,
|
||||
Size(Pixels),
|
||||
}
|
||||
|
||||
impl From<Pixels> for ButtonRounded {
|
||||
fn from(px: Pixels) -> Self {
|
||||
ButtonRounded::Size(px)
|
||||
}
|
||||
Normal,
|
||||
Full,
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, PartialEq, Eq)]
|
||||
pub struct ButtonCustomVariant {
|
||||
color: Hsla,
|
||||
foreground: Hsla,
|
||||
border: Hsla,
|
||||
shadow: bool,
|
||||
hover: Hsla,
|
||||
active: Hsla,
|
||||
}
|
||||
@@ -66,12 +54,10 @@ pub trait ButtonVariants: Sized {
|
||||
impl ButtonCustomVariant {
|
||||
pub fn new(_window: &Window, cx: &App) -> Self {
|
||||
Self {
|
||||
color: cx.theme().accent.step(cx, ColorScaleStep::NINE),
|
||||
foreground: cx.theme().accent.step(cx, ColorScaleStep::ONE),
|
||||
border: cx.theme().accent.step(cx, ColorScaleStep::TEN),
|
||||
hover: cx.theme().accent.step(cx, ColorScaleStep::TEN),
|
||||
active: cx.theme().accent.step(cx, ColorScaleStep::ELEVEN),
|
||||
shadow: true,
|
||||
color: cx.theme().element_background,
|
||||
foreground: cx.theme().element_foreground,
|
||||
hover: cx.theme().element_hover,
|
||||
active: cx.theme().element_active,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -85,11 +71,6 @@ impl ButtonCustomVariant {
|
||||
self
|
||||
}
|
||||
|
||||
pub fn border(mut self, color: Hsla) -> Self {
|
||||
self.border = color;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn hover(mut self, color: Hsla) -> Self {
|
||||
self.hover = color;
|
||||
self
|
||||
@@ -99,11 +80,6 @@ impl ButtonCustomVariant {
|
||||
self.active = color;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn shadow(mut self, shadow: bool) -> Self {
|
||||
self.shadow = shadow;
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
/// The variant of the Button.
|
||||
@@ -149,12 +125,9 @@ pub struct Button {
|
||||
disabled: bool,
|
||||
variant: ButtonVariant,
|
||||
rounded: ButtonRounded,
|
||||
border_corners: Corners<bool>,
|
||||
border_edges: Edges<bool>,
|
||||
size: Size,
|
||||
reverse: bool,
|
||||
bold: bool,
|
||||
centered: bool,
|
||||
tooltip: Option<SharedString>,
|
||||
on_click: OnClick,
|
||||
loading: bool,
|
||||
@@ -179,9 +152,7 @@ impl Button {
|
||||
disabled: false,
|
||||
selected: false,
|
||||
variant: ButtonVariant::default(),
|
||||
rounded: ButtonRounded::Medium,
|
||||
border_corners: Corners::all(true),
|
||||
border_edges: Edges::all(true),
|
||||
rounded: ButtonRounded::Normal,
|
||||
size: Size::Medium,
|
||||
tooltip: None,
|
||||
on_click: None,
|
||||
@@ -189,7 +160,6 @@ impl Button {
|
||||
loading: false,
|
||||
reverse: false,
|
||||
bold: false,
|
||||
centered: true,
|
||||
children: Vec::new(),
|
||||
loading_icon: None,
|
||||
}
|
||||
@@ -201,18 +171,6 @@ impl Button {
|
||||
self
|
||||
}
|
||||
|
||||
/// Set the border corners side of the Button.
|
||||
pub(crate) fn border_corners(mut self, corners: impl Into<Corners<bool>>) -> Self {
|
||||
self.border_corners = corners.into();
|
||||
self
|
||||
}
|
||||
|
||||
/// Set the border edges of the Button.
|
||||
pub(crate) fn border_edges(mut self, edges: impl Into<Edges<bool>>) -> Self {
|
||||
self.border_edges = edges.into();
|
||||
self
|
||||
}
|
||||
|
||||
/// Set label to the Button, if no label is set, the button will be in Icon Button mode.
|
||||
pub fn label(mut self, label: impl Into<SharedString>) -> Self {
|
||||
self.label = Some(label.into());
|
||||
@@ -243,11 +201,6 @@ impl Button {
|
||||
self
|
||||
}
|
||||
|
||||
pub fn not_centered(mut self) -> Self {
|
||||
self.centered = false;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn bold(mut self) -> Self {
|
||||
self.bold = true;
|
||||
self
|
||||
@@ -335,11 +288,12 @@ impl RenderOnce for Button {
|
||||
.id(self.id)
|
||||
.flex()
|
||||
.items_center()
|
||||
.when(self.centered, |this| this.justify_center())
|
||||
.justify_center()
|
||||
.cursor_pointer()
|
||||
.overflow_hidden()
|
||||
.when(cx.theme().shadow && normal_style.shadow, |this| {
|
||||
this.shadow_sm()
|
||||
.map(|this| match self.rounded {
|
||||
ButtonRounded::Normal => this.rounded(cx.theme().radius),
|
||||
ButtonRounded::Full => this.rounded_full(),
|
||||
})
|
||||
.when(!style.no_padding(), |this| {
|
||||
if self.label.is_none() && self.children.is_empty() {
|
||||
@@ -361,50 +315,20 @@ impl RenderOnce for Button {
|
||||
}
|
||||
}
|
||||
})
|
||||
.when(
|
||||
self.border_corners.top_left && self.border_corners.bottom_left,
|
||||
|this| match self.rounded {
|
||||
ButtonRounded::Small => this.rounded_l(px(cx.theme().radius * 0.5)),
|
||||
ButtonRounded::Medium => this.rounded_l(px(cx.theme().radius)),
|
||||
ButtonRounded::Large => this.rounded_l(px(cx.theme().radius * 1.6)),
|
||||
ButtonRounded::Size(px) => this.rounded_l(px),
|
||||
ButtonRounded::None => this.rounded_none(),
|
||||
},
|
||||
)
|
||||
.when(
|
||||
self.border_corners.top_right && self.border_corners.bottom_right,
|
||||
|this| match self.rounded {
|
||||
ButtonRounded::Small => this.rounded_r(px(cx.theme().radius * 0.5)),
|
||||
ButtonRounded::Medium => this.rounded_r(px(cx.theme().radius)),
|
||||
ButtonRounded::Large => this.rounded_r(px(cx.theme().radius * 1.6)),
|
||||
ButtonRounded::Size(px) => this.rounded_r(px),
|
||||
ButtonRounded::None => this.rounded_none(),
|
||||
},
|
||||
)
|
||||
.when(self.border_edges.left, |this| this.border_l_1())
|
||||
.when(self.border_edges.right, |this| this.border_r_1())
|
||||
.when(self.border_edges.top, |this| this.border_t_1())
|
||||
.when(self.border_edges.bottom, |this| this.border_b_1())
|
||||
.text_color(normal_style.fg)
|
||||
.when(self.selected, |this| {
|
||||
let selected_style = style.selected(window, cx);
|
||||
this.bg(selected_style.bg)
|
||||
.border_color(selected_style.border)
|
||||
.text_color(selected_style.fg)
|
||||
this.bg(selected_style.bg).text_color(selected_style.fg)
|
||||
})
|
||||
.when(!self.disabled && !self.selected, |this| {
|
||||
this.border_color(normal_style.border)
|
||||
.bg(normal_style.bg)
|
||||
this.bg(normal_style.bg)
|
||||
.when(normal_style.underline, |this| this.text_decoration_1())
|
||||
.hover(|this| {
|
||||
let hover_style = style.hovered(window, cx);
|
||||
this.bg(hover_style.bg).border_color(hover_style.border)
|
||||
this.bg(hover_style.bg)
|
||||
})
|
||||
.active(|this| {
|
||||
let active_style = style.active(window, cx);
|
||||
this.bg(active_style.bg)
|
||||
.border_color(active_style.border)
|
||||
.text_color(active_style.fg)
|
||||
this.bg(active_style.bg).text_color(active_style.fg)
|
||||
})
|
||||
})
|
||||
.when_some(
|
||||
@@ -427,9 +351,9 @@ impl RenderOnce for Button {
|
||||
this.cursor_not_allowed()
|
||||
.bg(disabled_style.bg)
|
||||
.text_color(disabled_style.fg)
|
||||
.border_color(disabled_style.border)
|
||||
.shadow_none()
|
||||
})
|
||||
.text_color(normal_style.fg)
|
||||
.child({
|
||||
div()
|
||||
.flex()
|
||||
@@ -474,45 +398,26 @@ impl RenderOnce for Button {
|
||||
|
||||
struct ButtonVariantStyle {
|
||||
bg: Hsla,
|
||||
border: Hsla,
|
||||
fg: Hsla,
|
||||
underline: bool,
|
||||
shadow: bool,
|
||||
}
|
||||
|
||||
impl ButtonVariant {
|
||||
fn bg_color(&self, _window: &Window, cx: &App) -> Hsla {
|
||||
match self {
|
||||
ButtonVariant::Primary => cx.theme().accent.step(cx, ColorScaleStep::NINE),
|
||||
ButtonVariant::Primary => cx.theme().element_background,
|
||||
ButtonVariant::Custom(colors) => colors.color,
|
||||
_ => cx.theme().transparent,
|
||||
_ => cx.theme().ghost_element_background,
|
||||
}
|
||||
}
|
||||
|
||||
fn text_color(&self, _window: &Window, cx: &App) -> Hsla {
|
||||
match self {
|
||||
ButtonVariant::Primary => match cx.theme().accent.name().to_string().as_str() {
|
||||
"Sky" => cx.theme().base.darken(cx),
|
||||
"Mint" => cx.theme().base.darken(cx),
|
||||
"Lime" => cx.theme().base.darken(cx),
|
||||
"Amber" => cx.theme().base.darken(cx),
|
||||
"Yellow" => cx.theme().base.darken(cx),
|
||||
_ => cx.theme().accent.step(cx, ColorScaleStep::ONE),
|
||||
},
|
||||
ButtonVariant::Link => cx.theme().accent.step(cx, ColorScaleStep::NINE),
|
||||
ButtonVariant::Ghost => cx.theme().base.step(cx, ColorScaleStep::TWELVE),
|
||||
ButtonVariant::Primary => cx.theme().element_foreground,
|
||||
ButtonVariant::Link => cx.theme().text_accent,
|
||||
ButtonVariant::Ghost => cx.theme().text_muted,
|
||||
ButtonVariant::Custom(colors) => colors.foreground,
|
||||
_ => cx.theme().base.step(cx, ColorScaleStep::TWELVE),
|
||||
}
|
||||
}
|
||||
|
||||
fn border_color(&self, _window: &Window, cx: &App) -> Hsla {
|
||||
match self {
|
||||
ButtonVariant::Primary => cx.theme().accent.step(cx, ColorScaleStep::NINE),
|
||||
ButtonVariant::Ghost | ButtonVariant::Link | ButtonVariant::Text => {
|
||||
cx.theme().transparent
|
||||
}
|
||||
ButtonVariant::Custom(colors) => colors.border,
|
||||
_ => cx.theme().text,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -520,142 +425,79 @@ impl ButtonVariant {
|
||||
matches!(self, ButtonVariant::Link)
|
||||
}
|
||||
|
||||
fn shadow(&self, _window: &Window, _cx: &App) -> bool {
|
||||
match self {
|
||||
ButtonVariant::Primary => true,
|
||||
ButtonVariant::Custom(c) => c.shadow,
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
|
||||
fn normal(&self, window: &Window, cx: &App) -> ButtonVariantStyle {
|
||||
let bg = self.bg_color(window, cx);
|
||||
let border = self.border_color(window, cx);
|
||||
let fg = self.text_color(window, cx);
|
||||
let underline = self.underline(window, cx);
|
||||
let shadow = self.shadow(window, cx);
|
||||
|
||||
ButtonVariantStyle {
|
||||
bg,
|
||||
border,
|
||||
fg,
|
||||
underline,
|
||||
shadow,
|
||||
}
|
||||
ButtonVariantStyle { bg, fg, underline }
|
||||
}
|
||||
|
||||
fn hovered(&self, window: &Window, cx: &App) -> ButtonVariantStyle {
|
||||
let bg = match self {
|
||||
ButtonVariant::Primary => cx.theme().accent.step(cx, ColorScaleStep::TEN),
|
||||
ButtonVariant::Ghost => cx.theme().base.step(cx, ColorScaleStep::FOUR),
|
||||
ButtonVariant::Link => cx.theme().transparent,
|
||||
ButtonVariant::Text => cx.theme().transparent,
|
||||
ButtonVariant::Primary => cx.theme().element_hover,
|
||||
ButtonVariant::Ghost => cx.theme().ghost_element_hover,
|
||||
ButtonVariant::Link => cx.theme().ghost_element_background,
|
||||
ButtonVariant::Text => cx.theme().ghost_element_background,
|
||||
ButtonVariant::Custom(colors) => colors.hover,
|
||||
};
|
||||
let border = self.border_color(window, cx);
|
||||
let fg = match self {
|
||||
ButtonVariant::Ghost => cx.theme().base.step(cx, ColorScaleStep::TWELVE),
|
||||
ButtonVariant::Link => cx.theme().accent.step(cx, ColorScaleStep::TEN),
|
||||
ButtonVariant::Ghost => cx.theme().text,
|
||||
ButtonVariant::Link => cx.theme().text_accent,
|
||||
_ => self.text_color(window, cx),
|
||||
};
|
||||
let underline = self.underline(window, cx);
|
||||
let shadow = self.shadow(window, cx);
|
||||
|
||||
ButtonVariantStyle {
|
||||
bg,
|
||||
border,
|
||||
fg,
|
||||
underline,
|
||||
shadow,
|
||||
}
|
||||
ButtonVariantStyle { bg, fg, underline }
|
||||
}
|
||||
|
||||
fn active(&self, window: &Window, cx: &App) -> ButtonVariantStyle {
|
||||
let bg = match self {
|
||||
ButtonVariant::Primary => cx.theme().accent.step(cx, ColorScaleStep::TEN),
|
||||
ButtonVariant::Ghost => cx.theme().base.step(cx, ColorScaleStep::THREE),
|
||||
ButtonVariant::Link => cx.theme().transparent,
|
||||
ButtonVariant::Text => cx.theme().transparent,
|
||||
ButtonVariant::Primary => cx.theme().element_active,
|
||||
ButtonVariant::Ghost => cx.theme().ghost_element_active,
|
||||
ButtonVariant::Custom(colors) => colors.active,
|
||||
_ => cx.theme().ghost_element_background,
|
||||
};
|
||||
|
||||
let fg = match self {
|
||||
ButtonVariant::Link => cx.theme().accent.step(cx, ColorScaleStep::NINE),
|
||||
ButtonVariant::Text => cx.theme().base.step(cx, ColorScaleStep::ELEVEN),
|
||||
ButtonVariant::Link => cx.theme().text_accent,
|
||||
ButtonVariant::Text => cx.theme().text,
|
||||
_ => self.text_color(window, cx),
|
||||
};
|
||||
|
||||
let border = self.border_color(window, cx);
|
||||
let underline = self.underline(window, cx);
|
||||
let shadow = self.shadow(window, cx);
|
||||
|
||||
ButtonVariantStyle {
|
||||
bg,
|
||||
border,
|
||||
fg,
|
||||
underline,
|
||||
shadow,
|
||||
}
|
||||
ButtonVariantStyle { bg, fg, underline }
|
||||
}
|
||||
|
||||
fn selected(&self, window: &Window, cx: &App) -> ButtonVariantStyle {
|
||||
let bg = match self {
|
||||
ButtonVariant::Primary => cx.theme().accent.step(cx, ColorScaleStep::TEN),
|
||||
ButtonVariant::Ghost => cx.theme().base.step(cx, ColorScaleStep::THREE),
|
||||
ButtonVariant::Link => cx.theme().transparent,
|
||||
ButtonVariant::Text => cx.theme().transparent,
|
||||
ButtonVariant::Primary => cx.theme().element_selected,
|
||||
ButtonVariant::Ghost => cx.theme().ghost_element_selected,
|
||||
ButtonVariant::Custom(colors) => colors.active,
|
||||
_ => cx.theme().ghost_element_background,
|
||||
};
|
||||
|
||||
let fg = match self {
|
||||
ButtonVariant::Link => cx.theme().accent.step(cx, ColorScaleStep::TEN),
|
||||
ButtonVariant::Text => cx.theme().accent.step(cx, ColorScaleStep::TEN),
|
||||
ButtonVariant::Link => cx.theme().text_accent,
|
||||
ButtonVariant::Text => cx.theme().text,
|
||||
_ => self.text_color(window, cx),
|
||||
};
|
||||
|
||||
let border = self.border_color(window, cx);
|
||||
let underline = self.underline(window, cx);
|
||||
let shadow = self.shadow(window, cx);
|
||||
|
||||
ButtonVariantStyle {
|
||||
bg,
|
||||
border,
|
||||
fg,
|
||||
underline,
|
||||
shadow,
|
||||
}
|
||||
ButtonVariantStyle { bg, fg, underline }
|
||||
}
|
||||
|
||||
fn disabled(&self, window: &Window, cx: &App) -> ButtonVariantStyle {
|
||||
let bg = match self {
|
||||
ButtonVariant::Link | ButtonVariant::Ghost | ButtonVariant::Text => {
|
||||
cx.theme().transparent
|
||||
cx.theme().ghost_element_disabled
|
||||
}
|
||||
_ => cx.theme().base.step(cx, ColorScaleStep::THREE),
|
||||
_ => cx.theme().element_disabled,
|
||||
};
|
||||
|
||||
let fg = match self {
|
||||
ButtonVariant::Primary => match cx.theme().accent.name().to_string().as_str() {
|
||||
"Sky" => cx.theme().base.darken(cx),
|
||||
"Mint" => cx.theme().base.darken(cx),
|
||||
"Lime" => cx.theme().base.darken(cx),
|
||||
"Amber" => cx.theme().base.darken(cx),
|
||||
"Yellow" => cx.theme().base.darken(cx),
|
||||
_ => cx.theme().accent.step(cx, ColorScaleStep::ONE),
|
||||
},
|
||||
_ => cx.theme().base.step(cx, ColorScaleStep::ELEVEN),
|
||||
ButtonVariant::Primary => cx.theme().text_muted, // TODO: use a different color?
|
||||
_ => cx.theme().text_muted,
|
||||
};
|
||||
|
||||
let border = bg;
|
||||
let underline = self.underline(window, cx);
|
||||
let shadow = false;
|
||||
|
||||
ButtonVariantStyle {
|
||||
bg,
|
||||
border,
|
||||
fg,
|
||||
underline,
|
||||
shadow,
|
||||
}
|
||||
ButtonVariantStyle { bg, fg, underline }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,201 +0,0 @@
|
||||
use gpui::{
|
||||
div, prelude::FluentBuilder as _, App, Corners, Div, Edges, ElementId, InteractiveElement,
|
||||
IntoElement, ParentElement, RenderOnce, StatefulInteractiveElement as _, Styled, Window,
|
||||
};
|
||||
use std::{cell::Cell, rc::Rc};
|
||||
|
||||
use crate::{
|
||||
button::{Button, ButtonVariant, ButtonVariants},
|
||||
Disableable, Sizable, Size,
|
||||
};
|
||||
|
||||
type OnClick = Option<Box<dyn Fn(&Vec<usize>, &mut Window, &mut App) + 'static>>;
|
||||
|
||||
/// A ButtonGroup element, to wrap multiple buttons in a group.
|
||||
#[derive(IntoElement)]
|
||||
pub struct ButtonGroup {
|
||||
pub base: Div,
|
||||
id: ElementId,
|
||||
children: Vec<Button>,
|
||||
multiple: bool,
|
||||
disabled: bool,
|
||||
|
||||
// The button props
|
||||
compact: Option<bool>,
|
||||
variant: Option<ButtonVariant>,
|
||||
size: Option<Size>,
|
||||
|
||||
on_click: OnClick,
|
||||
}
|
||||
|
||||
impl Disableable for ButtonGroup {
|
||||
fn disabled(mut self, disabled: bool) -> Self {
|
||||
self.disabled = disabled;
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
impl ButtonGroup {
|
||||
/// Creates a new ButtonGroup.
|
||||
pub fn new(id: impl Into<ElementId>) -> Self {
|
||||
Self {
|
||||
base: div(),
|
||||
children: Vec::new(),
|
||||
id: id.into(),
|
||||
variant: None,
|
||||
size: None,
|
||||
compact: None,
|
||||
multiple: false,
|
||||
disabled: false,
|
||||
on_click: None,
|
||||
}
|
||||
}
|
||||
|
||||
/// Adds a button as a child to the ButtonGroup.
|
||||
pub fn child(mut self, child: Button) -> Self {
|
||||
self.children.push(child.disabled(self.disabled));
|
||||
self
|
||||
}
|
||||
|
||||
/// With the multiple selection mode.
|
||||
pub fn multiple(mut self, multiple: bool) -> Self {
|
||||
self.multiple = multiple;
|
||||
self
|
||||
}
|
||||
|
||||
/// With the compact mode for the ButtonGroup.
|
||||
pub fn compact(mut self) -> Self {
|
||||
self.compact = Some(true);
|
||||
self
|
||||
}
|
||||
|
||||
/// Sets the on_click handler for the ButtonGroup.
|
||||
///
|
||||
/// The handler first argument is a vector of the selected button indices.
|
||||
pub fn on_click(
|
||||
mut self,
|
||||
handler: impl Fn(&Vec<usize>, &mut Window, &mut App) + 'static,
|
||||
) -> Self {
|
||||
self.on_click = Some(Box::new(handler));
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
impl Sizable for ButtonGroup {
|
||||
fn with_size(mut self, size: impl Into<Size>) -> Self {
|
||||
self.size = Some(size.into());
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
impl Styled for ButtonGroup {
|
||||
fn style(&mut self) -> &mut gpui::StyleRefinement {
|
||||
self.base.style()
|
||||
}
|
||||
}
|
||||
|
||||
impl ButtonVariants for ButtonGroup {
|
||||
fn with_variant(mut self, variant: ButtonVariant) -> Self {
|
||||
self.variant = Some(variant);
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
impl RenderOnce for ButtonGroup {
|
||||
fn render(self, _window: &mut Window, _cx: &mut App) -> impl IntoElement {
|
||||
let children_len = self.children.len();
|
||||
let mut selected_ixs: Vec<usize> = Vec::new();
|
||||
let state = Rc::new(Cell::new(None));
|
||||
|
||||
for (ix, child) in self.children.iter().enumerate() {
|
||||
if child.selected {
|
||||
selected_ixs.push(ix);
|
||||
}
|
||||
}
|
||||
|
||||
self.base
|
||||
.id(self.id)
|
||||
.flex()
|
||||
.items_center()
|
||||
.children(
|
||||
self.children
|
||||
.into_iter()
|
||||
.enumerate()
|
||||
.map(|(child_index, child)| {
|
||||
let state = Rc::clone(&state);
|
||||
|
||||
if children_len == 1 {
|
||||
child
|
||||
} else if child_index == 0 {
|
||||
// First
|
||||
child
|
||||
.border_corners(Corners {
|
||||
top_left: true,
|
||||
top_right: false,
|
||||
bottom_left: true,
|
||||
bottom_right: false,
|
||||
})
|
||||
.border_edges(Edges {
|
||||
left: true,
|
||||
top: true,
|
||||
right: true,
|
||||
bottom: true,
|
||||
})
|
||||
} else if child_index == children_len - 1 {
|
||||
// Last
|
||||
child
|
||||
.border_edges(Edges {
|
||||
left: false,
|
||||
top: true,
|
||||
right: true,
|
||||
bottom: true,
|
||||
})
|
||||
.border_corners(Corners {
|
||||
top_left: false,
|
||||
top_right: true,
|
||||
bottom_left: false,
|
||||
bottom_right: true,
|
||||
})
|
||||
} else {
|
||||
// Middle
|
||||
child
|
||||
.border_corners(Corners::all(false))
|
||||
.border_edges(Edges {
|
||||
left: false,
|
||||
top: true,
|
||||
right: true,
|
||||
bottom: true,
|
||||
})
|
||||
}
|
||||
.stop_propagation(false)
|
||||
.when_some(self.size, |this, size| this.with_size(size))
|
||||
.when_some(self.variant, |this, variant| this.with_variant(variant))
|
||||
.on_click(move |_, _, _| {
|
||||
state.set(Some(child_index));
|
||||
})
|
||||
}),
|
||||
)
|
||||
.when_some(
|
||||
self.on_click.filter(|_| !self.disabled),
|
||||
move |this, on_click| {
|
||||
this.on_click(move |_, window, cx| {
|
||||
let mut selected_ixs = selected_ixs.clone();
|
||||
if let Some(ix) = state.get() {
|
||||
if self.multiple {
|
||||
if let Some(pos) = selected_ixs.iter().position(|&i| i == ix) {
|
||||
selected_ixs.remove(pos);
|
||||
} else {
|
||||
selected_ixs.push(ix);
|
||||
}
|
||||
} else {
|
||||
selected_ixs.clear();
|
||||
selected_ixs.push(ix);
|
||||
}
|
||||
}
|
||||
|
||||
on_click(&selected_ixs, window, cx);
|
||||
})
|
||||
},
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -1,13 +1,11 @@
|
||||
use crate::{
|
||||
h_flex,
|
||||
theme::{scale::ColorScaleStep, ActiveTheme},
|
||||
v_flex, Disableable, IconName, Selectable,
|
||||
};
|
||||
use gpui::{
|
||||
div, prelude::FluentBuilder as _, relative, svg, App, ElementId, InteractiveElement,
|
||||
IntoElement, ParentElement, RenderOnce, SharedString, StatefulInteractiveElement as _,
|
||||
Styled as _, Window,
|
||||
};
|
||||
use theme::ActiveTheme;
|
||||
|
||||
use crate::{h_flex, v_flex, Disableable, IconName, Selectable};
|
||||
|
||||
type OnClick = Option<Box<dyn Fn(&bool, &mut Window, &mut App) + 'static>>;
|
||||
|
||||
@@ -68,15 +66,9 @@ impl Selectable for Checkbox {
|
||||
impl RenderOnce for Checkbox {
|
||||
fn render(self, _window: &mut Window, cx: &mut App) -> impl IntoElement {
|
||||
let (color, icon_color) = if self.disabled {
|
||||
(
|
||||
cx.theme().base.step(cx, ColorScaleStep::THREE),
|
||||
cx.theme().base.step(cx, ColorScaleStep::ELEVEN),
|
||||
)
|
||||
(cx.theme().ghost_element_disabled, cx.theme().text_muted)
|
||||
} else {
|
||||
(
|
||||
cx.theme().accent.step(cx, ColorScaleStep::NINE),
|
||||
cx.theme().accent.step(cx, ColorScaleStep::ONE),
|
||||
)
|
||||
(cx.theme().text_accent, cx.theme().surface_background)
|
||||
};
|
||||
|
||||
h_flex()
|
||||
@@ -93,7 +85,7 @@ impl RenderOnce for Checkbox {
|
||||
.size_4()
|
||||
.flex_shrink_0()
|
||||
.map(|this| match self.checked {
|
||||
false => this.bg(cx.theme().transparent),
|
||||
false => this.bg(cx.theme().ghost_element_background),
|
||||
_ => this.bg(color),
|
||||
})
|
||||
.child(
|
||||
@@ -111,22 +103,21 @@ impl RenderOnce for Checkbox {
|
||||
)
|
||||
.map(|this| {
|
||||
if let Some(label) = self.label {
|
||||
this.text_color(cx.theme().base.step(cx, ColorScaleStep::ELEVEN))
|
||||
.child(
|
||||
div()
|
||||
.w_full()
|
||||
.overflow_x_hidden()
|
||||
.text_ellipsis()
|
||||
.line_height(relative(1.))
|
||||
.child(label),
|
||||
)
|
||||
this.text_color(cx.theme().text_muted).child(
|
||||
div()
|
||||
.w_full()
|
||||
.overflow_x_hidden()
|
||||
.text_ellipsis()
|
||||
.line_height(relative(1.))
|
||||
.child(label),
|
||||
)
|
||||
} else {
|
||||
this
|
||||
}
|
||||
})
|
||||
.when(self.disabled, |this| {
|
||||
this.cursor_not_allowed()
|
||||
.text_color(cx.theme().base.step(cx, ColorScaleStep::TEN))
|
||||
.text_color(cx.theme().text_placeholder)
|
||||
})
|
||||
.when_some(
|
||||
self.on_click.filter(|_| !self.disabled),
|
||||
|
||||
@@ -1,158 +0,0 @@
|
||||
use gpui::{
|
||||
prelude::FluentBuilder, AnyElement, App, ClipboardItem, Element, ElementId, GlobalElementId,
|
||||
IntoElement, LayoutId, ParentElement, SharedString, Styled, Window,
|
||||
};
|
||||
use std::{cell::RefCell, rc::Rc, time::Duration};
|
||||
|
||||
use crate::{
|
||||
button::{Button, ButtonVariants as _},
|
||||
h_flex, IconName, Sizable as _,
|
||||
};
|
||||
|
||||
type ContentBuilder = Option<Box<dyn Fn(&mut Window, &mut App) -> AnyElement>>;
|
||||
type CopiedCallback = Option<Rc<dyn Fn(SharedString, &mut Window, &mut App)>>;
|
||||
|
||||
pub struct Clipboard {
|
||||
id: ElementId,
|
||||
value: SharedString,
|
||||
content_builder: ContentBuilder,
|
||||
copied_callback: CopiedCallback,
|
||||
}
|
||||
|
||||
impl Clipboard {
|
||||
pub fn new(id: impl Into<ElementId>) -> Self {
|
||||
Self {
|
||||
id: id.into(),
|
||||
value: "".into(),
|
||||
content_builder: None,
|
||||
copied_callback: None,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn value(mut self, value: impl Into<SharedString>) -> Self {
|
||||
self.value = value.into();
|
||||
self
|
||||
}
|
||||
|
||||
pub fn content<E, F>(mut self, builder: F) -> Self
|
||||
where
|
||||
E: IntoElement,
|
||||
F: Fn(&mut Window, &mut App) -> E + 'static,
|
||||
{
|
||||
self.content_builder = Some(Box::new(move |window, cx| {
|
||||
builder(window, cx).into_any_element()
|
||||
}));
|
||||
self
|
||||
}
|
||||
|
||||
pub fn on_copied<F>(mut self, handler: F) -> Self
|
||||
where
|
||||
F: Fn(SharedString, &mut Window, &mut App) + 'static,
|
||||
{
|
||||
self.copied_callback = Some(Rc::new(handler));
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
impl IntoElement for Clipboard {
|
||||
type Element = Self;
|
||||
|
||||
fn into_element(self) -> Self::Element {
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct ClipboardState {
|
||||
copied: Rc<RefCell<bool>>,
|
||||
}
|
||||
|
||||
impl Element for Clipboard {
|
||||
type RequestLayoutState = AnyElement;
|
||||
|
||||
type PrepaintState = ();
|
||||
|
||||
fn id(&self) -> Option<ElementId> {
|
||||
Some(self.id.clone())
|
||||
}
|
||||
|
||||
fn request_layout(
|
||||
&mut self,
|
||||
global_id: Option<&GlobalElementId>,
|
||||
window: &mut Window,
|
||||
cx: &mut App,
|
||||
) -> (LayoutId, Self::RequestLayoutState) {
|
||||
window.with_element_state::<ClipboardState, _>(global_id.unwrap(), |state, window| {
|
||||
let state = state.unwrap_or_default();
|
||||
|
||||
let content_element = self
|
||||
.content_builder
|
||||
.as_ref()
|
||||
.map(|builder| builder(window, cx).into_any_element());
|
||||
let value = self.value.clone();
|
||||
let clipboard_id = self.id.clone();
|
||||
let copied_callback = self.copied_callback.as_ref().map(|c| c.clone());
|
||||
let copied = state.copied.clone();
|
||||
let copide_value = *copied.borrow();
|
||||
|
||||
let mut element = h_flex()
|
||||
.gap_1()
|
||||
.items_center()
|
||||
.when_some(content_element, |this, element| this.child(element))
|
||||
.child(
|
||||
Button::new(clipboard_id)
|
||||
.icon(if copide_value {
|
||||
IconName::Check
|
||||
} else {
|
||||
IconName::Copy
|
||||
})
|
||||
.ghost()
|
||||
.xsmall()
|
||||
.when(!copide_value, |this| {
|
||||
this.on_click(move |_, window, cx| {
|
||||
cx.stop_propagation();
|
||||
cx.write_to_clipboard(ClipboardItem::new_string(value.to_string()));
|
||||
*copied.borrow_mut() = true;
|
||||
|
||||
let copied = copied.clone();
|
||||
cx.spawn(async move |cx| {
|
||||
cx.background_executor().timer(Duration::from_secs(2)).await;
|
||||
*copied.borrow_mut() = false;
|
||||
})
|
||||
.detach();
|
||||
|
||||
if let Some(callback) = &copied_callback {
|
||||
callback(value.clone(), window, cx);
|
||||
}
|
||||
})
|
||||
}),
|
||||
)
|
||||
.into_any_element();
|
||||
|
||||
((element.request_layout(window, cx), element), state)
|
||||
})
|
||||
}
|
||||
|
||||
fn prepaint(
|
||||
&mut self,
|
||||
_: Option<&gpui::GlobalElementId>,
|
||||
_: gpui::Bounds<gpui::Pixels>,
|
||||
element: &mut Self::RequestLayoutState,
|
||||
window: &mut Window,
|
||||
cx: &mut App,
|
||||
) {
|
||||
element.prepaint(window, cx);
|
||||
}
|
||||
|
||||
fn paint(
|
||||
&mut self,
|
||||
_: Option<&gpui::GlobalElementId>,
|
||||
_: gpui::Bounds<gpui::Pixels>,
|
||||
element: &mut Self::RequestLayoutState,
|
||||
_: &mut Self::PrepaintState,
|
||||
window: &mut Window,
|
||||
cx: &mut App,
|
||||
) {
|
||||
element.paint(window, cx)
|
||||
}
|
||||
}
|
||||
@@ -2,8 +2,7 @@ use gpui::{
|
||||
div, prelude::FluentBuilder as _, px, Axis, Div, Hsla, IntoElement, ParentElement, RenderOnce,
|
||||
SharedString, Styled,
|
||||
};
|
||||
|
||||
use crate::theme::{scale::ColorScaleStep, ActiveTheme};
|
||||
use theme::ActiveTheme;
|
||||
|
||||
/// A divider that can be either vertical or horizontal.
|
||||
#[derive(IntoElement)]
|
||||
@@ -65,9 +64,7 @@ impl RenderOnce for Divider {
|
||||
Axis::Vertical => this.w(px(2.)).h_full(),
|
||||
Axis::Horizontal => this.h(px(2.)).w_full(),
|
||||
})
|
||||
.bg(self
|
||||
.color
|
||||
.unwrap_or(cx.theme().base.step(cx, ColorScaleStep::FIVE))),
|
||||
.bg(self.color.unwrap_or(cx.theme().border_variant)),
|
||||
)
|
||||
.when_some(self.label, |this, label| {
|
||||
this.child(
|
||||
|
||||
@@ -1,16 +1,17 @@
|
||||
use std::sync::Arc;
|
||||
|
||||
use gpui::{
|
||||
div, prelude::FluentBuilder as _, px, App, AppContext, Axis, Context, Element, Entity,
|
||||
InteractiveElement as _, IntoElement, MouseMoveEvent, MouseUpEvent, ParentElement as _, Pixels,
|
||||
Point, Render, StatefulInteractiveElement, Style, Styled as _, WeakEntity, Window,
|
||||
};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::sync::Arc;
|
||||
use theme::ActiveTheme;
|
||||
|
||||
use super::{DockArea, DockItem};
|
||||
use crate::{
|
||||
dock_area::{panel::PanelView, tab_panel::TabPanel},
|
||||
resizable::{HANDLE_PADDING, HANDLE_SIZE, PANEL_MIN_SIZE},
|
||||
theme::{scale::ColorScaleStep, ActiveTheme as _},
|
||||
AxisExt as _, StyledExt,
|
||||
};
|
||||
|
||||
@@ -268,7 +269,7 @@ impl Dock {
|
||||
.child(
|
||||
div()
|
||||
.rounded_full()
|
||||
.hover(|this| this.bg(cx.theme().base.step(cx, ColorScaleStep::SIX)))
|
||||
.hover(|this| this.bg(cx.theme().border_variant))
|
||||
.when(axis.is_horizontal(), |this| this.h_full().w(HANDLE_SIZE))
|
||||
.when(axis.is_vertical(), |this| this.w_full().h(HANDLE_SIZE)),
|
||||
)
|
||||
|
||||
@@ -1,3 +1,12 @@
|
||||
use gpui::{
|
||||
div, prelude::FluentBuilder, px, rems, App, AppContext, Context, Corner, DefiniteLength,
|
||||
DismissEvent, DragMoveEvent, Empty, Entity, EventEmitter, FocusHandle, Focusable,
|
||||
InteractiveElement as _, IntoElement, ParentElement, Pixels, Render, ScrollHandle,
|
||||
SharedString, StatefulInteractiveElement, Styled, WeakEntity, Window,
|
||||
};
|
||||
use std::sync::Arc;
|
||||
use theme::ActiveTheme;
|
||||
|
||||
use super::{
|
||||
panel::PanelView, stack_panel::StackPanel, ClosePanel, DockArea, PanelEvent, PanelStyle,
|
||||
ToggleZoom,
|
||||
@@ -8,16 +17,8 @@ use crate::{
|
||||
h_flex,
|
||||
popup_menu::{PopupMenu, PopupMenuExt},
|
||||
tab::{tab_bar::TabBar, Tab},
|
||||
theme::{scale::ColorScaleStep, ActiveTheme},
|
||||
v_flex, AxisExt, IconName, Placement, Selectable, Sizable, StyledExt,
|
||||
};
|
||||
use gpui::{
|
||||
div, prelude::FluentBuilder, px, rems, App, AppContext, Context, Corner, DefiniteLength,
|
||||
DismissEvent, DragMoveEvent, Empty, Entity, EventEmitter, FocusHandle, Focusable,
|
||||
InteractiveElement as _, IntoElement, ParentElement, Pixels, Render, ScrollHandle,
|
||||
SharedString, StatefulInteractiveElement, Styled, WeakEntity, Window,
|
||||
};
|
||||
use std::sync::Arc;
|
||||
|
||||
#[derive(Clone)]
|
||||
struct TabState {
|
||||
@@ -53,11 +54,11 @@ impl Render for DragPanel {
|
||||
.justify_center()
|
||||
.overflow_hidden()
|
||||
.whitespace_nowrap()
|
||||
.rounded(px(cx.theme().radius))
|
||||
.rounded(cx.theme().radius)
|
||||
.text_xs()
|
||||
.shadow_lg()
|
||||
.bg(cx.theme().background)
|
||||
.text_color(cx.theme().accent.step(cx, ColorScaleStep::TWELVE))
|
||||
.text_color(cx.theme().text_accent)
|
||||
.child(self.panel.title(cx))
|
||||
}
|
||||
}
|
||||
@@ -639,9 +640,7 @@ impl TabPanel {
|
||||
this.rounded_l_none()
|
||||
.border_l_2()
|
||||
.border_r_0()
|
||||
.border_color(
|
||||
cx.theme().base.step(cx, ColorScaleStep::FIVE),
|
||||
)
|
||||
.border_color(cx.theme().border)
|
||||
})
|
||||
.on_drop(cx.listener(
|
||||
move |this, drag: &DragPanel, window, cx| {
|
||||
@@ -660,10 +659,10 @@ impl TabPanel {
|
||||
.h_full()
|
||||
.flex_grow()
|
||||
.min_w_16()
|
||||
.rounded(px(cx.theme().radius))
|
||||
.rounded(cx.theme().radius)
|
||||
.when(state.droppable, |this| {
|
||||
this.drag_over::<DragPanel>(|this, _, _, cx| {
|
||||
this.bg(cx.theme().base.step(cx, ColorScaleStep::TWO))
|
||||
this.bg(cx.theme().surface_background)
|
||||
})
|
||||
.on_drop(cx.listener(
|
||||
move |this, drag: &DragPanel, window, cx| {
|
||||
@@ -718,8 +717,8 @@ impl TabPanel {
|
||||
.size_full()
|
||||
.rounded_lg()
|
||||
.shadow_sm()
|
||||
.when(cx.theme().appearance.is_dark(), |this| this.shadow_lg())
|
||||
.bg(cx.theme().background)
|
||||
.when(cx.theme().mode.is_dark(), |this| this.shadow_lg())
|
||||
.bg(cx.theme().panel_background)
|
||||
.overflow_hidden()
|
||||
.child(
|
||||
active_panel
|
||||
@@ -738,8 +737,8 @@ impl TabPanel {
|
||||
div()
|
||||
.rounded_lg()
|
||||
.border_1()
|
||||
.border_color(cx.theme().accent.step(cx, ColorScaleStep::FOUR))
|
||||
.bg(cx.theme().accent.step_alpha(cx, ColorScaleStep::THREE))
|
||||
.border_color(cx.theme().element_disabled)
|
||||
.bg(cx.theme().drop_target_background)
|
||||
.size_full(),
|
||||
)
|
||||
.map(|this| match self.will_split_placement {
|
||||
|
||||
@@ -5,11 +5,11 @@ use gpui::{
|
||||
Pixels, Render, SharedString, StatefulInteractiveElement, Styled, Subscription, Task,
|
||||
WeakEntity, Window,
|
||||
};
|
||||
use theme::ActiveTheme;
|
||||
|
||||
use crate::{
|
||||
h_flex,
|
||||
list::{self, List, ListDelegate, ListItem},
|
||||
theme::{scale::ColorScaleStep, ActiveTheme},
|
||||
v_flex, Icon, IconName, Sizable, Size, StyleSized, StyledExt,
|
||||
};
|
||||
|
||||
@@ -216,7 +216,7 @@ where
|
||||
h_flex()
|
||||
.justify_center()
|
||||
.py_6()
|
||||
.text_color(cx.theme().base.step(cx, ColorScaleStep::ELEVEN))
|
||||
.text_color(cx.theme().text_muted)
|
||||
.child(Icon::new(IconName::Inbox).size(px(28.)))
|
||||
.into_any_element()
|
||||
}
|
||||
@@ -545,18 +545,15 @@ where
|
||||
.when_some(self.title_prefix.clone(), |this, prefix| this.child(prefix))
|
||||
.child(title.clone())
|
||||
} else {
|
||||
div()
|
||||
.text_color(cx.theme().accent.step(cx, ColorScaleStep::ELEVEN))
|
||||
.child(
|
||||
self.placeholder
|
||||
.clone()
|
||||
.unwrap_or_else(|| "Please select".into()),
|
||||
)
|
||||
div().text_color(cx.theme().text_accent).child(
|
||||
self.placeholder
|
||||
.clone()
|
||||
.unwrap_or_else(|| "Please select".into()),
|
||||
)
|
||||
};
|
||||
|
||||
title.when(self.disabled, |this| {
|
||||
this.cursor_not_allowed()
|
||||
.text_color(cx.theme().base.step(cx, ColorScaleStep::ELEVEN))
|
||||
this.cursor_not_allowed().text_color(cx.theme().text_muted)
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -623,9 +620,9 @@ where
|
||||
.justify_between()
|
||||
.bg(cx.theme().background)
|
||||
.border_1()
|
||||
.border_color(cx.theme().base.step(cx, ColorScaleStep::SIX))
|
||||
.rounded(px(cx.theme().radius))
|
||||
.when(cx.theme().shadow, |this| this.shadow_sm())
|
||||
.border_color(cx.theme().border)
|
||||
.rounded(cx.theme().radius)
|
||||
.shadow_sm()
|
||||
.map(|this| {
|
||||
if self.disabled {
|
||||
this.cursor_not_allowed()
|
||||
@@ -672,10 +669,8 @@ where
|
||||
Icon::new(icon)
|
||||
.xsmall()
|
||||
.text_color(match self.disabled {
|
||||
true => cx.theme().base.step(cx, ColorScaleStep::TEN),
|
||||
false => {
|
||||
cx.theme().base.step(cx, ColorScaleStep::ELEVEN)
|
||||
}
|
||||
true => cx.theme().icon_muted,
|
||||
false => cx.theme().icon,
|
||||
})
|
||||
.when(self.disabled, |this| this.cursor_not_allowed()),
|
||||
)
|
||||
@@ -706,10 +701,8 @@ where
|
||||
.mt_1p5()
|
||||
.bg(cx.theme().background)
|
||||
.border_1()
|
||||
.border_color(
|
||||
cx.theme().base.step(cx, ColorScaleStep::SEVEN),
|
||||
)
|
||||
.rounded(px(cx.theme().radius))
|
||||
.border_color(cx.theme().border_focused)
|
||||
.rounded(cx.theme().radius)
|
||||
.shadow_md()
|
||||
.on_mouse_down_out(|_, _, cx| {
|
||||
cx.dispatch_action(&Escape);
|
||||
|
||||
@@ -6,12 +6,12 @@ use gpui::{
|
||||
StatefulInteractiveElement, Styled, WeakEntity, Window,
|
||||
};
|
||||
use serde::Deserialize;
|
||||
use theme::ActiveTheme;
|
||||
|
||||
use crate::{
|
||||
button::{Button, ButtonVariants},
|
||||
input::TextInput,
|
||||
popover::{Popover, PopoverContent},
|
||||
theme::{scale::ColorScaleStep, ActiveTheme},
|
||||
Icon,
|
||||
};
|
||||
|
||||
@@ -99,11 +99,9 @@ impl RenderOnce for EmojiPicker {
|
||||
.flex()
|
||||
.items_center()
|
||||
.justify_center()
|
||||
.rounded(px(cx.theme().radius))
|
||||
.rounded(cx.theme().radius)
|
||||
.child(e.clone())
|
||||
.hover(|this| {
|
||||
this.bg(cx.theme().base.step(cx, ColorScaleStep::THREE))
|
||||
})
|
||||
.hover(|this| this.bg(cx.theme().ghost_element_hover))
|
||||
.on_click({
|
||||
let item = e.clone();
|
||||
let input = input.upgrade();
|
||||
|
||||
@@ -1,12 +1,11 @@
|
||||
use crate::{
|
||||
theme::{scale::ColorScaleStep, ActiveTheme},
|
||||
Sizable, Size,
|
||||
};
|
||||
use gpui::{
|
||||
prelude::FluentBuilder as _, svg, AnyElement, App, AppContext, Entity, Hsla, IntoElement,
|
||||
Radians, Render, RenderOnce, SharedString, StyleRefinement, Styled, Svg, Transformation,
|
||||
Window,
|
||||
};
|
||||
use theme::ActiveTheme;
|
||||
|
||||
use crate::{Sizable, Size};
|
||||
|
||||
#[derive(IntoElement, Clone)]
|
||||
pub enum IconName {
|
||||
@@ -295,9 +294,7 @@ impl Render for Icon {
|
||||
_window: &mut gpui::Window,
|
||||
cx: &mut gpui::Context<Self>,
|
||||
) -> impl IntoElement {
|
||||
let text_color = self
|
||||
.text_color
|
||||
.unwrap_or_else(|| cx.theme().base.step(cx, ColorScaleStep::ELEVEN));
|
||||
let text_color = self.text_color.unwrap_or_else(|| cx.theme().icon);
|
||||
|
||||
svg()
|
||||
.flex_none()
|
||||
|
||||
@@ -1,11 +1,12 @@
|
||||
use super::TextInput;
|
||||
use crate::theme::{scale::ColorScaleStep, ActiveTheme};
|
||||
use gpui::{
|
||||
fill, point, px, relative, size, App, Bounds, Corners, Element, ElementId, ElementInputHandler,
|
||||
Entity, GlobalElementId, IntoElement, LayoutId, MouseButton, MouseMoveEvent, PaintQuad, Path,
|
||||
Pixels, Point, Style, TextRun, UnderlineStyle, Window, WrappedLine,
|
||||
};
|
||||
use smallvec::SmallVec;
|
||||
use theme::ActiveTheme;
|
||||
|
||||
use super::TextInput;
|
||||
|
||||
const RIGHT_MARGIN: Pixels = px(5.);
|
||||
const BOTTOM_MARGIN: Pixels = px(20.);
|
||||
@@ -149,7 +150,7 @@ impl TextElement {
|
||||
),
|
||||
size(px(1.), cursor_height),
|
||||
),
|
||||
cx.theme().accent.step(cx, ColorScaleStep::TEN),
|
||||
cx.theme().element_active,
|
||||
))
|
||||
};
|
||||
}
|
||||
@@ -342,17 +343,11 @@ impl Element for TextElement {
|
||||
let mut bounds = bounds;
|
||||
|
||||
let (display_text, text_color) = if text.is_empty() {
|
||||
(
|
||||
placeholder,
|
||||
cx.theme().base.step(cx, ColorScaleStep::ELEVEN),
|
||||
)
|
||||
(placeholder, cx.theme().text_muted)
|
||||
} else if input.masked {
|
||||
(
|
||||
"*".repeat(text.chars().count()).into(),
|
||||
cx.theme().base.step(cx, ColorScaleStep::TWELVE),
|
||||
)
|
||||
("*".repeat(text.chars().count()).into(), cx.theme().text)
|
||||
} else {
|
||||
(text, cx.theme().base.step(cx, ColorScaleStep::TWELVE))
|
||||
(text, cx.theme().text)
|
||||
};
|
||||
|
||||
let run = TextRun {
|
||||
@@ -471,7 +466,7 @@ impl Element for TextElement {
|
||||
|
||||
// Paint selections
|
||||
if let Some(path) = prepaint.selection_path.take() {
|
||||
window.paint_path(path, cx.theme().accent.step(cx, ColorScaleStep::FOUR));
|
||||
window.paint_path(path, cx.theme().element_disabled);
|
||||
}
|
||||
|
||||
// Paint multi line text
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
use std::{cell::Cell, ops::Range, rc::Rc};
|
||||
|
||||
use gpui::{
|
||||
actions, div, point, prelude::FluentBuilder as _, px, AnyElement, App, AppContext, Bounds,
|
||||
ClipboardItem, Context, Entity, EntityInputHandler, EventEmitter, FocusHandle, Focusable,
|
||||
@@ -6,7 +8,7 @@ use gpui::{
|
||||
ScrollWheelEvent, SharedString, Styled as _, Subscription, UTF16Selection, Window, WrappedLine,
|
||||
};
|
||||
use smallvec::SmallVec;
|
||||
use std::{cell::Cell, ops::Range, rc::Rc};
|
||||
use theme::ActiveTheme;
|
||||
use unicode_segmentation::*;
|
||||
|
||||
use super::{blink_cursor::BlinkCursor, change::Change, element::TextElement};
|
||||
@@ -14,7 +16,6 @@ use crate::{
|
||||
history::History,
|
||||
indicator::Indicator,
|
||||
scroll::{Scrollbar, ScrollbarAxis, ScrollbarState},
|
||||
theme::{scale::ColorScaleStep, ActiveTheme},
|
||||
Sizable, Size, StyleSized, StyledExt,
|
||||
};
|
||||
|
||||
@@ -1624,9 +1625,8 @@ impl Render for TextInput {
|
||||
.cursor_text()
|
||||
.when(self.multi_line, |this| this.h_auto())
|
||||
.when(self.appearance, |this| {
|
||||
this.bg(cx.theme().base.step(cx, ColorScaleStep::THREE))
|
||||
.rounded(px(cx.theme().radius))
|
||||
.when(cx.theme().shadow, |this| this.shadow_sm())
|
||||
this.bg(cx.theme().elevated_surface_background)
|
||||
.rounded(cx.theme().radius)
|
||||
.when(focused, |this| this.outline(window, cx))
|
||||
.when(prefix.is_none(), |this| this.input_pl(self.size))
|
||||
.when(suffix.is_none(), |this| this.input_pr(self.size))
|
||||
@@ -1642,7 +1642,7 @@ impl Render for TextInput {
|
||||
.child(TextElement::new(cx.entity().clone())),
|
||||
)
|
||||
.when(self.loading, |this| {
|
||||
this.child(Indicator::new().color(cx.theme().base.step(cx, ColorScaleStep::ELEVEN)))
|
||||
this.child(Indicator::new().color(cx.theme().text_muted))
|
||||
})
|
||||
.children(suffix)
|
||||
.when(self.is_multi_line(), |this| {
|
||||
|
||||
@@ -1,8 +1,24 @@
|
||||
pub use event::InteractiveElementExt;
|
||||
pub use focusable::FocusableCycle;
|
||||
pub use icon::*;
|
||||
pub use root::{ContextModal, Root};
|
||||
pub use styled::*;
|
||||
pub use title_bar::*;
|
||||
pub use window_border::{window_border, WindowBorder};
|
||||
|
||||
pub use crate::Disableable;
|
||||
|
||||
mod event;
|
||||
mod focusable;
|
||||
mod icon;
|
||||
mod root;
|
||||
mod styled;
|
||||
mod title_bar;
|
||||
mod window_border;
|
||||
|
||||
pub mod animation;
|
||||
pub mod button;
|
||||
pub mod button_group;
|
||||
pub mod checkbox;
|
||||
pub mod clipboard;
|
||||
pub mod context_menu;
|
||||
pub mod divider;
|
||||
pub mod dock_area;
|
||||
@@ -16,34 +32,14 @@ pub mod modal;
|
||||
pub mod notification;
|
||||
pub mod popover;
|
||||
pub mod popup_menu;
|
||||
pub mod progress;
|
||||
pub mod radio;
|
||||
pub mod resizable;
|
||||
pub mod scroll;
|
||||
pub mod skeleton;
|
||||
pub mod switch;
|
||||
pub mod tab;
|
||||
pub mod text;
|
||||
pub mod theme;
|
||||
pub mod tooltip;
|
||||
|
||||
pub use crate::Disableable;
|
||||
pub use event::InteractiveElementExt;
|
||||
pub use focusable::FocusableCycle;
|
||||
pub use icon::*;
|
||||
pub use root::{ContextModal, Root};
|
||||
pub use styled::*;
|
||||
pub use title_bar::*;
|
||||
pub use window_border::{window_border, WindowBorder};
|
||||
|
||||
mod event;
|
||||
mod focusable;
|
||||
mod icon;
|
||||
mod root;
|
||||
mod styled;
|
||||
mod title_bar;
|
||||
mod window_border;
|
||||
|
||||
/// Initialize the UI module.
|
||||
///
|
||||
/// This must be called before using any of the UI components.
|
||||
|
||||
@@ -1,9 +1,5 @@
|
||||
use crate::{
|
||||
input::{InputEvent, TextInput},
|
||||
scroll::{Scrollbar, ScrollbarState},
|
||||
theme::{scale::ColorScaleStep, ActiveTheme},
|
||||
v_flex, Icon, IconName, Size,
|
||||
};
|
||||
use std::{cell::Cell, rc::Rc, time::Duration};
|
||||
|
||||
use gpui::{
|
||||
actions, div, prelude::FluentBuilder, px, uniform_list, AnyElement, App, AppContext, Context,
|
||||
Entity, FocusHandle, Focusable, InteractiveElement, IntoElement, KeyBinding, Length,
|
||||
@@ -11,7 +7,13 @@ use gpui::{
|
||||
Subscription, Task, UniformListScrollHandle, Window,
|
||||
};
|
||||
use smol::Timer;
|
||||
use std::{cell::Cell, rc::Rc, time::Duration};
|
||||
use theme::ActiveTheme;
|
||||
|
||||
use crate::{
|
||||
input::{InputEvent, TextInput},
|
||||
scroll::{Scrollbar, ScrollbarState},
|
||||
v_flex, Icon, IconName, Size,
|
||||
};
|
||||
|
||||
actions!(list, [Cancel, Confirm, SelectPrev, SelectNext]);
|
||||
|
||||
@@ -122,10 +124,7 @@ where
|
||||
let query_input = cx.new(|cx| {
|
||||
TextInput::new(window, cx)
|
||||
.appearance(false)
|
||||
.prefix(|_window, cx| {
|
||||
Icon::new(IconName::Search)
|
||||
.text_color(cx.theme().base.step(cx, ColorScaleStep::ELEVEN))
|
||||
})
|
||||
.prefix(|_window, cx| Icon::new(IconName::Search).text_color(cx.theme().text_muted))
|
||||
.placeholder("Search...")
|
||||
.cleanable()
|
||||
});
|
||||
@@ -379,9 +378,9 @@ where
|
||||
.left(px(0.))
|
||||
.right(px(0.))
|
||||
.bottom(px(0.))
|
||||
.bg(cx.theme().accent.step(cx, ColorScaleStep::SIX))
|
||||
.bg(cx.theme().element_background)
|
||||
.border_1()
|
||||
.border_color(cx.theme().accent.step(cx, ColorScaleStep::NINE)),
|
||||
.border_color(cx.theme().border_selected),
|
||||
)
|
||||
})
|
||||
})
|
||||
@@ -394,7 +393,7 @@ where
|
||||
.right(px(0.))
|
||||
.bottom(px(0.))
|
||||
.border_1()
|
||||
.border_color(cx.theme().accent.step(cx, ColorScaleStep::NINE)),
|
||||
.border_color(cx.theme().element_active),
|
||||
)
|
||||
})
|
||||
.on_mouse_down(
|
||||
@@ -471,7 +470,7 @@ where
|
||||
_ => this.py_1().px_2(),
|
||||
})
|
||||
.border_b_1()
|
||||
.border_color(cx.theme().base.step(cx, ColorScaleStep::THREE))
|
||||
.border_color(cx.theme().border)
|
||||
.child(input),
|
||||
)
|
||||
})
|
||||
|
||||
@@ -1,14 +1,12 @@
|
||||
use crate::{
|
||||
h_flex,
|
||||
theme::{scale::ColorScaleStep, ActiveTheme},
|
||||
Disableable, Icon, IconName, Selectable, Sizable as _,
|
||||
};
|
||||
use gpui::{
|
||||
div, prelude::FluentBuilder as _, AnyElement, App, ClickEvent, Div, ElementId,
|
||||
InteractiveElement, IntoElement, MouseButton, MouseMoveEvent, ParentElement, RenderOnce,
|
||||
Stateful, StatefulInteractiveElement as _, Styled, Window,
|
||||
};
|
||||
use smallvec::SmallVec;
|
||||
use theme::ActiveTheme;
|
||||
|
||||
use crate::{h_flex, Disableable, Icon, IconName, Selectable, Sizable as _};
|
||||
|
||||
type OnClick = Option<Box<dyn Fn(&ClickEvent, &mut Window, &mut App) + 'static>>;
|
||||
type OnMouseEnter = Option<Box<dyn Fn(&MouseMoveEvent, &mut Window, &mut App) + 'static>>;
|
||||
@@ -132,7 +130,7 @@ impl RenderOnce for ListItem {
|
||||
let is_active = self.selected || self.confirmed;
|
||||
|
||||
self.base
|
||||
.text_color(cx.theme().base.step(cx, ColorScaleStep::TWELVE))
|
||||
.text_color(cx.theme().text_muted)
|
||||
.relative()
|
||||
.items_center()
|
||||
.justify_between()
|
||||
@@ -147,11 +145,9 @@ impl RenderOnce for ListItem {
|
||||
this
|
||||
}
|
||||
})
|
||||
.when(is_active, |this| {
|
||||
this.bg(cx.theme().accent.step(cx, ColorScaleStep::NINE))
|
||||
})
|
||||
.when(is_active, |this| this.bg(cx.theme().element_active))
|
||||
.when(!is_active && !self.disabled, |this| {
|
||||
this.hover(|this| this.bg(cx.theme().base.step(cx, ColorScaleStep::TWO)))
|
||||
this.hover(|this| this.bg(cx.theme().surface_background))
|
||||
})
|
||||
// Mouse enter
|
||||
.when_some(self.on_mouse_enter, |this, on_mouse_enter| {
|
||||
@@ -169,16 +165,15 @@ impl RenderOnce for ListItem {
|
||||
.gap_x_1()
|
||||
.child(div().w_full().children(self.children))
|
||||
.when_some(self.check_icon, |this, icon| {
|
||||
this.child(div().w_5().items_center().justify_center().when(
|
||||
self.confirmed,
|
||||
|this| {
|
||||
this.child(
|
||||
icon.small().text_color(
|
||||
cx.theme().base.step(cx, ColorScaleStep::ELEVEN),
|
||||
),
|
||||
)
|
||||
},
|
||||
))
|
||||
this.child(
|
||||
div()
|
||||
.w_5()
|
||||
.items_center()
|
||||
.justify_center()
|
||||
.when(self.confirmed, |this| {
|
||||
this.child(icon.small().text_color(cx.theme().text_muted))
|
||||
}),
|
||||
)
|
||||
}),
|
||||
)
|
||||
.when_some(self.suffix, |this, suffix| this.child(suffix(window, cx)))
|
||||
|
||||
@@ -6,11 +6,11 @@ use gpui::{
|
||||
IntoElement, KeyBinding, MouseButton, ParentElement, Pixels, Point, RenderOnce, SharedString,
|
||||
Styled, Window,
|
||||
};
|
||||
use theme::ActiveTheme;
|
||||
|
||||
use crate::{
|
||||
animation::cubic_bezier,
|
||||
button::{Button, ButtonCustomVariant, ButtonVariants as _},
|
||||
theme::{scale::ColorScaleStep, ActiveTheme as _},
|
||||
v_flex, ContextModal, IconName, StyledExt,
|
||||
};
|
||||
|
||||
@@ -47,7 +47,7 @@ impl Modal {
|
||||
let base = v_flex()
|
||||
.bg(cx.theme().background)
|
||||
.border_1()
|
||||
.border_color(cx.theme().base.step(cx, ColorScaleStep::SIX))
|
||||
.border_color(cx.theme().border)
|
||||
.rounded_xl()
|
||||
.shadow_md();
|
||||
|
||||
@@ -168,9 +168,7 @@ impl RenderOnce for Modal {
|
||||
.occlude()
|
||||
.w(view_size.width)
|
||||
.h(view_size.height)
|
||||
.when(self.overlay, |this| {
|
||||
this.bg(cx.theme().base.step_alpha(cx, ColorScaleStep::TWO))
|
||||
})
|
||||
.when(self.overlay, |this| this.bg(cx.theme().overlay))
|
||||
.when(self.keyboard, |this| {
|
||||
this.on_mouse_down(MouseButton::Left, {
|
||||
let on_close = self.on_close.clone();
|
||||
@@ -201,7 +199,7 @@ impl RenderOnce for Modal {
|
||||
.items_center()
|
||||
.font_semibold()
|
||||
.border_b_1()
|
||||
.border_color(cx.theme().base.step(cx, ColorScaleStep::SIX))
|
||||
.border_color(cx.theme().border)
|
||||
.line_height(relative(1.))
|
||||
.child(title),
|
||||
)
|
||||
@@ -217,13 +215,10 @@ impl RenderOnce for Modal {
|
||||
.right_2()
|
||||
.custom(
|
||||
ButtonCustomVariant::new(window, cx)
|
||||
.foreground(
|
||||
cx.theme().base.step(cx, ColorScaleStep::NINE),
|
||||
)
|
||||
.color(cx.theme().transparent)
|
||||
.hover(cx.theme().transparent)
|
||||
.active(cx.theme().transparent)
|
||||
.border(cx.theme().transparent),
|
||||
.foreground(cx.theme().icon_muted)
|
||||
.color(cx.theme().ghost_element_background)
|
||||
.hover(cx.theme().ghost_element_background)
|
||||
.active(cx.theme().ghost_element_background),
|
||||
)
|
||||
.on_click(
|
||||
move |_, window, cx| {
|
||||
|
||||
@@ -1,21 +1,3 @@
|
||||
use crate::{
|
||||
animation::cubic_bezier,
|
||||
button::{Button, ButtonVariants as _},
|
||||
h_flex,
|
||||
theme::{
|
||||
colors::{blue, green, red, yellow},
|
||||
scale::ColorScaleStep,
|
||||
ActiveTheme as _,
|
||||
},
|
||||
v_flex, Icon, IconName, Sizable as _, StyledExt,
|
||||
};
|
||||
use gpui::{
|
||||
div, prelude::FluentBuilder, px, Animation, AnimationExt, App, AppContext, ClickEvent, Context,
|
||||
DismissEvent, ElementId, Entity, EventEmitter, InteractiveElement as _, IntoElement,
|
||||
ParentElement as _, Render, SharedString, StatefulInteractiveElement, Styled, Subscription,
|
||||
Window,
|
||||
};
|
||||
use smol::Timer;
|
||||
use std::{
|
||||
any::TypeId,
|
||||
collections::{HashMap, VecDeque},
|
||||
@@ -23,6 +5,21 @@ use std::{
|
||||
time::Duration,
|
||||
};
|
||||
|
||||
use gpui::{
|
||||
blue, div, green, prelude::FluentBuilder, px, red, yellow, Animation, AnimationExt, App,
|
||||
AppContext, ClickEvent, Context, DismissEvent, ElementId, Entity, EventEmitter,
|
||||
InteractiveElement as _, IntoElement, ParentElement as _, Render, SharedString,
|
||||
StatefulInteractiveElement, Styled, Subscription, Window,
|
||||
};
|
||||
use smol::Timer;
|
||||
use theme::ActiveTheme;
|
||||
|
||||
use crate::{
|
||||
animation::cubic_bezier,
|
||||
button::{Button, ButtonVariants as _},
|
||||
h_flex, v_flex, Icon, IconName, Sizable as _, StyledExt,
|
||||
};
|
||||
|
||||
pub enum NotificationType {
|
||||
Info,
|
||||
Success,
|
||||
@@ -57,7 +54,7 @@ pub struct Notification {
|
||||
///
|
||||
/// None means the notification will be added to the end of the list.
|
||||
id: NotificationId,
|
||||
type_: NotificationType,
|
||||
kind: NotificationType,
|
||||
title: Option<SharedString>,
|
||||
message: SharedString,
|
||||
icon: Option<Icon>,
|
||||
@@ -110,7 +107,7 @@ impl Notification {
|
||||
id: id.into(),
|
||||
title: None,
|
||||
message: message.into(),
|
||||
type_: NotificationType::Info,
|
||||
kind: NotificationType::Info,
|
||||
icon: None,
|
||||
autohide: true,
|
||||
on_click: None,
|
||||
@@ -169,7 +166,7 @@ impl Notification {
|
||||
|
||||
/// Set the type of the notification, default is NotificationType::Info.
|
||||
pub fn with_type(mut self, type_: NotificationType) -> Self {
|
||||
self.type_ = type_;
|
||||
self.kind = type_;
|
||||
self
|
||||
}
|
||||
|
||||
@@ -217,16 +214,13 @@ impl Render for Notification {
|
||||
let closing = self.closing;
|
||||
let icon = match self.icon.clone() {
|
||||
Some(icon) => icon,
|
||||
None => match self.type_ {
|
||||
NotificationType::Info => {
|
||||
Icon::new(IconName::Info).text_color(blue().step(cx, ColorScaleStep::NINE))
|
||||
None => match self.kind {
|
||||
NotificationType::Info => Icon::new(IconName::Info).text_color(blue()),
|
||||
NotificationType::Error => Icon::new(IconName::CloseCircle).text_color(red()),
|
||||
NotificationType::Success => Icon::new(IconName::CheckCircle).text_color(green()),
|
||||
NotificationType::Warning => {
|
||||
Icon::new(IconName::TriangleAlert).text_color(yellow())
|
||||
}
|
||||
NotificationType::Error => Icon::new(IconName::CloseCircle)
|
||||
.text_color(red().step(cx, ColorScaleStep::NINE)),
|
||||
NotificationType::Success => Icon::new(IconName::CheckCircle)
|
||||
.text_color(green().step(cx, ColorScaleStep::NINE)),
|
||||
NotificationType::Warning => Icon::new(IconName::TriangleAlert)
|
||||
.text_color(yellow().step(cx, ColorScaleStep::NINE)),
|
||||
},
|
||||
};
|
||||
|
||||
@@ -237,9 +231,9 @@ impl Render for Notification {
|
||||
.relative()
|
||||
.w_72()
|
||||
.border_1()
|
||||
.border_color(cx.theme().base.step(cx, ColorScaleStep::FIVE))
|
||||
.bg(cx.theme().background)
|
||||
.rounded(px(cx.theme().radius))
|
||||
.border_color(cx.theme().border)
|
||||
.bg(cx.theme().surface_background)
|
||||
.rounded(cx.theme().radius)
|
||||
.shadow_md()
|
||||
.p_2()
|
||||
.gap_3()
|
||||
|
||||
@@ -1,12 +1,5 @@
|
||||
use crate::{
|
||||
button::Button,
|
||||
h_flex,
|
||||
list::ListItem,
|
||||
popover::Popover,
|
||||
scroll::{Scrollbar, ScrollbarState},
|
||||
theme::{scale::ColorScaleStep, ActiveTheme},
|
||||
v_flex, Icon, IconName, Selectable, Sizable as _, StyledExt,
|
||||
};
|
||||
use std::{cell::Cell, ops::Deref, rc::Rc};
|
||||
|
||||
use gpui::{
|
||||
actions, anchored, canvas, div, prelude::FluentBuilder, px, rems, Action, AnyElement, App,
|
||||
AppContext, Bounds, Context, Corner, DismissEvent, Edges, Entity, EventEmitter, FocusHandle,
|
||||
@@ -14,7 +7,16 @@ use gpui::{
|
||||
Render, ScrollHandle, SharedString, StatefulInteractiveElement, Styled, Subscription,
|
||||
WeakEntity, Window,
|
||||
};
|
||||
use std::{cell::Cell, ops::Deref, rc::Rc};
|
||||
use theme::ActiveTheme;
|
||||
|
||||
use crate::{
|
||||
button::Button,
|
||||
h_flex,
|
||||
list::ListItem,
|
||||
popover::Popover,
|
||||
scroll::{Scrollbar, ScrollbarState},
|
||||
v_flex, Icon, IconName, Selectable, Sizable as _, StyledExt,
|
||||
};
|
||||
|
||||
actions!(menu, [Confirm, Dismiss, SelectNext, SelectPrev]);
|
||||
|
||||
@@ -456,14 +458,12 @@ impl PopupMenu {
|
||||
) -> Option<impl IntoElement> {
|
||||
if let Some(action) = action {
|
||||
if let Some(keybinding) = window.bindings_for_action(action.deref()).first() {
|
||||
let el = div()
|
||||
.text_color(cx.theme().base.step(cx, ColorScaleStep::ELEVEN))
|
||||
.children(
|
||||
keybinding
|
||||
.keystrokes()
|
||||
.iter()
|
||||
.map(|key| key_shortcut(key.clone())),
|
||||
);
|
||||
let el = div().text_color(cx.theme().text_muted).children(
|
||||
keybinding
|
||||
.keystrokes()
|
||||
.iter()
|
||||
.map(|key| key_shortcut(key.clone())),
|
||||
);
|
||||
|
||||
return Some(el);
|
||||
}
|
||||
@@ -590,10 +590,7 @@ impl Render for PopupMenu {
|
||||
.h(px(1.))
|
||||
.mx_neg_1()
|
||||
.my_0p5()
|
||||
.bg(cx
|
||||
.theme()
|
||||
.base
|
||||
.step(cx, ColorScaleStep::TWO)),
|
||||
.bg(cx.theme().border_variant),
|
||||
)
|
||||
}
|
||||
PopupMenuItem::ElementItem { render, .. } => this
|
||||
|
||||
@@ -1,62 +0,0 @@
|
||||
use crate::theme::{scale::ColorScaleStep, ActiveTheme};
|
||||
use gpui::{
|
||||
div, prelude::FluentBuilder, px, relative, App, IntoElement, ParentElement, RenderOnce, Styled,
|
||||
Window,
|
||||
};
|
||||
|
||||
/// A Progress bar element.
|
||||
#[derive(IntoElement)]
|
||||
pub struct Progress {
|
||||
value: f32,
|
||||
height: f32,
|
||||
}
|
||||
|
||||
impl Progress {
|
||||
pub fn new() -> Self {
|
||||
Progress {
|
||||
value: Default::default(),
|
||||
height: 8.,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn value(mut self, value: f32) -> Self {
|
||||
self.value = value;
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for Progress {
|
||||
fn default() -> Self {
|
||||
Self::new()
|
||||
}
|
||||
}
|
||||
|
||||
impl RenderOnce for Progress {
|
||||
fn render(self, _window: &mut Window, cx: &mut App) -> impl IntoElement {
|
||||
let rounded = px(self.height / 2.);
|
||||
let relative_w = relative(match self.value {
|
||||
v if v < 0. => 0.,
|
||||
v if v > 100. => 1.,
|
||||
v => v / 100.,
|
||||
});
|
||||
|
||||
div()
|
||||
.relative()
|
||||
.h(px(self.height))
|
||||
.rounded(rounded)
|
||||
.bg(cx.theme().accent.step(cx, ColorScaleStep::THREE))
|
||||
.child(
|
||||
div()
|
||||
.absolute()
|
||||
.top_0()
|
||||
.left_0()
|
||||
.h_full()
|
||||
.w(relative_w)
|
||||
.bg(cx.theme().accent.step(cx, ColorScaleStep::NINE))
|
||||
.map(|this| match self.value {
|
||||
v if v >= 100. => this.rounded(rounded),
|
||||
_ => this.rounded_l(rounded),
|
||||
}),
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -1,111 +0,0 @@
|
||||
use crate::{
|
||||
h_flex,
|
||||
theme::{scale::ColorScaleStep, ActiveTheme},
|
||||
IconName,
|
||||
};
|
||||
use gpui::{
|
||||
div, prelude::FluentBuilder, relative, svg, App, ElementId, InteractiveElement, IntoElement,
|
||||
ParentElement, RenderOnce, SharedString, StatefulInteractiveElement, Styled, Window,
|
||||
};
|
||||
|
||||
type OnClick = Option<Box<dyn Fn(&bool, &mut Window, &mut App) + 'static>>;
|
||||
|
||||
/// A Radio element.
|
||||
///
|
||||
/// This is not included the Radio group implementation, you can manage the group by yourself.
|
||||
#[derive(IntoElement)]
|
||||
pub struct Radio {
|
||||
id: ElementId,
|
||||
label: Option<SharedString>,
|
||||
checked: bool,
|
||||
disabled: bool,
|
||||
on_click: OnClick,
|
||||
}
|
||||
|
||||
impl Radio {
|
||||
pub fn new(id: impl Into<ElementId>) -> Self {
|
||||
Self {
|
||||
id: id.into(),
|
||||
label: None,
|
||||
checked: false,
|
||||
disabled: false,
|
||||
on_click: None,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn label(mut self, label: impl Into<SharedString>) -> Self {
|
||||
self.label = Some(label.into());
|
||||
self
|
||||
}
|
||||
|
||||
pub fn checked(mut self, checked: bool) -> Self {
|
||||
self.checked = checked;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn disabled(mut self, disabled: bool) -> Self {
|
||||
self.disabled = disabled;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn on_click(mut self, handler: impl Fn(&bool, &mut Window, &mut App) + 'static) -> Self {
|
||||
self.on_click = Some(Box::new(handler));
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
impl RenderOnce for Radio {
|
||||
fn render(self, _window: &mut Window, cx: &mut App) -> impl IntoElement {
|
||||
let color = if self.disabled {
|
||||
cx.theme().accent.step(cx, ColorScaleStep::FIVE)
|
||||
} else {
|
||||
cx.theme().accent.step(cx, ColorScaleStep::NINE)
|
||||
};
|
||||
|
||||
h_flex()
|
||||
.id(self.id)
|
||||
.gap_x_2()
|
||||
.items_center()
|
||||
.line_height(relative(1.))
|
||||
.child(
|
||||
div()
|
||||
.relative()
|
||||
.size_4()
|
||||
.flex_shrink_0()
|
||||
.rounded_full()
|
||||
.border_1()
|
||||
.border_color(color)
|
||||
.when(self.checked, |this| this.bg(color))
|
||||
.child(
|
||||
svg()
|
||||
.absolute()
|
||||
.top_px()
|
||||
.left_px()
|
||||
.size_3()
|
||||
.text_color(color)
|
||||
.map(|this| match self.checked {
|
||||
true => this.path(IconName::Check.path()),
|
||||
false => this,
|
||||
}),
|
||||
),
|
||||
)
|
||||
.when_some(self.label, |this, label| {
|
||||
this.child(
|
||||
div()
|
||||
.size_full()
|
||||
.overflow_x_hidden()
|
||||
.text_ellipsis()
|
||||
.line_height(relative(1.))
|
||||
.child(label),
|
||||
)
|
||||
})
|
||||
.when_some(
|
||||
self.on_click.filter(|_| !self.disabled),
|
||||
|this, on_click| {
|
||||
this.on_click(move |_event, window, cx| {
|
||||
on_click(&!self.checked, window, cx);
|
||||
})
|
||||
},
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -1,12 +1,11 @@
|
||||
use crate::{
|
||||
theme::{scale::ColorScaleStep, ActiveTheme as _},
|
||||
AxisExt as _,
|
||||
};
|
||||
use gpui::{
|
||||
div, prelude::FluentBuilder as _, px, App, Axis, Div, ElementId, InteractiveElement,
|
||||
IntoElement, ParentElement as _, Pixels, RenderOnce, Stateful, StatefulInteractiveElement,
|
||||
Styled as _, Window,
|
||||
};
|
||||
use theme::ActiveTheme;
|
||||
|
||||
use crate::AxisExt as _;
|
||||
|
||||
pub(crate) const HANDLE_PADDING: Pixels = px(8.);
|
||||
pub(crate) const HANDLE_SIZE: Pixels = px(2.);
|
||||
@@ -65,7 +64,7 @@ impl RenderOnce for ResizeHandle {
|
||||
.child(
|
||||
div()
|
||||
.rounded_full()
|
||||
.hover(|this| this.bg(cx.theme().base.step(cx, ColorScaleStep::SIX)))
|
||||
.hover(|this| this.bg(cx.theme().border_variant))
|
||||
.when(self.axis.is_horizontal(), |this| {
|
||||
this.h_full().w(HANDLE_SIZE)
|
||||
})
|
||||
|
||||
@@ -1,14 +1,16 @@
|
||||
use crate::{
|
||||
modal::Modal,
|
||||
notification::{Notification, NotificationList},
|
||||
theme::{scale::ColorScaleStep, ActiveTheme},
|
||||
window_border,
|
||||
};
|
||||
use std::rc::Rc;
|
||||
|
||||
use gpui::{
|
||||
div, AnyView, App, AppContext, Context, Entity, FocusHandle, InteractiveElement, IntoElement,
|
||||
ParentElement as _, Render, Styled, Window,
|
||||
};
|
||||
use std::rc::Rc;
|
||||
use theme::ActiveTheme;
|
||||
|
||||
use crate::{
|
||||
modal::Modal,
|
||||
notification::{Notification, NotificationList},
|
||||
window_border,
|
||||
};
|
||||
|
||||
/// Extension trait for [`WindowContext`] and [`ViewContext`] to add drawer functionality.
|
||||
pub trait ContextModal: Sized {
|
||||
@@ -230,8 +232,8 @@ impl Render for Root {
|
||||
.relative()
|
||||
.size_full()
|
||||
.font_family(".SystemUIFont")
|
||||
.bg(cx.theme().base.step(cx, ColorScaleStep::ONE))
|
||||
.text_color(cx.theme().base.step(cx, ColorScaleStep::TWELVE))
|
||||
.bg(cx.theme().background)
|
||||
.text_color(cx.theme().text)
|
||||
.child(self.view.clone()),
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1,38 +1,18 @@
|
||||
use gpui::{
|
||||
fill, point, px, relative, App, BorderStyle, Bounds, ContentMask, CursorStyle, Edges, Element,
|
||||
EntityId, Hitbox, Hsla, IntoElement, MouseDownEvent, MouseMoveEvent, MouseUpEvent, PaintQuad,
|
||||
Pixels, Point, Position, ScrollHandle, ScrollWheelEvent, UniformListScrollHandle, Window,
|
||||
};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::{
|
||||
cell::Cell,
|
||||
rc::Rc,
|
||||
time::{Duration, Instant},
|
||||
};
|
||||
|
||||
use crate::theme::{scale::ColorScaleStep, ActiveTheme};
|
||||
|
||||
/// Scrollbar show mode.
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, Hash, Default)]
|
||||
pub enum ScrollbarShow {
|
||||
#[default]
|
||||
Scrolling,
|
||||
Hover,
|
||||
Always,
|
||||
}
|
||||
|
||||
impl ScrollbarShow {
|
||||
fn is_hover(&self) -> bool {
|
||||
matches!(self, Self::Hover)
|
||||
}
|
||||
|
||||
fn is_always(&self) -> bool {
|
||||
matches!(self, Self::Always)
|
||||
}
|
||||
}
|
||||
use gpui::{
|
||||
fill, point, px, relative, App, BorderStyle, Bounds, ContentMask, CursorStyle, Edges, Element,
|
||||
EntityId, Hitbox, Hsla, IntoElement, MouseDownEvent, MouseMoveEvent, MouseUpEvent, PaintQuad,
|
||||
Pixels, Point, Position, ScrollHandle, ScrollWheelEvent, UniformListScrollHandle, Window,
|
||||
};
|
||||
use theme::ActiveTheme;
|
||||
|
||||
const WIDTH: Pixels = px(12.);
|
||||
const BORDER_WIDTH: Pixels = px(0.);
|
||||
pub(crate) const WIDTH: Pixels = px(12.);
|
||||
const MIN_THUMB_SIZE: f32 = 80.;
|
||||
const THUMB_RADIUS: Pixels = Pixels(4.0);
|
||||
const THUMB_INSET: Pixels = Pixels(3.);
|
||||
@@ -336,9 +316,9 @@ impl Scrollbar {
|
||||
|
||||
fn style_for_active(cx: &App) -> (Hsla, Hsla, Hsla, Pixels, Pixels) {
|
||||
(
|
||||
cx.theme().scrollbar_thumb_hover,
|
||||
cx.theme().scrollbar,
|
||||
cx.theme().base.step(cx, ColorScaleStep::SEVEN),
|
||||
cx.theme().scrollbar_thumb_hover_background,
|
||||
cx.theme().scrollbar_thumb_background,
|
||||
cx.theme().scrollbar_thumb_border,
|
||||
THUMB_INSET - px(1.),
|
||||
THUMB_RADIUS,
|
||||
)
|
||||
@@ -346,24 +326,20 @@ impl Scrollbar {
|
||||
|
||||
fn style_for_hovered_thumb(cx: &App) -> (Hsla, Hsla, Hsla, Pixels, Pixels) {
|
||||
(
|
||||
cx.theme().scrollbar_thumb_hover,
|
||||
cx.theme().scrollbar,
|
||||
cx.theme().base.step(cx, ColorScaleStep::SIX),
|
||||
cx.theme().scrollbar_thumb_hover_background,
|
||||
cx.theme().scrollbar_thumb_background,
|
||||
cx.theme().scrollbar_thumb_border,
|
||||
THUMB_INSET - px(1.),
|
||||
THUMB_RADIUS,
|
||||
)
|
||||
}
|
||||
|
||||
fn style_for_hovered_bar(cx: &App) -> (Hsla, Hsla, Hsla, Pixels, Pixels) {
|
||||
let (inset, radius) = if cx.theme().scrollbar_show.is_hover() {
|
||||
(THUMB_INSET, THUMB_RADIUS - px(1.))
|
||||
} else {
|
||||
(THUMB_INSET - px(1.), THUMB_RADIUS)
|
||||
};
|
||||
let (inset, radius) = (THUMB_INSET - px(1.), THUMB_RADIUS);
|
||||
|
||||
(
|
||||
cx.theme().scrollbar_thumb,
|
||||
cx.theme().scrollbar,
|
||||
cx.theme().scrollbar_thumb_background,
|
||||
cx.theme().scrollbar_thumb_border,
|
||||
gpui::transparent_black(),
|
||||
inset,
|
||||
radius,
|
||||
@@ -514,22 +490,12 @@ impl Element for Scrollbar {
|
||||
};
|
||||
|
||||
let state = self.state.clone();
|
||||
let is_always_to_show = cx.theme().scrollbar_show.is_always();
|
||||
let is_hover_to_show = cx.theme().scrollbar_show.is_hover();
|
||||
let is_hovered_on_bar = state.get().hovered_axis == Some(axis);
|
||||
let is_hovered_on_thumb = state.get().hovered_on_thumb == Some(axis);
|
||||
|
||||
let (thumb_bg, bar_bg, bar_border, inset, radius) =
|
||||
if state.get().dragged_axis == Some(axis) {
|
||||
Self::style_for_active(cx)
|
||||
} else if (is_hover_to_show || is_always_to_show)
|
||||
&& (is_hovered_on_bar || is_hovered_on_thumb)
|
||||
{
|
||||
if is_hovered_on_thumb {
|
||||
Self::style_for_hovered_thumb(cx)
|
||||
} else {
|
||||
Self::style_for_hovered_bar(cx)
|
||||
}
|
||||
} else {
|
||||
let mut idle_state = Self::style_for_idle(cx);
|
||||
// Delay 2s to fade out the scrollbar thumb (in 1s)
|
||||
@@ -545,11 +511,12 @@ impl Element for Scrollbar {
|
||||
};
|
||||
} else {
|
||||
if elapsed < FADE_OUT_DELAY {
|
||||
idle_state.0 = cx.theme().scrollbar_thumb;
|
||||
idle_state.0 = cx.theme().scrollbar_thumb_background;
|
||||
} else {
|
||||
// opacity = 1 - (x - 2)^10
|
||||
let opacity = 1.0 - (elapsed - FADE_OUT_DELAY).powi(10);
|
||||
idle_state.0 = cx.theme().scrollbar_thumb.opacity(opacity);
|
||||
idle_state.0 =
|
||||
cx.theme().scrollbar_thumb_background.opacity(opacity);
|
||||
};
|
||||
|
||||
window.request_animation_frame();
|
||||
@@ -626,11 +593,10 @@ impl Element for Scrollbar {
|
||||
_: &mut Self::RequestLayoutState,
|
||||
prepaint: &mut Self::PrepaintState,
|
||||
window: &mut Window,
|
||||
cx: &mut App,
|
||||
_cx: &mut App,
|
||||
) {
|
||||
let hitbox_bounds = prepaint.hitbox.bounds;
|
||||
let is_visible = self.state.get().is_scrollbar_visible();
|
||||
let is_hover_to_show = cx.theme().scrollbar_show.is_hover();
|
||||
|
||||
// Update last_scroll_time when offset is changed.
|
||||
if self.scroll_handle.offset() != self.state.get().last_scroll_offset {
|
||||
@@ -711,7 +677,7 @@ impl Element for Scrollbar {
|
||||
|
||||
let safe_range = (-scroll_area_size + container_size)..px(0.);
|
||||
|
||||
if is_hover_to_show || is_visible {
|
||||
if is_visible {
|
||||
window.on_mouse_event({
|
||||
let state = self.state.clone();
|
||||
let view_id = self.view_id;
|
||||
@@ -770,7 +736,7 @@ impl Element for Scrollbar {
|
||||
let mut notify = false;
|
||||
// When is hover to show mode or it was visible,
|
||||
// we need to update the hovered state and increase the last_scroll_time.
|
||||
let need_hover_to_update = is_hover_to_show || is_visible;
|
||||
let need_hover_to_update = is_visible;
|
||||
// Update hovered state for scrollbar
|
||||
if bounds.contains(&event.position) && need_hover_to_update {
|
||||
state.set(state.get().with_hovered(Some(axis)));
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
use crate::theme::{scale::ColorScaleStep, ActiveTheme};
|
||||
use std::time::Duration;
|
||||
|
||||
use gpui::{
|
||||
bounce, div, ease_in_out, Animation, AnimationExt, Div, IntoElement, ParentElement as _,
|
||||
RenderOnce, Styled,
|
||||
};
|
||||
use std::time::Duration;
|
||||
use theme::ActiveTheme;
|
||||
|
||||
#[derive(IntoElement)]
|
||||
pub struct Skeleton {
|
||||
@@ -34,7 +35,7 @@ impl RenderOnce for Skeleton {
|
||||
fn render(self, _window: &mut gpui::Window, cx: &mut gpui::App) -> impl IntoElement {
|
||||
div().child(
|
||||
self.base
|
||||
.bg(cx.theme().base.step(cx, ColorScaleStep::THREE))
|
||||
.bg(cx.theme().ghost_element_disabled)
|
||||
.with_animation(
|
||||
"skeleton",
|
||||
Animation::new(Duration::from_secs(2))
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
use crate::{
|
||||
scroll::{Scrollable, ScrollbarAxis},
|
||||
theme::{scale::ColorScaleStep, ActiveTheme},
|
||||
};
|
||||
use std::fmt::{self, Display, Formatter};
|
||||
|
||||
use gpui::{div, px, App, Axis, Div, Element, ElementId, EntityId, Pixels, Styled, Window};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::fmt::{self, Display, Formatter};
|
||||
use theme::ActiveTheme;
|
||||
|
||||
use crate::scroll::{Scrollable, ScrollbarAxis};
|
||||
|
||||
/// Returns a `Div` as horizontal flex layout.
|
||||
pub fn h_flex() -> Div {
|
||||
@@ -39,7 +39,7 @@ pub trait StyledExt: Styled + Sized {
|
||||
|
||||
/// Render a border with a width of 1px, color ring color
|
||||
fn outline(self, _window: &Window, cx: &App) -> Self {
|
||||
self.border_color(cx.theme().accent.step(cx, ColorScaleStep::NINE))
|
||||
self.border_color(cx.theme().ring)
|
||||
}
|
||||
|
||||
/// Wraps the element in a ScrollView.
|
||||
@@ -66,7 +66,7 @@ pub trait StyledExt: Styled + Sized {
|
||||
fn popover_style(self, cx: &mut App) -> Self {
|
||||
self.bg(cx.theme().background)
|
||||
.border_1()
|
||||
.border_color(cx.theme().base.step(cx, ColorScaleStep::SIX))
|
||||
.border_color(cx.theme().border)
|
||||
.shadow_lg()
|
||||
.rounded_lg()
|
||||
}
|
||||
|
||||
@@ -1,14 +1,13 @@
|
||||
use crate::{
|
||||
h_flex,
|
||||
theme::{scale::ColorScaleStep, ActiveTheme},
|
||||
Disableable, Side, Sizable, Size,
|
||||
};
|
||||
use std::{cell::RefCell, rc::Rc, time::Duration};
|
||||
|
||||
use gpui::{
|
||||
div, prelude::FluentBuilder as _, px, Animation, AnimationExt as _, AnyElement, App, Element,
|
||||
ElementId, GlobalElementId, InteractiveElement, IntoElement, LayoutId, ParentElement as _,
|
||||
SharedString, Styled as _, Window,
|
||||
};
|
||||
use std::{cell::RefCell, rc::Rc, time::Duration};
|
||||
use theme::ActiveTheme;
|
||||
|
||||
use crate::{h_flex, Disableable, Side, Sizable, Size};
|
||||
|
||||
type OnClick = Option<Rc<dyn Fn(&bool, &mut Window, &mut App)>>;
|
||||
|
||||
@@ -110,11 +109,8 @@ impl Element for Switch {
|
||||
let on_click = self.on_click.clone();
|
||||
|
||||
let (bg, toggle_bg) = match self.checked {
|
||||
true => (
|
||||
theme.accent.step(cx, ColorScaleStep::NINE),
|
||||
theme.background,
|
||||
),
|
||||
false => (theme.base.step(cx, ColorScaleStep::THREE), theme.background),
|
||||
true => (theme.icon_accent, theme.background),
|
||||
false => (theme.element_background, theme.background),
|
||||
};
|
||||
|
||||
let (bg, toggle_bg) = match self.disabled {
|
||||
@@ -126,10 +122,12 @@ impl Element for Switch {
|
||||
Size::XSmall | Size::Small => (px(28.), px(16.)),
|
||||
_ => (px(36.), px(20.)),
|
||||
};
|
||||
|
||||
let bar_width = match self.size {
|
||||
Size::XSmall | Size::Small => px(12.),
|
||||
_ => px(16.),
|
||||
};
|
||||
|
||||
let inset = px(2.);
|
||||
|
||||
let mut element = div()
|
||||
@@ -150,7 +148,7 @@ impl Element for Switch {
|
||||
.flex()
|
||||
.items_center()
|
||||
.border(inset)
|
||||
.border_color(theme.transparent)
|
||||
.border_color(theme.border_transparent)
|
||||
.bg(bg)
|
||||
.when(!self.disabled, |this| this.cursor_pointer())
|
||||
.child(
|
||||
|
||||
@@ -1,8 +1,10 @@
|
||||
use crate::theme::scale::ColorScaleStep;
|
||||
use crate::theme::ActiveTheme;
|
||||
use gpui::{
|
||||
div, prelude::FluentBuilder, px, AnyElement, App, Div, ElementId, InteractiveElement,
|
||||
IntoElement, ParentElement, RenderOnce, Stateful, StatefulInteractiveElement, Styled, Window,
|
||||
};
|
||||
use theme::ActiveTheme;
|
||||
|
||||
use crate::Selectable;
|
||||
use gpui::prelude::FluentBuilder;
|
||||
use gpui::*;
|
||||
|
||||
pub mod tab_bar;
|
||||
|
||||
@@ -80,25 +82,24 @@ impl RenderOnce for Tab {
|
||||
fn render(self, _window: &mut Window, cx: &mut App) -> impl IntoElement {
|
||||
let (text_color, bg_color, hover_bg_color) = match (self.selected, self.disabled) {
|
||||
(true, false) => (
|
||||
cx.theme().base.step(cx, ColorScaleStep::TWELVE),
|
||||
cx.theme().base.step(cx, ColorScaleStep::FIVE),
|
||||
cx.theme().base.step(cx, ColorScaleStep::FOUR),
|
||||
cx.theme().text,
|
||||
cx.theme().tab_active_background,
|
||||
cx.theme().tab_hover_background,
|
||||
),
|
||||
(false, false) => (
|
||||
cx.theme().base.step(cx, ColorScaleStep::ELEVEN),
|
||||
cx.theme().transparent,
|
||||
cx.theme().base.step(cx, ColorScaleStep::FOUR),
|
||||
cx.theme().text_muted,
|
||||
cx.theme().ghost_element_background,
|
||||
cx.theme().tab_hover_background,
|
||||
),
|
||||
// disabled
|
||||
(true, true) => (
|
||||
cx.theme().base.step(cx, ColorScaleStep::ELEVEN),
|
||||
cx.theme().transparent,
|
||||
cx.theme().base.step(cx, ColorScaleStep::FOUR),
|
||||
cx.theme().text_muted,
|
||||
cx.theme().ghost_element_background,
|
||||
cx.theme().tab_hover_background,
|
||||
),
|
||||
(false, true) => (
|
||||
cx.theme().base.step(cx, ColorScaleStep::ELEVEN),
|
||||
cx.theme().transparent,
|
||||
cx.theme().base.step(cx, ColorScaleStep::FOUR),
|
||||
cx.theme().text_muted,
|
||||
cx.theme().ghost_element_background,
|
||||
cx.theme().tab_hover_background,
|
||||
),
|
||||
};
|
||||
|
||||
@@ -115,7 +116,7 @@ impl RenderOnce for Tab {
|
||||
.text_ellipsis()
|
||||
.text_color(text_color)
|
||||
.bg(bg_color)
|
||||
.rounded(px(cx.theme().radius))
|
||||
.rounded(cx.theme().radius)
|
||||
.hover(|this| this.bg(hover_bg_color))
|
||||
.when_some(self.prefix, |this, prefix| {
|
||||
this.child(prefix).text_color(text_color)
|
||||
|
||||
@@ -8,8 +8,7 @@ use nostr_sdk::prelude::*;
|
||||
use once_cell::sync::Lazy;
|
||||
use regex::Regex;
|
||||
use std::{collections::HashMap, ops::Range, sync::Arc};
|
||||
|
||||
use crate::theme::{scale::ColorScaleStep, ActiveTheme};
|
||||
use theme::ActiveTheme;
|
||||
|
||||
static NOSTR_URI_REGEX: Lazy<Regex> =
|
||||
Lazy::new(|| Regex::new(r"nostr:(npub|note|nprofile|nevent|naddr)[a-zA-Z0-9]+").unwrap());
|
||||
@@ -78,7 +77,7 @@ impl RichText {
|
||||
}
|
||||
|
||||
pub fn element(&self, id: ElementId, window: &mut Window, cx: &App) -> AnyElement {
|
||||
let link_color = cx.theme().accent.step(cx, ColorScaleStep::ELEVEN);
|
||||
let link_color = cx.theme().text_accent;
|
||||
|
||||
InteractiveText::new(
|
||||
id,
|
||||
|
||||
@@ -1,166 +0,0 @@
|
||||
use crate::scroll::ScrollbarShow;
|
||||
use colors::{default_color_scales, hsl};
|
||||
use gpui::{App, Global, Hsla, SharedString, Window, WindowAppearance};
|
||||
use scale::ColorScaleSet;
|
||||
use std::ops::{Deref, DerefMut};
|
||||
|
||||
pub mod colors;
|
||||
pub mod scale;
|
||||
|
||||
pub fn init(cx: &mut App) {
|
||||
Theme::sync_system_appearance(None, cx)
|
||||
}
|
||||
|
||||
pub trait ActiveTheme {
|
||||
fn theme(&self) -> &Theme;
|
||||
}
|
||||
|
||||
impl ActiveTheme for App {
|
||||
#[inline]
|
||||
fn theme(&self) -> &Theme {
|
||||
Theme::global(self)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, Default)]
|
||||
pub struct SystemColors {
|
||||
pub background: Hsla,
|
||||
pub transparent: Hsla,
|
||||
pub scrollbar: Hsla,
|
||||
pub scrollbar_thumb: Hsla,
|
||||
pub scrollbar_thumb_hover: Hsla,
|
||||
pub window_border: Hsla,
|
||||
pub danger: Hsla,
|
||||
}
|
||||
|
||||
impl SystemColors {
|
||||
pub fn light() -> Self {
|
||||
Self {
|
||||
background: hsl(0.0, 0.0, 100.),
|
||||
transparent: Hsla::transparent_black(),
|
||||
window_border: hsl(240.0, 5.9, 78.0),
|
||||
scrollbar: hsl(0., 0., 97.).opacity(0.75),
|
||||
scrollbar_thumb: hsl(0., 0., 69.).opacity(0.9),
|
||||
scrollbar_thumb_hover: hsl(0., 0., 59.),
|
||||
danger: hsl(0.0, 84.2, 60.2),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn dark() -> Self {
|
||||
Self {
|
||||
background: hsl(0.0, 0.0, 8.0),
|
||||
transparent: Hsla::transparent_black(),
|
||||
window_border: hsl(240.0, 3.7, 28.0),
|
||||
scrollbar: hsl(240., 1., 15.).opacity(0.75),
|
||||
scrollbar_thumb: hsl(0., 0., 48.).opacity(0.9),
|
||||
scrollbar_thumb_hover: hsl(0., 0., 68.),
|
||||
danger: hsl(0.0, 62.8, 30.6),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, Default, PartialEq, PartialOrd, Eq)]
|
||||
pub enum Appearance {
|
||||
#[default]
|
||||
Light,
|
||||
Dark,
|
||||
}
|
||||
|
||||
impl Appearance {
|
||||
pub fn is_dark(&self) -> bool {
|
||||
matches!(self, Self::Dark)
|
||||
}
|
||||
}
|
||||
|
||||
pub struct Theme {
|
||||
colors: SystemColors,
|
||||
/// Base colors.
|
||||
pub base: ColorScaleSet,
|
||||
/// Accent colors.
|
||||
pub accent: ColorScaleSet,
|
||||
/// Window appearances.
|
||||
pub appearance: Appearance,
|
||||
pub font_family: SharedString,
|
||||
pub font_size: f32,
|
||||
pub radius: f32,
|
||||
pub shadow: bool,
|
||||
/// Show the scrollbar mode, default: Scrolling
|
||||
pub scrollbar_show: ScrollbarShow,
|
||||
}
|
||||
|
||||
impl Deref for Theme {
|
||||
type Target = SystemColors;
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
&self.colors
|
||||
}
|
||||
}
|
||||
|
||||
impl DerefMut for Theme {
|
||||
fn deref_mut(&mut self) -> &mut Self::Target {
|
||||
&mut self.colors
|
||||
}
|
||||
}
|
||||
|
||||
impl Global for Theme {}
|
||||
|
||||
impl Theme {
|
||||
/// Returns the global theme reference
|
||||
pub fn global(cx: &App) -> &Theme {
|
||||
cx.global::<Theme>()
|
||||
}
|
||||
|
||||
/// Returns the global theme mutable reference
|
||||
pub fn global_mut(cx: &mut App) -> &mut Theme {
|
||||
cx.global_mut::<Theme>()
|
||||
}
|
||||
|
||||
/// Sync the theme with the system appearance
|
||||
pub fn sync_system_appearance(window: Option<&mut Window>, cx: &mut App) {
|
||||
match cx.window_appearance() {
|
||||
WindowAppearance::Dark | WindowAppearance::VibrantDark => {
|
||||
Self::change(Appearance::Dark, window, cx)
|
||||
}
|
||||
WindowAppearance::Light | WindowAppearance::VibrantLight => {
|
||||
Self::change(Appearance::Light, window, cx)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn change(mode: Appearance, window: Option<&mut Window>, cx: &mut App) {
|
||||
let theme = Theme::new(mode);
|
||||
cx.set_global(theme);
|
||||
|
||||
if let Some(window) = window {
|
||||
window.refresh();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Theme {
|
||||
fn new(appearance: Appearance) -> Self {
|
||||
let color_scales = default_color_scales();
|
||||
let colors = match appearance {
|
||||
Appearance::Light => SystemColors::light(),
|
||||
Appearance::Dark => SystemColors::dark(),
|
||||
};
|
||||
|
||||
Theme {
|
||||
base: color_scales.gray,
|
||||
accent: color_scales.yellow,
|
||||
font_size: 15.0,
|
||||
font_family: if cfg!(target_os = "macos") {
|
||||
".SystemUIFont".into()
|
||||
} else if cfg!(target_os = "windows") {
|
||||
"Segoe UI".into()
|
||||
} else {
|
||||
"FreeMono".into()
|
||||
},
|
||||
radius: 5.0,
|
||||
shadow: false,
|
||||
scrollbar_show: ScrollbarShow::default(),
|
||||
appearance,
|
||||
colors,
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,15 +1,20 @@
|
||||
use crate::{h_flex, theme::ActiveTheme, Icon, IconName, InteractiveElementExt as _, Sizable as _};
|
||||
use std::rc::Rc;
|
||||
|
||||
use gpui::{
|
||||
black, div, prelude::FluentBuilder as _, px, relative, white, AnyElement, App, ClickEvent, Div,
|
||||
Element, Hsla, InteractiveElement as _, IntoElement, MouseButton, ParentElement, Pixels,
|
||||
RenderOnce, Rgba, Stateful, StatefulInteractiveElement as _, Style, Styled, Window,
|
||||
};
|
||||
use std::rc::Rc;
|
||||
use theme::ActiveTheme;
|
||||
|
||||
use crate::{h_flex, Icon, IconName, InteractiveElementExt as _, Sizable as _};
|
||||
|
||||
const HEIGHT: Pixels = px(34.);
|
||||
const TITLE_BAR_HEIGHT: Pixels = px(34.);
|
||||
|
||||
#[cfg(target_os = "macos")]
|
||||
const TITLE_BAR_LEFT_PADDING: Pixels = px(80.);
|
||||
|
||||
#[cfg(not(target_os = "macos"))]
|
||||
const TITLE_BAR_LEFT_PADDING: Pixels = px(12.);
|
||||
|
||||
@@ -105,7 +110,7 @@ impl Control {
|
||||
}
|
||||
|
||||
fn fg(&self, _window: &Window, cx: &App) -> Hsla {
|
||||
if cx.theme().appearance.is_dark() {
|
||||
if cx.theme().mode.is_dark() {
|
||||
white()
|
||||
} else {
|
||||
black()
|
||||
@@ -113,7 +118,7 @@ impl Control {
|
||||
}
|
||||
|
||||
fn hover_fg(&self, _window: &Window, cx: &App) -> Hsla {
|
||||
if self.is_close() || cx.theme().appearance.is_dark() {
|
||||
if self.is_close() || cx.theme().mode.is_dark() {
|
||||
white()
|
||||
} else {
|
||||
black()
|
||||
@@ -128,7 +133,7 @@ impl Control {
|
||||
b: 32.0 / 255.0,
|
||||
a: 1.0,
|
||||
}
|
||||
} else if cx.theme().appearance.is_dark() {
|
||||
} else if cx.theme().mode.is_dark() {
|
||||
Rgba {
|
||||
r: 0.9,
|
||||
g: 0.9,
|
||||
@@ -247,7 +252,7 @@ impl RenderOnce for TitleBar {
|
||||
.items_center()
|
||||
.justify_between()
|
||||
.h(HEIGHT)
|
||||
.bg(cx.theme().transparent)
|
||||
.bg(cx.theme().title_bar)
|
||||
.when(window.is_fullscreen(), |this| this.pl(px(12.)))
|
||||
.on_double_click(|_, window, _cx| window.zoom_window())
|
||||
.child(
|
||||
|
||||
@@ -2,8 +2,7 @@ use gpui::{
|
||||
div, relative, App, AppContext, Context, Entity, IntoElement, ParentElement, Render,
|
||||
SharedString, Styled, Window,
|
||||
};
|
||||
|
||||
use crate::theme::{scale::ColorScaleStep, ActiveTheme};
|
||||
use theme::ActiveTheme;
|
||||
|
||||
pub struct Tooltip {
|
||||
text: SharedString,
|
||||
@@ -18,18 +17,17 @@ impl Tooltip {
|
||||
impl Render for Tooltip {
|
||||
fn render(&mut self, _window: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
|
||||
div().child(
|
||||
// Wrap in a child, to ensure the left margin is applied to the tooltip
|
||||
div()
|
||||
.font_family(".SystemUIFont")
|
||||
.m_3()
|
||||
.p_2()
|
||||
.border_1()
|
||||
.border_color(cx.theme().base.step(cx, ColorScaleStep::SIX))
|
||||
.bg(cx.theme().base.step(cx, ColorScaleStep::TWO))
|
||||
.border_color(cx.theme().border)
|
||||
.bg(cx.theme().surface_background)
|
||||
.shadow_lg()
|
||||
.rounded_lg()
|
||||
.text_sm()
|
||||
.text_color(cx.theme().base.step(cx, ColorScaleStep::ELEVEN))
|
||||
.text_color(cx.theme().text_muted)
|
||||
.line_height(relative(1.25))
|
||||
.child(self.text.clone()),
|
||||
)
|
||||
|
||||
@@ -1,14 +1,16 @@
|
||||
use crate::theme::ActiveTheme;
|
||||
use gpui::{
|
||||
canvas, div, point, prelude::FluentBuilder as _, px, AnyElement, App, Bounds, CursorStyle,
|
||||
Decorations, Edges, Hsla, InteractiveElement as _, IntoElement, MouseButton, ParentElement,
|
||||
Pixels, Point, RenderOnce, ResizeEdge, Size, Styled as _, Window,
|
||||
};
|
||||
use theme::ActiveTheme;
|
||||
|
||||
pub(crate) const BORDER_SIZE: Pixels = Pixels(1.0);
|
||||
pub(crate) const BORDER_RADIUS: Pixels = Pixels(0.0);
|
||||
|
||||
#[cfg(not(target_os = "linux"))]
|
||||
pub(crate) const SHADOW_SIZE: Pixels = Pixels(0.0);
|
||||
|
||||
#[cfg(target_os = "linux")]
|
||||
pub(crate) const SHADOW_SIZE: Pixels = Pixels(12.0);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user