update ui

This commit is contained in:
2026-07-29 10:37:55 +07:00
parent 0faa8694f8
commit ef7698d248
24 changed files with 208 additions and 891 deletions

69
Cargo.lock generated
View File

@@ -1230,7 +1230,7 @@ dependencies = [
[[package]] [[package]]
name = "collections" name = "collections"
version = "0.1.0" version = "0.1.0"
source = "git+https://github.com/zed-industries/zed#8e4e5a39eed932d943cb7a3e7078885b75d1ea54" source = "git+https://github.com/zed-industries/zed#06b6160d46ae8a9074cd367ed64f742b47beca64"
dependencies = [ dependencies = [
"gpui_util", "gpui_util",
"indexmap", "indexmap",
@@ -1393,6 +1393,7 @@ dependencies = [
"gpui", "gpui",
"gpui_platform", "gpui_platform",
"gpui_web", "gpui_web",
"instant",
"log", "log",
"person", "person",
"settings", "settings",
@@ -1713,7 +1714,7 @@ dependencies = [
[[package]] [[package]]
name = "derive_refineable" name = "derive_refineable"
version = "0.1.0" version = "0.1.0"
source = "git+https://github.com/zed-industries/zed#8e4e5a39eed932d943cb7a3e7078885b75d1ea54" source = "git+https://github.com/zed-industries/zed#06b6160d46ae8a9074cd367ed64f742b47beca64"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
@@ -1825,13 +1826,13 @@ dependencies = [
[[package]] [[package]]
name = "displaydoc" name = "displaydoc"
version = "0.2.6" version = "0.2.7"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1ac70aa55017e108007fbaf5aa0f54b021c98f92ff8af59d42eda9da96e3dd4f" checksum = "c6232dd377dcc64799954cbd3a9bb882e9cdc1308ccd87b1c098f1fb2eaf82a8"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn 2.0.119", "syn 3.0.3",
] ]
[[package]] [[package]]
@@ -1906,7 +1907,7 @@ dependencies = [
"cc", "cc",
"memchr", "memchr",
"rustc_version", "rustc_version",
"toml 1.1.3+spec-1.1.0", "toml 1.1.4+spec-1.1.0",
"vswhom", "vswhom",
"winreg", "winreg",
] ]
@@ -2609,7 +2610,7 @@ dependencies = [
[[package]] [[package]]
name = "gpui" name = "gpui"
version = "0.2.2" version = "0.2.2"
source = "git+https://github.com/zed-industries/zed#8e4e5a39eed932d943cb7a3e7078885b75d1ea54" source = "git+https://github.com/zed-industries/zed#06b6160d46ae8a9074cd367ed64f742b47beca64"
dependencies = [ dependencies = [
"accesskit", "accesskit",
"anyhow", "anyhow",
@@ -2691,7 +2692,7 @@ dependencies = [
[[package]] [[package]]
name = "gpui_linux" name = "gpui_linux"
version = "0.1.0" version = "0.1.0"
source = "git+https://github.com/zed-industries/zed#8e4e5a39eed932d943cb7a3e7078885b75d1ea54" source = "git+https://github.com/zed-industries/zed#06b6160d46ae8a9074cd367ed64f742b47beca64"
dependencies = [ dependencies = [
"accesskit", "accesskit",
"accesskit_unix", "accesskit_unix",
@@ -2743,7 +2744,7 @@ dependencies = [
[[package]] [[package]]
name = "gpui_macos" name = "gpui_macos"
version = "0.1.0" version = "0.1.0"
source = "git+https://github.com/zed-industries/zed#8e4e5a39eed932d943cb7a3e7078885b75d1ea54" source = "git+https://github.com/zed-industries/zed#06b6160d46ae8a9074cd367ed64f742b47beca64"
dependencies = [ dependencies = [
"accesskit", "accesskit",
"accesskit_macos", "accesskit_macos",
@@ -2792,7 +2793,7 @@ dependencies = [
[[package]] [[package]]
name = "gpui_macros" name = "gpui_macros"
version = "0.1.0" version = "0.1.0"
source = "git+https://github.com/zed-industries/zed#8e4e5a39eed932d943cb7a3e7078885b75d1ea54" source = "git+https://github.com/zed-industries/zed#06b6160d46ae8a9074cd367ed64f742b47beca64"
dependencies = [ dependencies = [
"heck 0.5.0", "heck 0.5.0",
"proc-macro2", "proc-macro2",
@@ -2803,7 +2804,7 @@ dependencies = [
[[package]] [[package]]
name = "gpui_platform" name = "gpui_platform"
version = "0.1.0" version = "0.1.0"
source = "git+https://github.com/zed-industries/zed#8e4e5a39eed932d943cb7a3e7078885b75d1ea54" source = "git+https://github.com/zed-industries/zed#06b6160d46ae8a9074cd367ed64f742b47beca64"
dependencies = [ dependencies = [
"console_error_panic_hook", "console_error_panic_hook",
"gpui", "gpui",
@@ -2816,7 +2817,7 @@ dependencies = [
[[package]] [[package]]
name = "gpui_shared_string" name = "gpui_shared_string"
version = "0.1.0" version = "0.1.0"
source = "git+https://github.com/zed-industries/zed#8e4e5a39eed932d943cb7a3e7078885b75d1ea54" source = "git+https://github.com/zed-industries/zed#06b6160d46ae8a9074cd367ed64f742b47beca64"
dependencies = [ dependencies = [
"schemars", "schemars",
"serde", "serde",
@@ -2826,7 +2827,7 @@ dependencies = [
[[package]] [[package]]
name = "gpui_tokio" name = "gpui_tokio"
version = "0.1.0" version = "0.1.0"
source = "git+https://github.com/zed-industries/zed#8e4e5a39eed932d943cb7a3e7078885b75d1ea54" source = "git+https://github.com/zed-industries/zed#06b6160d46ae8a9074cd367ed64f742b47beca64"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"gpui", "gpui",
@@ -2837,7 +2838,7 @@ dependencies = [
[[package]] [[package]]
name = "gpui_util" name = "gpui_util"
version = "0.1.0" version = "0.1.0"
source = "git+https://github.com/zed-industries/zed#8e4e5a39eed932d943cb7a3e7078885b75d1ea54" source = "git+https://github.com/zed-industries/zed#06b6160d46ae8a9074cd367ed64f742b47beca64"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"log", "log",
@@ -2847,7 +2848,7 @@ dependencies = [
[[package]] [[package]]
name = "gpui_web" name = "gpui_web"
version = "0.1.0" version = "0.1.0"
source = "git+https://github.com/zed-industries/zed#8e4e5a39eed932d943cb7a3e7078885b75d1ea54" source = "git+https://github.com/zed-industries/zed#06b6160d46ae8a9074cd367ed64f742b47beca64"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"console_error_panic_hook", "console_error_panic_hook",
@@ -2870,7 +2871,7 @@ dependencies = [
[[package]] [[package]]
name = "gpui_wgpu" name = "gpui_wgpu"
version = "0.1.0" version = "0.1.0"
source = "git+https://github.com/zed-industries/zed#8e4e5a39eed932d943cb7a3e7078885b75d1ea54" source = "git+https://github.com/zed-industries/zed#06b6160d46ae8a9074cd367ed64f742b47beca64"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"bytemuck", "bytemuck",
@@ -2900,7 +2901,7 @@ dependencies = [
[[package]] [[package]]
name = "gpui_windows" name = "gpui_windows"
version = "0.1.0" version = "0.1.0"
source = "git+https://github.com/zed-industries/zed#8e4e5a39eed932d943cb7a3e7078885b75d1ea54" source = "git+https://github.com/zed-industries/zed#06b6160d46ae8a9074cd367ed64f742b47beca64"
dependencies = [ dependencies = [
"accesskit", "accesskit",
"accesskit_windows", "accesskit_windows",
@@ -3190,7 +3191,7 @@ dependencies = [
[[package]] [[package]]
name = "http_client" name = "http_client"
version = "0.1.0" version = "0.1.0"
source = "git+https://github.com/zed-industries/zed#8e4e5a39eed932d943cb7a3e7078885b75d1ea54" source = "git+https://github.com/zed-industries/zed#06b6160d46ae8a9074cd367ed64f742b47beca64"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"async-compression", "async-compression",
@@ -3210,7 +3211,7 @@ dependencies = [
[[package]] [[package]]
name = "http_client_tls" name = "http_client_tls"
version = "0.1.0" version = "0.1.0"
source = "git+https://github.com/zed-industries/zed#8e4e5a39eed932d943cb7a3e7078885b75d1ea54" source = "git+https://github.com/zed-industries/zed#06b6160d46ae8a9074cd367ed64f742b47beca64"
dependencies = [ dependencies = [
"rustls", "rustls",
"rustls-platform-verifier", "rustls-platform-verifier",
@@ -3502,6 +3503,9 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e0242819d153cba4b4b05a5a8f2a7e9bbf97b6055b2a002b395c96b5ff3c0222" checksum = "e0242819d153cba4b4b05a5a8f2a7e9bbf97b6055b2a002b395c96b5ff3c0222"
dependencies = [ dependencies = [
"cfg-if", "cfg-if",
"js-sys",
"wasm-bindgen",
"web-sys",
] ]
[[package]] [[package]]
@@ -4014,7 +4018,7 @@ dependencies = [
[[package]] [[package]]
name = "media" name = "media"
version = "0.1.0" version = "0.1.0"
source = "git+https://github.com/zed-industries/zed#8e4e5a39eed932d943cb7a3e7078885b75d1ea54" source = "git+https://github.com/zed-industries/zed#06b6160d46ae8a9074cd367ed64f742b47beca64"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"bindgen", "bindgen",
@@ -4917,7 +4921,7 @@ checksum = "9b4f627cb1b25917193a259e49bdad08f671f8d9708acfd5fe0a8c1455d87220"
[[package]] [[package]]
name = "perf" name = "perf"
version = "0.1.0" version = "0.1.0"
source = "git+https://github.com/zed-industries/zed#8e4e5a39eed932d943cb7a3e7078885b75d1ea54" source = "git+https://github.com/zed-industries/zed#06b6160d46ae8a9074cd367ed64f742b47beca64"
dependencies = [ dependencies = [
"collections", "collections",
"serde", "serde",
@@ -5729,7 +5733,7 @@ dependencies = [
[[package]] [[package]]
name = "refineable" name = "refineable"
version = "0.1.0" version = "0.1.0"
source = "git+https://github.com/zed-industries/zed#8e4e5a39eed932d943cb7a3e7078885b75d1ea54" source = "git+https://github.com/zed-industries/zed#06b6160d46ae8a9074cd367ed64f742b47beca64"
dependencies = [ dependencies = [
"derive_refineable", "derive_refineable",
] ]
@@ -5811,7 +5815,7 @@ dependencies = [
[[package]] [[package]]
name = "reqwest_client" name = "reqwest_client"
version = "0.1.0" version = "0.1.0"
source = "git+https://github.com/zed-industries/zed#8e4e5a39eed932d943cb7a3e7078885b75d1ea54" source = "git+https://github.com/zed-industries/zed#06b6160d46ae8a9074cd367ed64f742b47beca64"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"bytes", "bytes",
@@ -6126,7 +6130,7 @@ dependencies = [
[[package]] [[package]]
name = "scheduler" name = "scheduler"
version = "0.1.0" version = "0.1.0"
source = "git+https://github.com/zed-industries/zed#8e4e5a39eed932d943cb7a3e7078885b75d1ea54" source = "git+https://github.com/zed-industries/zed#06b6160d46ae8a9074cd367ed64f742b47beca64"
dependencies = [ dependencies = [
"async-task", "async-task",
"backtrace", "backtrace",
@@ -6750,7 +6754,7 @@ checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292"
[[package]] [[package]]
name = "sum_tree" name = "sum_tree"
version = "0.1.0" version = "0.1.0"
source = "git+https://github.com/zed-industries/zed#8e4e5a39eed932d943cb7a3e7078885b75d1ea54" source = "git+https://github.com/zed-industries/zed#06b6160d46ae8a9074cd367ed64f742b47beca64"
dependencies = [ dependencies = [
"heapless 0.9.3", "heapless 0.9.3",
"log", "log",
@@ -7286,9 +7290,9 @@ dependencies = [
[[package]] [[package]]
name = "toml" name = "toml"
version = "1.1.3+spec-1.1.0" version = "1.1.4+spec-1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "53c96ecdfa941c8fc4fcaed14f99ada8ebed502eef533015095a07e3301d4c3c" checksum = "3aace63f4bbcdfc2c965b059de67119c89c4017a70d633be6c104910f67056f5"
dependencies = [ dependencies = [
"indexmap", "indexmap",
"serde_core", "serde_core",
@@ -7751,7 +7755,7 @@ checksum = "b6c140620e7ffbb22c2dee59cafe6084a59b5ffc27a8859a5f0d494b5d52b6be"
[[package]] [[package]]
name = "util_macros" name = "util_macros"
version = "0.1.0" version = "0.1.0"
source = "git+https://github.com/zed-industries/zed#8e4e5a39eed932d943cb7a3e7078885b75d1ea54" source = "git+https://github.com/zed-industries/zed#06b6160d46ae8a9074cd367ed64f742b47beca64"
dependencies = [ dependencies = [
"perf", "perf",
"quote", "quote",
@@ -7961,8 +7965,7 @@ dependencies = [
[[package]] [[package]]
name = "wasm_thread" name = "wasm_thread"
version = "0.3.3" version = "0.3.3"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "git+https://github.com/zed-industries/wasm_thread?rev=0cf96c7708dfb97ccf3da50347e25edcf75d6937#0cf96c7708dfb97ccf3da50347e25edcf75d6937"
checksum = "b7516db7f32decdadb1c3b8deb1b7d78b9df7606c5cc2f6241737c2ab3a0258e"
dependencies = [ dependencies = [
"futures", "futures",
"js-sys", "js-sys",
@@ -9485,7 +9488,7 @@ dependencies = [
[[package]] [[package]]
name = "zlog" name = "zlog"
version = "0.1.0" version = "0.1.0"
source = "git+https://github.com/zed-industries/zed#8e4e5a39eed932d943cb7a3e7078885b75d1ea54" source = "git+https://github.com/zed-industries/zed#06b6160d46ae8a9074cd367ed64f742b47beca64"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"chrono", "chrono",
@@ -9502,7 +9505,7 @@ checksum = "29666d0abbfad1e3dc4dcf6144730dd3a3ab225bbbdac83319345b1b44ccfc1b"
[[package]] [[package]]
name = "ztracing" name = "ztracing"
version = "0.1.0" version = "0.1.0"
source = "git+https://github.com/zed-industries/zed#8e4e5a39eed932d943cb7a3e7078885b75d1ea54" source = "git+https://github.com/zed-industries/zed#06b6160d46ae8a9074cd367ed64f742b47beca64"
dependencies = [ dependencies = [
"tracing", "tracing",
"tracing-subscriber", "tracing-subscriber",
@@ -9513,7 +9516,7 @@ dependencies = [
[[package]] [[package]]
name = "ztracing_macro" name = "ztracing_macro"
version = "0.1.0" version = "0.1.0"
source = "git+https://github.com/zed-industries/zed#8e4e5a39eed932d943cb7a3e7078885b75d1ea54" source = "git+https://github.com/zed-industries/zed#06b6160d46ae8a9074cd367ed64f742b47beca64"
[[package]] [[package]]
name = "zune-core" name = "zune-core"

View File

@@ -2,7 +2,6 @@ use std::cmp::Reverse;
use std::collections::{BTreeSet, HashMap, HashSet}; use std::collections::{BTreeSet, HashMap, HashSet};
use std::sync::atomic::{AtomicBool, Ordering}; use std::sync::atomic::{AtomicBool, Ordering};
use std::sync::{Arc, LazyLock, RwLock}; use std::sync::{Arc, LazyLock, RwLock};
use instant::Duration;
use anyhow::{Error, anyhow}; use anyhow::{Error, anyhow};
use common::EventExt; use common::EventExt;
@@ -12,6 +11,7 @@ use gpui::{
App, AppContext, Context, Entity, EventEmitter, Global, SharedString, Subscription, Task, App, AppContext, Context, Entity, EventEmitter, Global, SharedString, Subscription, Task,
WeakEntity, Window, WeakEntity, Window,
}; };
use instant::Duration;
use nostr_sdk::prelude::*; use nostr_sdk::prelude::*;
use smallvec::{SmallVec, smallvec}; use smallvec::{SmallVec, smallvec};
use state::{DEVICE_GIFTWRAP, NostrRegistry, USER_GIFTWRAP, UniversalSigner}; use state::{DEVICE_GIFTWRAP, NostrRegistry, USER_GIFTWRAP, UniversalSigner};
@@ -399,7 +399,7 @@ impl ChatRegistry {
} }
let filter = Filter::new().kind(Kind::GiftWrap).pubkey(public_key); let filter = Filter::new().kind(Kind::GiftWrap).pubkey(public_key);
let id = SubscriptionId::new(format!("{}-msg", public_key.to_hex())); let id = SubscriptionId::new(USER_GIFTWRAP);
let target: HashMap<RelayUrl, Filter> = relays let target: HashMap<RelayUrl, Filter> = relays
.into_iter() .into_iter()

View File

@@ -1474,12 +1474,7 @@ impl Render for ChatPanel {
.gap_2() .gap_2()
.border_b_1() .border_b_1()
.border_color(cx.theme().border) .border_color(cx.theme().border)
.child( .child(Input::new(&self.subject_input).text_sm().small())
Input::new(&self.subject_input)
.text_sm()
.small()
.bordered(false),
)
.child( .child(
Button::new("change") Button::new("change")
.icon(IconName::CheckCircle) .icon(IconName::CheckCircle)

View File

@@ -39,14 +39,15 @@ pub const NOSTR_CONNECT_RELAY: &str = "wss://relay.nip46.com";
pub const WOT_RELAYS: [&str; 1] = ["wss://relay.vertexlab.io"]; pub const WOT_RELAYS: [&str; 1] = ["wss://relay.vertexlab.io"];
/// Default search relays /// Default search relays
pub const INDEXER_RELAYS: [&str; 1] = ["wss://indexer.coracle.social"]; pub const INDEXER_RELAYS: [&str; 2] = ["wss://indexer.coracle.social", "wss://user.kindpag.es"];
/// Default search relays /// Default search relays
pub const SEARCH_RELAYS: [&str; 2] = ["wss://antiprimal.net", "wss://search.nos.today"]; pub const SEARCH_RELAYS: [&str; 2] = ["wss://antiprimal.net", "wss://search.nos.today"];
/// Default bootstrap relays /// Default bootstrap relays
pub const BOOTSTRAP_RELAYS: [&str; 3] = [ pub const BOOTSTRAP_RELAYS: [&str; 4] = [
"wss://relay.ditto.pub", "wss://relay.ditto.pub",
"wss://relay.primal.net", "wss://relay.primal.net",
"wss://user.kindpag.es", "wss://relay.nostr.net",
"wss://profiles.nostr1.com",
]; ];

View File

@@ -112,7 +112,7 @@ impl ThemeColors {
elevated_surface_background: neutral().light().step_3(), elevated_surface_background: neutral().light().step_3(),
panel_background: neutral().light().step_1(), panel_background: neutral().light().step_1(),
overlay: neutral().light_alpha().step_3(), overlay: neutral().light_alpha().step_3(),
title_bar: neutral().light().step_3(), title_bar: neutral().light().step_2(),
title_bar_inactive: neutral().light().step_1(), title_bar_inactive: neutral().light().step_1(),
window_border: hsl(240.0, 5.9, 78.0), window_border: hsl(240.0, 5.9, 78.0),
@@ -198,7 +198,7 @@ impl ThemeColors {
elevated_surface_background: neutral().dark().step_3(), elevated_surface_background: neutral().dark().step_3(),
panel_background: neutral().dark().step_1(), panel_background: neutral().dark().step_1(),
overlay: neutral().dark_alpha().step_3(), overlay: neutral().dark_alpha().step_3(),
title_bar: neutral().dark().step_3(), title_bar: neutral().dark().step_2(),
title_bar_inactive: neutral().dark().step_1(), title_bar_inactive: neutral().dark().step_1(),
window_border: hsl(240.0, 3.7, 28.0), window_border: hsl(240.0, 3.7, 28.0),

View File

@@ -34,7 +34,7 @@ pub const CLIENT_SIDE_DECORATION_BORDER: Pixels = px(1.0);
pub const TITLEBAR_HEIGHT: Pixels = px(36.0); pub const TITLEBAR_HEIGHT: Pixels = px(36.0);
/// Defines workspace tabbar height /// Defines workspace tabbar height
pub const TABBAR_HEIGHT: Pixels = px(28.0); pub const TABBAR_HEIGHT: Pixels = px(36.0);
/// Defines default sidebar width /// Defines default sidebar width
pub const SIDEBAR_WIDTH: Pixels = px(240.); pub const SIDEBAR_WIDTH: Pixels = px(240.);

View File

@@ -2,11 +2,10 @@ use std::sync::Arc;
use gpui::prelude::FluentBuilder; use gpui::prelude::FluentBuilder;
use gpui::{ use gpui::{
AnyElement, AnyView, App, AppContext, Axis, Bounds, Context, Decorations, Edges, Entity, AnyElement, AnyView, App, AppContext, Axis, Bounds, Context, Edges, Entity, EntityId,
EntityId, EventEmitter, Focusable, InteractiveElement as _, IntoElement, ParentElement as _, EventEmitter, Focusable, InteractiveElement as _, IntoElement, ParentElement as _, Pixels,
Pixels, Render, SharedString, Styled, Subscription, WeakEntity, Window, actions, div, px, Render, SharedString, Styled, Subscription, WeakEntity, Window, actions, div, px,
}; };
use theme::CLIENT_SIDE_DECORATION_ROUNDING;
use crate::ElementExt; use crate::ElementExt;
@@ -110,15 +109,8 @@ impl DockItem {
window: &mut Window, window: &mut Window,
cx: &mut App, cx: &mut App,
) -> Self { ) -> Self {
let mut items = items;
let stack_panel = cx.new(|cx| { let stack_panel = cx.new(|cx| {
let mut stack_panel = StackPanel::new(axis, window, cx); let mut stack_panel = StackPanel::new(axis, window, cx);
for (i, item) in items.iter_mut().enumerate() {
let view = item.view();
let size = sizes.get(i).copied().flatten();
stack_panel.add_panel(view.clone(), size, dock_area.clone(), window, cx)
}
for (i, item) in items.iter().enumerate() { for (i, item) in items.iter().enumerate() {
let view = item.view(); let view = item.view();
@@ -745,34 +737,22 @@ impl EventEmitter<DockEvent> for DockArea {}
impl Render for DockArea { impl Render for DockArea {
fn render(&mut self, window: &mut Window, cx: &mut Context<Self>) -> impl IntoElement { fn render(&mut self, window: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
let view = cx.entity().clone(); let view = cx.entity().clone();
let decorations = window.window_decorations();
div() div()
.id("dock-area") .id("dock-area")
.relative() .relative()
.size_full() .size_full()
.overflow_hidden()
.on_prepaint(move |bounds, _, cx| view.update(cx, |r, _| r.bounds = bounds)) .on_prepaint(move |bounds, _, cx| view.update(cx, |r, _| r.bounds = bounds))
.map(|this| { .map(|this| {
if let Some(zoom_view) = self.zoom_view.clone() { if let Some(zoom_view) = self.zoom_view.clone() {
this.map(|this| match decorations { this.child(zoom_view)
Decorations::Server => this,
Decorations::Client { tiling } => this
.when(!(tiling.top || tiling.right), |div| {
div.rounded_br(CLIENT_SIDE_DECORATION_ROUNDING)
})
.when(!(tiling.top || tiling.left), |div| {
div.rounded_bl(CLIENT_SIDE_DECORATION_ROUNDING)
}),
})
.child(zoom_view)
} else { } else {
// render dock // render dock
this.child( this.child(
div() div()
.flex() .flex()
.flex_row() .flex_row()
.h_full() .size_full()
// Left dock // Left dock
.when_some(self.left_dock.clone(), |this, dock| { .when_some(self.left_dock.clone(), |this, dock| {
this.child(div().flex().flex_none().child(dock)) this.child(div().flex().flex_none().child(dock))
@@ -783,14 +763,8 @@ impl Render for DockArea {
.flex() .flex()
.flex_1() .flex_1()
.flex_col() .flex_col()
.overflow_hidden()
// Top center // Top center
.child( .child(div().flex_1().child(self.render_items(window, cx)))
div()
.flex_1()
.overflow_hidden()
.child(self.render_items(window, cx)),
)
// Bottom Dock // Bottom Dock
.when_some(self.bottom_dock.clone(), |this, dock| { .when_some(self.bottom_dock.clone(), |this, dock| {
this.child(dock) this.child(dock)

View File

@@ -1,13 +1,12 @@
use std::sync::Arc; use std::sync::Arc;
use gpui::prelude::FluentBuilder;
use gpui::{ use gpui::{
App, AppContext, Axis, Context, DismissEvent, Entity, EventEmitter, FocusHandle, Focusable, App, AppContext, Axis, Context, DismissEvent, Entity, EventEmitter, FocusHandle, Focusable,
IntoElement, ParentElement, Pixels, Render, SharedString, Styled, Subscription, WeakEntity, IntoElement, ParentElement, Pixels, Render, SharedString, Styled, Subscription, WeakEntity,
Window, Window,
}; };
use smallvec::SmallVec; use smallvec::SmallVec;
use theme::{ActiveTheme, AxisExt as _, CLIENT_SIDE_DECORATION_ROUNDING, Placement}; use theme::{AxisExt as _, Placement};
use super::{DockArea, PanelEvent}; use super::{DockArea, PanelEvent};
use crate::dock::panel::{Panel, PanelView}; use crate::dock::panel::{Panel, PanelView};
@@ -369,26 +368,20 @@ impl Focusable for StackPanel {
} }
impl EventEmitter<PanelEvent> for StackPanel {} impl EventEmitter<PanelEvent> for StackPanel {}
impl EventEmitter<DismissEvent> for StackPanel {} impl EventEmitter<DismissEvent> for StackPanel {}
impl Render for StackPanel { impl Render for StackPanel {
fn render(&mut self, _window: &mut Window, cx: &mut Context<Self>) -> impl IntoElement { fn render(&mut self, _window: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
h_flex() h_flex().size_full().overflow_hidden().child(
.size_full() ResizablePanelGroup::new("stack-panel-group")
.overflow_hidden() .with_state(&self.state)
.bg(cx.theme().panel_background) .axis(self.axis)
.when(cx.theme().platform.is_linux(), |this| { .children(self.panels.clone().into_iter().map(|panel| {
this.rounded_br(CLIENT_SIDE_DECORATION_ROUNDING) resizable_panel()
}) .child(panel.view())
.child( .visible(panel.visible(cx))
ResizablePanelGroup::new("stack-panel-group") })),
.with_state(&self.state) )
.axis(self.axis)
.children(self.panels.clone().into_iter().map(|panel| {
resizable_panel()
.child(panel.view())
.visible(panel.visible(cx))
})),
)
} }
} }

View File

@@ -7,7 +7,7 @@ use gpui::{
ParentElement, Pixels, Render, ScrollHandle, SharedString, StatefulInteractiveElement, Styled, ParentElement, Pixels, Render, ScrollHandle, SharedString, StatefulInteractiveElement, Styled,
WeakEntity, Window, div, px, rems, WeakEntity, Window, div, px, rems,
}; };
use theme::{ActiveTheme, AxisExt, CLIENT_SIDE_DECORATION_ROUNDING, Placement, TABBAR_HEIGHT}; use theme::{ActiveTheme, AxisExt, Placement, TABBAR_HEIGHT};
use crate::button::{Button, ButtonVariants as _}; use crate::button::{Button, ButtonVariants as _};
use crate::dock::dock::DockPlacement; use crate::dock::dock::DockPlacement;
@@ -567,6 +567,7 @@ impl TabPanel {
let left_dock_button = self.render_dock_toggle_button(DockPlacement::Left, window, cx); let left_dock_button = self.render_dock_toggle_button(DockPlacement::Left, window, cx);
let bottom_dock_button = self.render_dock_toggle_button(DockPlacement::Bottom, window, cx); let bottom_dock_button = self.render_dock_toggle_button(DockPlacement::Bottom, window, cx);
let right_dock_button = self.render_dock_toggle_button(DockPlacement::Right, window, cx); let right_dock_button = self.render_dock_toggle_button(DockPlacement::Right, window, cx);
let has_extend_dock_button = left_dock_button.is_some() || bottom_dock_button.is_some(); let has_extend_dock_button = left_dock_button.is_some() || bottom_dock_button.is_some();
let tabs_count = self.panels.len(); let tabs_count = self.panels.len();
let is_bottom_dock = bottom_dock_button.is_some(); let is_bottom_dock = bottom_dock_button.is_some();
@@ -586,6 +587,7 @@ impl TabPanel {
.py_2() .py_2()
.pl_3() .pl_3()
.pr_2() .pr_2()
.rounded_t(cx.theme().radius_lg)
.bg(cx.theme().panel_background) .bg(cx.theme().panel_background)
.when(left_dock_button.is_some(), |this| this.pl_2()) .when(left_dock_button.is_some(), |this| this.pl_2())
.when(right_dock_button.is_some(), |this| this.pr_2()) .when(right_dock_button.is_some(), |this| this.pr_2())
@@ -641,17 +643,14 @@ impl TabPanel {
TabBar::new("tab-bar") TabBar::new("tab-bar")
.track_scroll(&self.tab_bar_scroll_handle) .track_scroll(&self.tab_bar_scroll_handle)
.h(TABBAR_HEIGHT) .h(TABBAR_HEIGHT)
.bg(cx.theme().panel_background)
.rounded_t(cx.theme().radius_lg)
.when(has_extend_dock_button, |this| { .when(has_extend_dock_button, |this| {
this.prefix( this.prefix(
h_flex() h_flex()
.items_center() .items_center()
.top_0() .top_0()
.right(-px(1.)) .right(-px(1.))
.border_r_1()
.border_b_1()
.h_full()
.border_color(cx.theme().border)
.bg(cx.theme().tab_background)
.pl_0p5() .pl_0p5()
.pr_1() .pr_1()
.children(left_dock_button) .children(left_dock_button)
@@ -780,12 +779,8 @@ impl TabPanel {
.top_0() .top_0()
.right_0() .right_0()
.h_full() .h_full()
.border_l_1()
.border_b_1()
.px_0p5() .px_0p5()
.gap_1() .gap_1()
.border_color(cx.theme().border)
.bg(cx.theme().tab_background)
.child(self.render_toolbar(state, window, cx)) .child(self.render_toolbar(state, window, cx))
.when_some(right_dock_button, |this, btn| this.child(btn)), .when_some(right_dock_button, |this, btn| this.child(btn)),
) )
@@ -815,10 +810,8 @@ impl TabPanel {
.child( .child(
div() div()
.size_full() .size_full()
.rounded_b(cx.theme().radius_lg)
.bg(cx.theme().panel_background) .bg(cx.theme().panel_background)
.when(cx.theme().platform.is_linux(), |this| {
this.rounded_b(CLIENT_SIDE_DECORATION_ROUNDING)
})
.overflow_hidden() .overflow_hidden()
.child( .child(
active_panel active_panel
@@ -1140,17 +1133,24 @@ impl Render for TabPanel {
state.closable = false; state.closable = false;
} }
v_flex() div()
.when(!self.collapsed, |this| { .when(!self.collapsed, |this| {
this.on_action(cx.listener(Self::on_action_toggle_zoom)) this.on_action(cx.listener(Self::on_action_toggle_zoom))
.on_action(cx.listener(Self::on_action_close_panel)) .on_action(cx.listener(Self::on_action_close_panel))
}) })
.id("tab-panel") .id("tab-panel")
.tab_group()
.track_focus(&focus_handle) .track_focus(&focus_handle)
.size_full() .size_full()
.p_1()
.overflow_hidden() .overflow_hidden()
.child(self.render_title_bar(&state, window, cx)) .child(
.child(self.render_active_panel(&state, window, cx)) v_flex()
.rounded(cx.theme().radius_lg)
.when(cx.theme().shadow, |this| this.shadow_xs())
.size_full()
.tab_group()
.child(self.render_title_bar(&state, window, cx))
.child(self.render_active_panel(&state, window, cx)),
)
} }
} }

View File

@@ -35,8 +35,6 @@ pub struct Input {
cleanable: bool, cleanable: bool,
mask_toggle: bool, mask_toggle: bool,
disabled: bool, disabled: bool,
bordered: bool,
focus_bordered: bool,
tab_index: isize, tab_index: isize,
selected: bool, selected: bool,
} }
@@ -73,8 +71,6 @@ impl Input {
cleanable: false, cleanable: false,
mask_toggle: false, mask_toggle: false,
disabled: false, disabled: false,
bordered: true,
focus_bordered: true,
tab_index: 0, tab_index: 0,
selected: false, selected: false,
} }
@@ -108,18 +104,6 @@ impl Input {
self self
} }
/// Set the bordered for the input, default: true
pub fn bordered(mut self, bordered: bool) -> Self {
self.bordered = bordered;
self
}
/// Set focus border for the input, default is true.
pub fn focus_bordered(mut self, bordered: bool) -> Self {
self.focus_bordered = bordered;
self
}
/// Set whether to show the clear button when the input field is not empty, default is false. /// Set whether to show the clear button when the input field is not empty, default is false.
pub fn cleanable(mut self, cleanable: bool) -> Self { pub fn cleanable(mut self, cleanable: bool) -> Self {
self.cleanable = cleanable; self.cleanable = cleanable;
@@ -332,11 +316,6 @@ impl RenderOnce for Input {
this.bg(bg) this.bg(bg)
.when(self.disabled, |this| this.opacity(0.5)) .when(self.disabled, |this| this.opacity(0.5))
.rounded(cx.theme().radius) .rounded(cx.theme().radius)
.when(self.bordered, |this| {
this.border_color(cx.theme().border)
.border_1()
.when(cx.theme().shadow, |this| this.shadow_xs())
})
}) })
.items_center() .items_center()
.gap(gap_x) .gap(gap_x)

View File

@@ -117,7 +117,7 @@ impl<T: 'static, E: 'static + Render> Element for ResizeHandle<T, E> {
cx.theme().border cx.theme().border
}; };
let mut el = div() let mut ele = div()
.id(self.id.clone()) .id(self.id.clone())
.occlude() .occlude()
.absolute() .absolute()
@@ -160,16 +160,15 @@ impl<T: 'static, E: 'static + Render> Element for ResizeHandle<T, E> {
}) })
.child( .child(
div() div()
.bg(bg_color)
.group_hover("handle", |this| this.bg(bg_color)) .group_hover("handle", |this| this.bg(bg_color))
.when(axis.is_horizontal(), |this| this.h_full().w(HANDLE_SIZE)) .when(axis.is_horizontal(), |this| this.h_full().w(HANDLE_SIZE))
.when(axis.is_vertical(), |this| this.w_full().h(HANDLE_SIZE)), .when(axis.is_vertical(), |this| this.w_full().h(HANDLE_SIZE)),
) )
.into_any_element(); .into_any_element();
let layout_id = el.request_layout(window, cx); let layout_id = ele.request_layout(window, cx);
((layout_id, el), state) ((layout_id, ele), state)
}) })
} }

View File

@@ -2,396 +2,15 @@ use std::rc::Rc;
use gpui::prelude::FluentBuilder as _; use gpui::prelude::FluentBuilder as _;
use gpui::{ use gpui::{
AnyElement, App, ClickEvent, Div, Edges, Hsla, InteractiveElement, IntoElement, MouseButton, AnyElement, App, ClickEvent, Div, InteractiveElement, IntoElement, MouseButton, ParentElement,
ParentElement, Pixels, RenderOnce, SharedString, StatefulInteractiveElement, Styled, Window, RenderOnce, SharedString, StatefulInteractiveElement, Styled, Window, div, px, relative,
div, px, relative,
}; };
use theme::ActiveTheme; use theme::{ActiveTheme, TABBAR_HEIGHT};
use crate::{Icon, IconName, Selectable, Sizable, Size, StyledExt, h_flex}; use crate::{Icon, IconName, Selectable, h_flex};
pub mod tab_bar; pub mod tab_bar;
/// Tab variants.
#[derive(Debug, Clone, Default, Copy, PartialEq, Eq, Hash)]
pub enum TabVariant {
#[default]
Tab,
Outline,
Pill,
Segmented,
Underline,
}
impl TabVariant {
fn height(&self, size: Size) -> Pixels {
match size {
Size::XSmall => match self {
TabVariant::Underline => px(26.),
_ => px(20.),
},
Size::Small => match self {
TabVariant::Underline => px(30.),
_ => px(24.),
},
Size::Large => match self {
TabVariant::Underline => px(44.),
_ => px(36.),
},
_ => match self {
TabVariant::Underline => px(36.),
_ => px(32.),
},
}
}
fn inner_height(&self, size: Size) -> Pixels {
match size {
Size::XSmall => match self {
TabVariant::Tab | TabVariant::Outline | TabVariant::Pill => px(18.),
TabVariant::Segmented => px(16.),
TabVariant::Underline => px(20.),
},
Size::Small => match self {
TabVariant::Tab | TabVariant::Outline | TabVariant::Pill => px(22.),
TabVariant::Segmented => px(18.),
TabVariant::Underline => px(22.),
},
Size::Large => match self {
TabVariant::Tab | TabVariant::Outline | TabVariant::Pill => px(36.),
TabVariant::Segmented => px(28.),
TabVariant::Underline => px(32.),
},
_ => match self {
TabVariant::Tab => px(30.),
TabVariant::Outline | TabVariant::Pill => px(26.),
TabVariant::Segmented => px(24.),
TabVariant::Underline => px(26.),
},
}
}
/// Default px(12) to match panel px_3, See [`crate::dock::TabPanel`]
fn inner_paddings(&self, size: Size) -> Edges<Pixels> {
let mut padding_x = match size {
Size::XSmall => px(8.),
Size::Small => px(10.),
Size::Large => px(16.),
_ => px(12.),
};
if matches!(self, TabVariant::Underline) {
padding_x = px(0.);
}
Edges {
left: padding_x,
right: padding_x,
..Default::default()
}
}
fn inner_margins(&self, size: Size) -> Edges<Pixels> {
match size {
Size::XSmall => match self {
TabVariant::Underline => Edges {
top: px(1.),
bottom: px(2.),
..Default::default()
},
_ => Edges::all(px(0.)),
},
Size::Small => match self {
TabVariant::Underline => Edges {
top: px(2.),
bottom: px(3.),
..Default::default()
},
_ => Edges::all(px(0.)),
},
Size::Large => match self {
TabVariant::Underline => Edges {
top: px(5.),
bottom: px(6.),
..Default::default()
},
_ => Edges::all(px(0.)),
},
_ => match self {
TabVariant::Underline => Edges {
top: px(3.),
bottom: px(4.),
..Default::default()
},
_ => Edges::all(px(0.)),
},
}
}
fn normal(&self, cx: &App) -> TabStyle {
match self {
TabVariant::Tab => TabStyle {
fg: cx.theme().tab_foreground,
bg: gpui::transparent_black(),
borders: Edges {
left: px(1.),
right: px(1.),
..Default::default()
},
border_color: gpui::transparent_black(),
..Default::default()
},
TabVariant::Outline => TabStyle {
fg: cx.theme().tab_foreground,
bg: gpui::transparent_black(),
borders: Edges::all(px(1.)),
border_color: cx.theme().border,
..Default::default()
},
TabVariant::Pill => TabStyle {
fg: cx.theme().text,
bg: gpui::transparent_black(),
..Default::default()
},
TabVariant::Segmented => TabStyle {
fg: cx.theme().tab_foreground,
bg: gpui::transparent_black(),
..Default::default()
},
TabVariant::Underline => TabStyle {
fg: cx.theme().tab_foreground,
bg: gpui::transparent_black(),
inner_bg: gpui::transparent_black(),
borders: Edges {
bottom: px(2.),
..Default::default()
},
border_color: gpui::transparent_black(),
..Default::default()
},
}
}
fn hovered(&self, selected: bool, cx: &App) -> TabStyle {
match self {
TabVariant::Tab => TabStyle {
fg: cx.theme().tab_foreground,
bg: gpui::transparent_black(),
borders: Edges {
left: px(1.),
right: px(1.),
..Default::default()
},
border_color: gpui::transparent_black(),
..Default::default()
},
TabVariant::Outline => TabStyle {
fg: cx.theme().secondary_foreground,
bg: cx.theme().secondary_hover,
borders: Edges::all(px(1.)),
border_color: cx.theme().border,
..Default::default()
},
TabVariant::Pill => TabStyle {
fg: cx.theme().secondary_foreground,
bg: cx.theme().secondary_background,
..Default::default()
},
TabVariant::Segmented => TabStyle {
fg: cx.theme().tab_foreground,
bg: gpui::transparent_black(),
inner_bg: if selected {
cx.theme().background
} else {
gpui::transparent_black()
},
..Default::default()
},
TabVariant::Underline => TabStyle {
fg: cx.theme().tab_foreground,
bg: gpui::transparent_black(),
inner_bg: gpui::transparent_black(),
borders: Edges {
bottom: px(2.),
..Default::default()
},
border_color: gpui::transparent_black(),
..Default::default()
},
}
}
fn selected(&self, cx: &App) -> TabStyle {
match self {
TabVariant::Tab => TabStyle {
fg: cx.theme().tab_active_foreground,
bg: cx.theme().tab_active_background,
borders: Edges {
left: px(1.),
right: px(1.),
..Default::default()
},
border_color: cx.theme().border,
..Default::default()
},
TabVariant::Outline => TabStyle {
fg: cx.theme().text_accent,
bg: gpui::transparent_black(),
borders: Edges::all(px(1.)),
border_color: cx.theme().element_active,
..Default::default()
},
TabVariant::Pill => TabStyle {
fg: cx.theme().element_foreground,
bg: cx.theme().element_background,
..Default::default()
},
TabVariant::Segmented => TabStyle {
fg: cx.theme().tab_active_foreground,
bg: gpui::transparent_black(),
inner_bg: cx.theme().background,
shadow: true,
..Default::default()
},
TabVariant::Underline => TabStyle {
fg: cx.theme().tab_active_foreground,
bg: gpui::transparent_black(),
borders: Edges {
bottom: px(2.),
..Default::default()
},
border_color: cx.theme().element_active,
..Default::default()
},
}
}
fn disabled(&self, selected: bool, cx: &App) -> TabStyle {
match self {
TabVariant::Tab => TabStyle {
fg: cx.theme().text_muted,
bg: gpui::transparent_black(),
border_color: if selected {
cx.theme().border
} else {
gpui::transparent_black()
},
borders: Edges {
left: px(1.),
right: px(1.),
..Default::default()
},
..Default::default()
},
TabVariant::Outline => TabStyle {
fg: cx.theme().text_muted,
bg: gpui::transparent_black(),
borders: Edges::all(px(1.)),
border_color: if selected {
cx.theme().element_active
} else {
cx.theme().border
},
..Default::default()
},
TabVariant::Pill => TabStyle {
fg: if selected {
cx.theme().element_foreground.opacity(0.5)
} else {
cx.theme().text_muted
},
bg: if selected {
cx.theme().element_background.opacity(0.5)
} else {
gpui::transparent_black()
},
..Default::default()
},
TabVariant::Segmented => TabStyle {
fg: cx.theme().text_muted,
bg: cx.theme().tab_background,
inner_bg: if selected {
cx.theme().background
} else {
gpui::transparent_black()
},
..Default::default()
},
TabVariant::Underline => TabStyle {
fg: cx.theme().text_muted,
bg: gpui::transparent_black(),
border_color: if selected {
cx.theme().border
} else {
gpui::transparent_black()
},
borders: Edges {
bottom: px(2.),
..Default::default()
},
..Default::default()
},
}
}
pub(super) fn tab_bar_radius(&self, size: Size, cx: &App) -> Pixels {
if *self != TabVariant::Segmented {
return px(0.);
}
match size {
Size::XSmall | Size::Small => cx.theme().radius,
Size::Large => cx.theme().radius_lg,
_ => cx.theme().radius_lg,
}
}
fn radius(&self, size: Size, cx: &App) -> Pixels {
match self {
TabVariant::Outline | TabVariant::Pill => px(99.),
TabVariant::Segmented => match size {
Size::XSmall | Size::Small => cx.theme().radius,
Size::Large => cx.theme().radius_lg,
_ => cx.theme().radius_lg,
},
_ => px(0.),
}
}
fn inner_radius(&self, size: Size, cx: &App) -> Pixels {
match self {
TabVariant::Segmented => match size {
Size::Large => self.tab_bar_radius(size, cx) - px(3.),
_ => self.tab_bar_radius(size, cx) - px(2.),
},
_ => px(0.),
}
}
}
#[allow(dead_code)]
struct TabStyle {
borders: Edges<Pixels>,
border_color: Hsla,
bg: Hsla,
fg: Hsla,
shadow: bool,
inner_bg: Hsla,
}
impl Default for TabStyle {
fn default() -> Self {
TabStyle {
borders: Edges::all(px(0.)),
border_color: gpui::transparent_white(),
bg: gpui::transparent_white(),
fg: gpui::transparent_white(),
shadow: false,
inner_bg: gpui::transparent_white(),
}
}
}
#[allow(clippy::type_complexity)] #[allow(clippy::type_complexity)]
/// A Tab element for the [`super::TabBar`]. /// A Tab element for the [`super::TabBar`].
#[derive(IntoElement)] #[derive(IntoElement)]
@@ -404,8 +23,6 @@ pub struct Tab {
pub(super) tab_bar_prefix: Option<bool>, pub(super) tab_bar_prefix: Option<bool>,
suffix: Option<AnyElement>, suffix: Option<AnyElement>,
children: Vec<AnyElement>, children: Vec<AnyElement>,
variant: TabVariant,
size: Size,
pub(super) disabled: bool, pub(super) disabled: bool,
pub(super) selected: bool, pub(super) selected: bool,
on_click: Option<Rc<dyn Fn(&ClickEvent, &mut Window, &mut App) + 'static>>, on_click: Option<Rc<dyn Fn(&ClickEvent, &mut Window, &mut App) + 'static>>,
@@ -454,8 +71,6 @@ impl Default for Tab {
selected: false, selected: false,
prefix: None, prefix: None,
suffix: None, suffix: None,
variant: TabVariant::default(),
size: Size::default(),
on_click: None, on_click: None,
} }
} }
@@ -479,36 +94,6 @@ impl Tab {
self self
} }
/// Set Tab Variant.
pub fn with_variant(mut self, variant: TabVariant) -> Self {
self.variant = variant;
self
}
/// Use Pill variant.
pub fn pill(mut self) -> Self {
self.variant = TabVariant::Pill;
self
}
/// Use outline variant.
pub fn outline(mut self) -> Self {
self.variant = TabVariant::Outline;
self
}
/// Use Segmented variant.
pub fn segmented(mut self) -> Self {
self.variant = TabVariant::Segmented;
self
}
/// Use Underline variant.
pub fn underline(mut self) -> Self {
self.variant = TabVariant::Underline;
self
}
/// Set the left side of the tab /// Set the left side of the tab
pub fn prefix(mut self, prefix: impl IntoElement) -> Self { pub fn prefix(mut self, prefix: impl IntoElement) -> Self {
self.prefix = Some(prefix.into_any_element()); self.prefix = Some(prefix.into_any_element());
@@ -580,42 +165,16 @@ impl Styled for Tab {
} }
} }
impl Sizable for Tab {
fn with_size(mut self, size: impl Into<Size>) -> Self {
self.size = size.into();
self
}
}
impl RenderOnce for Tab { impl RenderOnce for Tab {
fn render(self, _: &mut Window, cx: &mut App) -> impl IntoElement { fn render(self, _: &mut Window, cx: &mut App) -> impl IntoElement {
let mut tab_style = if self.selected { let fg = if self.disabled {
self.variant.selected(cx) cx.theme().text_muted
} else if self.selected {
cx.theme().tab_active_foreground
} else { } else {
self.variant.normal(cx) cx.theme().tab_foreground
}; };
let mut hover_style = self.variant.hovered(self.selected, cx);
if self.disabled {
tab_style = self.variant.disabled(self.selected, cx);
hover_style = self.variant.disabled(self.selected, cx);
}
let tab_bar_prefix = self.tab_bar_prefix.unwrap_or_default();
if !tab_bar_prefix && self.ix == 0 && self.variant == TabVariant::Tab {
tab_style.borders.left = px(0.);
hover_style.borders.left = px(0.);
}
let radius = self.variant.radius(self.size, cx);
let inner_radius = self.variant.inner_radius(self.size, cx);
let inner_paddings = self.variant.inner_paddings(self.size);
let inner_margins = self.variant.inner_margins(self.size);
let inner_height = self.variant.inner_height(self.size);
let height = self.variant.height(self.size);
self.base self.base
.id(self.ix) .id(self.ix)
.flex() .flex()
@@ -623,74 +182,39 @@ impl RenderOnce for Tab {
.gap_1() .gap_1()
.items_center() .items_center()
.flex_shrink_0() .flex_shrink_0()
.h(height) .h(TABBAR_HEIGHT)
.overflow_hidden() .overflow_hidden()
.text_color(tab_style.fg) .text_color(fg)
.map(|this| match self.size { .text_sm()
Size::XSmall => this.text_xs(),
Size::Large => this.text_base(),
_ => this.text_sm(),
})
.bg(tab_style.bg)
.border_l(tab_style.borders.left)
.border_r(tab_style.borders.right)
.border_t(tab_style.borders.top)
.border_b(tab_style.borders.bottom)
.border_color(tab_style.border_color)
.rounded(radius)
.when(!self.selected && !self.disabled, |this| { .when(!self.selected && !self.disabled, |this| {
this.hover(|this| { this.hover(|this| this.text_color(cx.theme().secondary_foreground))
this.text_color(hover_style.fg)
.bg(hover_style.bg)
.border_l(hover_style.borders.left)
.border_r(hover_style.borders.right)
.border_t(hover_style.borders.top)
.border_b(hover_style.borders.bottom)
.border_color(hover_style.border_color)
.rounded(radius)
})
}) })
.when_some(self.prefix, |this, prefix| this.child(prefix)) .when_some(self.prefix, |this, prefix| this.child(prefix))
.child( .child(
h_flex() h_flex()
.flex_1() .flex_1()
.h(inner_height) .h(px(30.))
.line_height(relative(1.)) .line_height(relative(1.))
.whitespace_nowrap() .whitespace_nowrap()
.items_center() .items_center()
.justify_center() .justify_center()
.overflow_hidden() .overflow_hidden()
.margins(inner_margins)
.flex_shrink_0() .flex_shrink_0()
.px_3()
.map(|this| match self.icon { .map(|this| match self.icon {
Some(icon) => { Some(icon) => this.w(px(38.)).child(icon.size_4()),
this.w(inner_height * 1.25)
.child(icon.map(|this| match self.size {
Size::XSmall => this.size_2p5(),
Size::Small => this.size_3p5(),
Size::Large => this.size_4(),
_ => this.size_4(),
}))
}
None => this None => this
.paddings(inner_paddings)
.map(|this| match self.label { .map(|this| match self.label {
Some(label) => this.child(label), Some(label) => this.child(label),
None => this, None => this,
}) })
.children(self.children), .children(self.children),
}) }),
.bg(tab_style.inner_bg)
.rounded(inner_radius)
.when(tab_style.shadow, |this| this.shadow_xs())
.hover(|this| this.bg(hover_style.inner_bg).rounded(inner_radius)),
) )
.when_some(self.suffix, |this, suffix| { .when_some(self.suffix, |this, suffix| {
this.child(div().pr_2().child(suffix)) this.child(div().pr_2().child(suffix))
}) })
.on_mouse_down(MouseButton::Left, |_, _, cx| { .on_mouse_down(MouseButton::Left, |_ev, _window, cx| {
// Stop propagation behavior, for works on TitleBar.
// https://github.com/longbridge/gpui-component/issues/1836
cx.stop_propagation(); cx.stop_propagation();
}) })
.when(!self.disabled, |this| { .when(!self.disabled, |this| {

View File

@@ -2,19 +2,17 @@ use std::rc::Rc;
use gpui::prelude::FluentBuilder as _; use gpui::prelude::FluentBuilder as _;
use gpui::{ use gpui::{
Anchor, AnyElement, App, Div, Edges, ElementId, InteractiveElement, IntoElement, ParentElement, Anchor, AnyElement, App, Div, ElementId, InteractiveElement, IntoElement, ParentElement,
RenderOnce, ScrollHandle, Stateful, StatefulInteractiveElement as _, StyleRefinement, Styled, RenderOnce, ScrollHandle, Stateful, StatefulInteractiveElement as _, StyleRefinement, Styled,
Window, div, px, Window, div, px,
}; };
use smallvec::SmallVec; use smallvec::SmallVec;
use theme::ActiveTheme;
use super::{Tab, TabVariant}; use super::Tab;
use crate::button::{Button, ButtonVariants as _}; use crate::button::{Button, ButtonVariants as _};
use crate::menu::{DropdownMenu as _, PopupMenuItem}; use crate::menu::{DropdownMenu as _, PopupMenuItem};
use crate::{IconName, Selectable, Sizable, Size, StyledExt, h_flex}; use crate::{IconName, Selectable, Sizable, StyledExt, h_flex};
#[allow(clippy::type_complexity)]
/// A TabBar element that contains multiple [`Tab`] items. /// A TabBar element that contains multiple [`Tab`] items.
#[derive(IntoElement)] #[derive(IntoElement)]
pub struct TabBar { pub struct TabBar {
@@ -26,9 +24,8 @@ pub struct TabBar {
children: SmallVec<[Tab; 2]>, children: SmallVec<[Tab; 2]>,
last_empty_space: AnyElement, last_empty_space: AnyElement,
selected_index: Option<usize>, selected_index: Option<usize>,
variant: TabVariant,
size: Size,
menu: bool, menu: bool,
#[allow(clippy::type_complexity)]
on_click: Option<Rc<dyn Fn(&usize, &mut Window, &mut App) + 'static>>, on_click: Option<Rc<dyn Fn(&usize, &mut Window, &mut App) + 'static>>,
} }
@@ -42,8 +39,6 @@ impl TabBar {
scroll_handle: None, scroll_handle: None,
prefix: None, prefix: None,
suffix: None, suffix: None,
variant: TabVariant::default(),
size: Size::default(),
last_empty_space: div().w_3().into_any_element(), last_empty_space: div().w_3().into_any_element(),
selected_index: None, selected_index: None,
on_click: None, on_click: None,
@@ -51,36 +46,6 @@ impl TabBar {
} }
} }
/// Set the Tab variant, all children will inherit the variant.
pub fn with_variant(mut self, variant: TabVariant) -> Self {
self.variant = variant;
self
}
/// Set the Tab variant to Pill, all children will inherit the variant.
pub fn pill(mut self) -> Self {
self.variant = TabVariant::Pill;
self
}
/// Set the Tab variant to Outline, all children will inherit the variant.
pub fn outline(mut self) -> Self {
self.variant = TabVariant::Outline;
self
}
/// Set the Tab variant to Segmented, all children will inherit the variant.
pub fn segmented(mut self) -> Self {
self.variant = TabVariant::Segmented;
self
}
/// Set the Tab variant to Underline, all children will inherit the variant.
pub fn underline(mut self) -> Self {
self.variant = TabVariant::Underline;
self
}
/// Set whether to show the menu button when tabs overflow, default is false. /// Set whether to show the menu button when tabs overflow, default is false.
pub fn menu(mut self, menu: bool) -> Self { pub fn menu(mut self, menu: bool) -> Self {
self.menu = menu; self.menu = menu;
@@ -105,13 +70,13 @@ impl TabBar {
self self
} }
/// Add children of the TabBar, all children will inherit the variant. /// Add children of the TabBar.
pub fn children(mut self, children: impl IntoIterator<Item = impl Into<Tab>>) -> Self { pub fn children(mut self, children: impl IntoIterator<Item = impl Into<Tab>>) -> Self {
self.children.extend(children.into_iter().map(Into::into)); self.children.extend(children.into_iter().map(Into::into));
self self
} }
/// Add child of the TabBar, tab will inherit the variant. /// Add child of the TabBar.
pub fn child(mut self, child: impl Into<Tab>) -> Self { pub fn child(mut self, child: impl Into<Tab>) -> Self {
self.children.push(child.into()); self.children.push(child.into());
self self
@@ -147,60 +112,8 @@ impl Styled for TabBar {
} }
} }
impl Sizable for TabBar {
fn with_size(mut self, size: impl Into<Size>) -> Self {
self.size = size.into();
self
}
}
impl RenderOnce for TabBar { impl RenderOnce for TabBar {
fn render(self, _: &mut Window, cx: &mut App) -> impl IntoElement { fn render(self, _: &mut Window, _cx: &mut App) -> impl IntoElement {
let default_gap = match self.size {
Size::Small | Size::XSmall => px(8.),
Size::Large => px(16.),
_ => px(12.),
};
let (bg, paddings, gap) = match self.variant {
TabVariant::Tab => {
let padding = Edges::all(px(0.));
(cx.theme().tab_background, padding, px(0.))
}
TabVariant::Outline => {
let padding = Edges::all(px(0.));
(gpui::transparent_black(), padding, default_gap)
}
TabVariant::Pill => {
let padding = Edges::all(px(0.));
(gpui::transparent_black(), padding, px(4.))
}
TabVariant::Segmented => {
let padding_x = match self.size {
Size::XSmall => px(2.),
Size::Small => px(3.),
_ => px(4.),
};
let padding = Edges {
left: padding_x,
right: padding_x,
..Default::default()
};
(cx.theme().tab_background, padding, px(2.))
}
TabVariant::Underline => {
// This gap is same as the tab inner_paddings
let gap = match self.size {
Size::XSmall => px(10.),
Size::Small => px(12.),
Size::Large => px(20.),
_ => px(16.),
};
(gpui::transparent_black(), Edges::all(px(0.)), gap)
}
};
let mut item_labels = Vec::new(); let mut item_labels = Vec::new();
let selected_index = self.selected_index; let selected_index = self.selected_index;
let on_click = self.on_click.clone(); let on_click = self.on_click.clone();
@@ -210,25 +123,6 @@ impl RenderOnce for TabBar {
.relative() .relative()
.flex() .flex()
.items_center() .items_center()
.bg(bg)
.text_color(cx.theme().tab_foreground)
.when(
self.variant == TabVariant::Underline || self.variant == TabVariant::Tab,
|this| {
this.child(
div()
.id("border-b")
.absolute()
.left_0()
.bottom_0()
.size_full()
.border_b_1()
.border_color(cx.theme().border),
)
},
)
.rounded(self.variant.tab_bar_radius(self.size, cx))
.paddings(paddings)
.refine_style(&self.style) .refine_style(&self.style)
.when_some(self.prefix, |this, prefix| this.child(prefix)) .when_some(self.prefix, |this, prefix| this.child(prefix))
.child( .child(
@@ -239,15 +133,13 @@ impl RenderOnce for TabBar {
.when_some(self.scroll_handle, |this, scroll_handle| { .when_some(self.scroll_handle, |this, scroll_handle| {
this.track_scroll(&scroll_handle) this.track_scroll(&scroll_handle)
}) })
.gap(gap) .gap(px(0.))
.children(self.children.into_iter().enumerate().map(|(ix, child)| { .children(self.children.into_iter().enumerate().map(|(ix, child)| {
item_labels.push((child.label.clone(), child.disabled)); item_labels.push((child.label.clone(), child.disabled));
let tab_bar_prefix = child.tab_bar_prefix.unwrap_or(true); let tab_bar_prefix = child.tab_bar_prefix.unwrap_or(true);
child child
.ix(ix) .ix(ix)
.tab_bar_prefix(tab_bar_prefix) .tab_bar_prefix(tab_bar_prefix)
.with_variant(self.variant)
.with_size(self.size)
.when_some(self.selected_index, |this, selected_ix| { .when_some(self.selected_index, |this, selected_ix| {
this.selected(selected_ix == ix) this.selected(selected_ix == ix)
}) })

View File

@@ -283,8 +283,6 @@ impl RenderOnce for TitleBar {
this.px_2() this.px_2()
} }
}) })
.border_b_1()
.border_color(cx.theme().border)
.bg(cx.theme().title_bar) .bg(cx.theme().title_bar)
.refine_style(&self.style) .refine_style(&self.style)
.when(is_linux, |this| { .when(is_linux, |this| {

View File

@@ -1,11 +1,10 @@
use instant::Duration;
use anyhow::{Error, anyhow}; use anyhow::{Error, anyhow};
use gpui::prelude::FluentBuilder; use gpui::prelude::FluentBuilder;
use gpui::{ use gpui::{
AppContext, Context, Entity, IntoElement, ParentElement, Render, SharedString, Styled, AppContext, Context, Entity, IntoElement, ParentElement, Render, SharedString, Styled,
Subscription, Task, Window, div, Subscription, Task, Window, div,
}; };
use instant::Duration;
use nostr_connect::prelude::*; use nostr_connect::prelude::*;
use state::{CoopAuthUrlHandler, NostrRegistry, USER_KEYRING}; use state::{CoopAuthUrlHandler, NostrRegistry, USER_KEYRING};
use theme::ActiveTheme; use theme::ActiveTheme;
@@ -36,7 +35,7 @@ pub struct ImportIdentity {
impl ImportIdentity { impl ImportIdentity {
pub fn new(window: &mut Window, cx: &mut Context<Self>) -> Self { pub fn new(window: &mut Window, cx: &mut Context<Self>) -> Self {
let key_input = cx.new(|cx| InputState::new(window, cx)); let key_input = cx.new(|cx| InputState::new(window, cx).placeholder("nsec or bunker://"));
let pass_input = cx.new(|cx| InputState::new(window, cx).masked(true)); let pass_input = cx.new(|cx| InputState::new(window, cx).masked(true));
let error = cx.new(|_| None); let error = cx.new(|_| None);
@@ -135,7 +134,6 @@ impl ImportIdentity {
})?; })?;
} }
} }
Ok(()) Ok(())
})); }));
} }
@@ -208,40 +206,45 @@ impl Render for ImportIdentity {
v_flex() v_flex()
.size_full() .size_full()
.gap_2() .gap_4()
.text_sm()
.child( .child(
v_flex() v_flex()
.gap_1() .gap_2()
.text_color(cx.theme().text_muted) .child(
.child("Continue with existing key or bunker connection") v_flex()
.child(Input::new(&self.key_input)), .gap_1()
.text_color(cx.theme().text_muted)
.child("Continue with existing key or bunker connection")
.child(Input::new(&self.key_input)),
)
.when(require_password, |this| {
this.child(
v_flex()
.gap_1()
.text_color(cx.theme().text_muted)
.child("Decrypt Password:")
.child(Input::new(&self.pass_input)),
)
})
.when(key_warning, |this| {
this.child(
div()
.text_xs()
.text_color(cx.theme().text_warning)
.child(div().font_semibold().child("Warning"))
.child(div().child(MSG)),
)
}),
) )
.when(require_password, |this| {
this.child(
v_flex()
.gap_1()
.text_color(cx.theme().text_muted)
.child("Decrypt Password:")
.child(Input::new(&self.pass_input)),
)
})
.when(key_warning, |this| {
this.child(
div()
.v_flex()
.text_xs()
.text_color(cx.theme().text_warning)
.child(div().font_semibold().child("Warning"))
.child(div().child(MSG)),
)
})
.child( .child(
Button::new("login") Button::new("login")
.label("Continue") .label("Continue")
.primary() .primary()
.font_semibold()
.loading(self.loading) .loading(self.loading)
.disabled(self.loading) .disabled(self.loading)
.on_click(cx.listener(move |this, _, window, cx| { .on_click(cx.listener(move |this, _ev, window, cx| {
this.login(window, cx); this.login(window, cx);
})), })),
) )

View File

@@ -28,6 +28,7 @@ use crate::dialogs::import::ImportIdentity;
use crate::dialogs::restore::RestoreEncryption; use crate::dialogs::restore::RestoreEncryption;
use crate::dialogs::settings; use crate::dialogs::settings;
use crate::panels::{backup, contact_list, greeter, messaging_relays, profile, relay_list, trash}; use crate::panels::{backup, contact_list, greeter, messaging_relays, profile, relay_list, trash};
use crate::sidebar::Sidebar;
mod dialogs; mod dialogs;
mod panels; mod panels;
@@ -60,6 +61,7 @@ enum Command {
} }
pub struct Workspace { pub struct Workspace {
sidebar: Entity<Sidebar>,
/// App's Dock Area /// App's Dock Area
dock: Entity<DockArea>, dock: Entity<DockArea>,
@@ -79,14 +81,10 @@ impl Workspace {
let device = DeviceRegistry::global(cx); let device = DeviceRegistry::global(cx);
let nostr = NostrRegistry::global(cx); let nostr = NostrRegistry::global(cx);
let dock = cx.new(|cx| { let sidebar = cx.new(|cx| Sidebar::new(window, cx));
let mut this = DockArea::new(window, cx); let dock = cx.new(|cx| DockArea::new(window, cx));
let left = DockItem::panel(Arc::new(sidebar::init(window, cx)));
this.set_left_dock(left, Some(SIDEBAR_WIDTH), true, window, cx);
this
});
let image_cache = CoopImageCache::new(IMAGE_CACHE_SIZE, cx); let image_cache = CoopImageCache::new(IMAGE_CACHE_SIZE, cx);
let mut subscriptions = smallvec![]; let mut subscriptions = smallvec![];
subscriptions.push( subscriptions.push(
@@ -245,6 +243,7 @@ impl Workspace {
}); });
Self { Self {
sidebar,
dock, dock,
image_cache, image_cache,
tasks: vec![], tasks: vec![],
@@ -826,8 +825,19 @@ impl Render for Workspace {
.child(self.titlebar_left(cx)) .child(self.titlebar_left(cx))
.child(self.titlebar_right(cx)), .child(self.titlebar_right(cx)),
) )
// Dock // Main
.child(self.dock.clone()), .child(
h_flex()
.size_full()
.child(
div()
.flex_shrink_0()
.h_full()
.w(SIDEBAR_WIDTH)
.child(self.sidebar.clone()),
)
.child(self.dock.clone()),
),
), ),
) )
// Notifications // Notifications

View File

@@ -1,10 +1,9 @@
use instant::Duration;
use anyhow::Error; use anyhow::Error;
use gpui::{ use gpui::{
AnyElement, App, AppContext, ClipboardItem, Context, Entity, EventEmitter, FocusHandle, AnyElement, App, AppContext, ClipboardItem, Context, Entity, EventEmitter, FocusHandle,
Focusable, IntoElement, ParentElement, Render, SharedString, Styled, Task, Window, div, Focusable, IntoElement, ParentElement, Render, SharedString, Styled, Task, Window, div,
}; };
use instant::Duration;
use nostr_sdk::prelude::*; use nostr_sdk::prelude::*;
use state::USER_KEYRING; use state::USER_KEYRING;
use theme::ActiveTheme; use theme::ActiveTheme;
@@ -155,12 +154,7 @@ impl Render for BackupPanel {
.text_color(cx.theme().text_muted) .text_color(cx.theme().text_muted)
.child(SharedString::from("Public Key:")), .child(SharedString::from("Public Key:")),
) )
.child( .child(Input::new(&self.npub_input).small().disabled(true)),
Input::new(&self.npub_input)
.small()
.bordered(false)
.disabled(true),
),
) )
.child( .child(
v_flex() v_flex()
@@ -173,12 +167,7 @@ impl Render for BackupPanel {
.text_color(cx.theme().text_muted) .text_color(cx.theme().text_muted)
.child(SharedString::from("Secret Key:")), .child(SharedString::from("Secret Key:")),
) )
.child( .child(Input::new(&self.nsec_input).small().disabled(true)),
Input::new(&self.nsec_input)
.small()
.bordered(false)
.disabled(true),
),
) )
.child( .child(
Button::new("copy") Button::new("copy")

View File

@@ -1,5 +1,4 @@
use std::collections::HashSet; use std::collections::HashSet;
use instant::Duration;
use anyhow::Error; use anyhow::Error;
use gpui::prelude::FluentBuilder; use gpui::prelude::FluentBuilder;
@@ -8,6 +7,7 @@ use gpui::{
InteractiveElement, IntoElement, ParentElement, Render, SharedString, Styled, Subscription, InteractiveElement, IntoElement, ParentElement, Render, SharedString, Styled, Subscription,
Task, TextAlign, Window, div, rems, Task, TextAlign, Window, div, rems,
}; };
use instant::Duration;
use nostr_sdk::prelude::*; use nostr_sdk::prelude::*;
use person::PersonRegistry; use person::PersonRegistry;
use smallvec::{SmallVec, smallvec}; use smallvec::{SmallVec, smallvec};
@@ -303,12 +303,7 @@ impl Render for ContactListPanel {
h_flex() h_flex()
.gap_1() .gap_1()
.w_full() .w_full()
.child( .child(Input::new(&self.input).small().cleanable(true))
Input::new(&self.input)
.small()
.bordered(false)
.cleanable(true),
)
.child( .child(
Button::new("add") Button::new("add")
.icon(IconName::Plus) .icon(IconName::Plus)

View File

@@ -1,5 +1,4 @@
use std::collections::HashSet; use std::collections::HashSet;
use instant::Duration;
use anyhow::{Error, anyhow}; use anyhow::{Error, anyhow};
use gpui::prelude::FluentBuilder; use gpui::prelude::FluentBuilder;
@@ -8,6 +7,7 @@ use gpui::{
InteractiveElement, IntoElement, ParentElement, Render, SharedString, Styled, Subscription, InteractiveElement, IntoElement, ParentElement, Render, SharedString, Styled, Subscription,
Task, TextAlign, Window, div, rems, Task, TextAlign, Window, div, rems,
}; };
use instant::Duration;
use nostr_sdk::prelude::*; use nostr_sdk::prelude::*;
use smallvec::{SmallVec, smallvec}; use smallvec::{SmallVec, smallvec};
use state::NostrRegistry; use state::NostrRegistry;
@@ -320,12 +320,7 @@ impl Render for MessagingRelayPanel {
h_flex() h_flex()
.gap_1() .gap_1()
.w_full() .w_full()
.child( .child(Input::new(&self.input).small().cleanable(true))
Input::new(&self.input)
.small()
.bordered(false)
.cleanable(true),
)
.child( .child(
Button::new("add") Button::new("add")
.icon(IconName::Plus) .icon(IconName::Plus)

View File

@@ -353,7 +353,7 @@ impl Render for ProfilePanel {
.text_color(cx.theme().text_muted) .text_color(cx.theme().text_muted)
.child(SharedString::from("What should people call you?")), .child(SharedString::from("What should people call you?")),
) )
.child(Input::new(&self.name_input).bordered(false).small()), .child(Input::new(&self.name_input).small()),
) )
.child( .child(
v_flex() v_flex()
@@ -364,7 +364,7 @@ impl Render for ProfilePanel {
.text_color(cx.theme().text_muted) .text_color(cx.theme().text_muted)
.child(SharedString::from("A short introduction about you:")), .child(SharedString::from("A short introduction about you:")),
) )
.child(Input::new(&self.bio_input).bordered(false).small()), .child(Input::new(&self.bio_input).small()),
) )
.child( .child(
v_flex() v_flex()
@@ -375,7 +375,7 @@ impl Render for ProfilePanel {
.text_color(cx.theme().text_muted) .text_color(cx.theme().text_muted)
.child(SharedString::from("Website:")), .child(SharedString::from("Website:")),
) )
.child(Input::new(&self.website_input).bordered(false).small()), .child(Input::new(&self.website_input).small()),
) )
.child( .child(
v_flex() v_flex()

View File

@@ -1,5 +1,4 @@
use std::collections::HashSet; use std::collections::HashSet;
use instant::Duration;
use anyhow::{Error, anyhow}; use anyhow::{Error, anyhow};
use gpui::prelude::FluentBuilder; use gpui::prelude::FluentBuilder;
@@ -8,6 +7,7 @@ use gpui::{
InteractiveElement, IntoElement, ParentElement, Render, SharedString, Styled, Subscription, InteractiveElement, IntoElement, ParentElement, Render, SharedString, Styled, Subscription,
Task, TextAlign, Window, div, px, rems, Task, TextAlign, Window, div, px, rems,
}; };
use instant::Duration;
use nostr_sdk::prelude::*; use nostr_sdk::prelude::*;
use serde::Deserialize; use serde::Deserialize;
use smallvec::{SmallVec, smallvec}; use smallvec::{SmallVec, smallvec};
@@ -371,12 +371,7 @@ impl Render for RelayListPanel {
h_flex() h_flex()
.gap_1() .gap_1()
.w_full() .w_full()
.child( .child(Input::new(&self.input).small().cleanable(true))
Input::new(&self.input)
.small()
.bordered(false)
.cleanable(true),
)
.child( .child(
Button::new("metadata") Button::new("metadata")
.map(|this| { .map(|this| {

View File

@@ -1,6 +1,5 @@
use std::collections::HashSet; use std::collections::HashSet;
use std::ops::Range; use std::ops::Range;
use instant::Duration;
use anyhow::Error; use anyhow::Error;
use chat::{ChatEvent, ChatRegistry, Room, RoomKind}; use chat::{ChatEvent, ChatRegistry, Room, RoomKind};
@@ -12,11 +11,12 @@ use gpui::{
ParentElement, Render, SharedString, Styled, Subscription, Task, UniformListScrollHandle, ParentElement, Render, SharedString, Styled, Subscription, Task, UniformListScrollHandle,
Window, div, uniform_list, Window, div, uniform_list,
}; };
use instant::Duration;
use nostr_sdk::prelude::*; use nostr_sdk::prelude::*;
use person::PersonRegistry; use person::PersonRegistry;
use smallvec::{SmallVec, smallvec}; use smallvec::{SmallVec, smallvec};
use state::{FIND_DELAY, IMAGE_CACHE_SIZE, NostrRegistry}; use state::{FIND_DELAY, IMAGE_CACHE_SIZE, NostrRegistry};
use theme::{ActiveTheme, SIDEBAR_WIDTH, TABBAR_HEIGHT}; use theme::{ActiveTheme, SIDEBAR_WIDTH};
use ui::button::{Button, ButtonVariants}; use ui::button::{Button, ButtonVariants};
use ui::dock::{Panel, PanelEvent}; use ui::dock::{Panel, PanelEvent};
use ui::indicator::Indicator; use ui::indicator::Indicator;
@@ -29,13 +29,8 @@ mod entry;
const INPUT_PLACEHOLDER: &str = "Find or start a conversation"; const INPUT_PLACEHOLDER: &str = "Find or start a conversation";
pub fn init(window: &mut Window, cx: &mut App) -> Entity<Sidebar> {
cx.new(|cx| Sidebar::new(window, cx))
}
/// Sidebar. /// Sidebar.
pub struct Sidebar { pub struct Sidebar {
name: SharedString,
focus_handle: FocusHandle, focus_handle: FocusHandle,
scroll_handle: UniformListScrollHandle, scroll_handle: UniformListScrollHandle,
@@ -80,7 +75,7 @@ pub struct Sidebar {
} }
impl Sidebar { impl Sidebar {
fn new(window: &mut Window, cx: &mut Context<Self>) -> Self { pub fn new(window: &mut Window, cx: &mut Context<Self>) -> Self {
let chat = ChatRegistry::global(cx); let chat = ChatRegistry::global(cx);
let filter = cx.new(|_| RoomKind::Ongoing); let filter = cx.new(|_| RoomKind::Ongoing);
let contact_list = cx.new(|_| None); let contact_list = cx.new(|_| None);
@@ -135,7 +130,6 @@ impl Sidebar {
); );
Self { Self {
name: "Sidebar".into(),
focus_handle: cx.focus_handle(), focus_handle: cx.focus_handle(),
scroll_handle: UniformListScrollHandle::new(), scroll_handle: UniformListScrollHandle::new(),
find_input, find_input,
@@ -474,7 +468,7 @@ impl Sidebar {
impl Panel for Sidebar { impl Panel for Sidebar {
fn panel_id(&self) -> SharedString { fn panel_id(&self) -> SharedString {
self.name.clone() "Sidebar".into()
} }
} }
@@ -488,8 +482,11 @@ impl Focusable for Sidebar {
impl Render for Sidebar { impl Render for Sidebar {
fn render(&mut self, _window: &mut Window, cx: &mut Context<Self>) -> impl IntoElement { fn render(&mut self, _window: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
let nostr = NostrRegistry::global(cx);
let chat = ChatRegistry::global(cx); let chat = ChatRegistry::global(cx);
let loading = chat.read(cx).loading(); let logged_in = nostr.read(cx).current_user().is_some();
let loading = chat.read(cx).loading() && logged_in;
let total_rooms = chat.read(cx).count(self.filter.read(cx), cx); let total_rooms = chat.read(cx).count(self.filter.read(cx), cx);
// Whether the find panel should be shown // Whether the find panel should be shown
@@ -507,27 +504,21 @@ impl Render for Sidebar {
.size_full() .size_full()
.gap_2() .gap_2()
.child( .child(
h_flex() h_flex().px_2().child(
.h(TABBAR_HEIGHT) Input::new(&self.find_input)
.border_b_1() .small()
.border_color(cx.theme().border) .text_xs()
.bg(cx.theme().tab_background) .disabled(loading)
.child( .when(!self.find_input.read(cx).loading, |this| {
Input::new(&self.find_input) this.suffix(
.appearance(false) Button::new("find-icon")
.bordered(false) .icon(IconName::Search)
.small() .tooltip("Press Enter to search")
.text_xs() .transparent()
.when(!self.find_input.read(cx).loading, |this| { .small(),
this.suffix( )
Button::new("find-icon") }),
.icon(IconName::Search) ),
.tooltip("Press Enter to search")
.transparent()
.small(),
)
}),
),
) )
.child( .child(
h_flex() h_flex()
@@ -608,17 +599,13 @@ impl Render for Sidebar {
.items_center() .items_center()
.justify_center() .justify_center()
.text_center() .text_center()
.child(div().text_sm().font_semibold().child("No conversations"))
.child( .child(
div() div()
.text_sm() .text_xs()
.font_semibold() .text_color(cx.theme().text_muted)
.child(SharedString::from("No conversations")), .child("Start a conversation with someone to get started."),
) ),
.child(div().text_xs().text_color(cx.theme().text_muted).child(
SharedString::from(
"Start a conversation with someone to get started.",
),
)),
), ),
) )
}) })
@@ -643,7 +630,7 @@ impl Render for Sidebar {
.font_semibold() .font_semibold()
.text_color(cx.theme().text_muted) .text_color(cx.theme().text_muted)
.child(Icon::new(IconName::ChevronDown)) .child(Icon::new(IconName::ChevronDown))
.child(SharedString::from("Results")), .child("Results"),
) )
.child( .child(
uniform_list( uniform_list(
@@ -748,7 +735,7 @@ impl Render for Sidebar {
.font_semibold() .font_semibold()
.text_color(cx.theme().text_muted) .text_color(cx.theme().text_muted)
.child(Indicator::new().small().color(cx.theme().icon_accent)) .child(Indicator::new().small().color(cx.theme().icon_accent))
.child(SharedString::from("Getting messages...")), .child("Getting messages..."),
), ),
) )
}) })

View File

@@ -24,6 +24,7 @@ gpui_platform.workspace = true
gpui_web = { git = "https://github.com/zed-industries/zed" } gpui_web = { git = "https://github.com/zed-industries/zed" }
log.workspace = true log.workspace = true
instant = { workspace = true, features = ["wasm-bindgen"] }
console_error_panic_hook = "0.1" console_error_panic_hook = "0.1"
tracing-wasm = "0.2" tracing-wasm = "0.2"

View File

@@ -1,21 +1,19 @@
use gpui::*; use gpui::*;
use ui::Root; use ui::Root;
use universal_time::{ use universal_time::{Instant, MonotonicClock, SystemTime, WallClock, define_time_provider};
Duration, Instant, MonotonicClock, SystemTime, WallClock, define_time_provider,
};
use wasm_bindgen::prelude::*; use wasm_bindgen::prelude::*;
struct CustomTimeProvider; struct CustomTimeProvider;
impl WallClock for CustomTimeProvider { impl WallClock for CustomTimeProvider {
fn system_time(&self) -> SystemTime { fn system_time(&self) -> SystemTime {
SystemTime::from_unix_duration(Duration::from_secs(0)) SystemTime::from_unix_duration(instant::Duration::from_secs(0))
} }
} }
impl MonotonicClock for CustomTimeProvider { impl MonotonicClock for CustomTimeProvider {
fn instant(&self) -> Instant { fn instant(&self) -> Instant {
Instant::from_ticks(Duration::from_secs(0)) Instant::from_ticks(instant::Duration::from_secs(0))
} }
} }
@@ -34,21 +32,7 @@ pub fn run() -> Result<(), JsValue> {
#[cfg(target_family = "wasm")] #[cfg(target_family = "wasm")]
gpui_platform::web_init(); gpui_platform::web_init();
#[cfg(not(target_family = "wasm"))] gpui_platform::application().run(|cx| {
let app = gpui_platform::application();
#[cfg(target_family = "wasm")]
let app = {
let app = gpui_platform::single_threaded_web();
// Workaround: intentionally leak the `Rc<AppCell>` to keep the application alive
struct WasmApplication(std::rc::Rc<AppCell>);
let wasm_app = unsafe { std::mem::transmute::<Application, WasmApplication>(app) };
std::mem::forget(wasm_app.0.clone());
unsafe { std::mem::transmute::<WasmApplication, Application>(wasm_app) }
};
app.run(|cx| {
// Open the root window // Open the root window
cx.open_window(WindowOptions::default(), |window, cx| { cx.open_window(WindowOptions::default(), |window, cx| {
// Initialize components // Initialize components