feat: Add card-based layout with theme tokens
Add a card-based UI layout with consistent spacing and visual hierarchy. The main content and sidebar are now wrapped in styled cards with gaps between them. New theme tokens (card, raised, hover, divider, disabled_fg) provide semantic colors for different surface levels and states. Create ui.rs module with helper functions (card_style, divider_h, section_header, priority_badge) to keep styling consistent across views.
This commit is contained in:
+21
-15
@@ -3,6 +3,7 @@ use gpui::prelude::*;
|
|||||||
use crate::models::{FilterState, ProjectTree};
|
use crate::models::{FilterState, ProjectTree};
|
||||||
use crate::task::{TaskOverview, TaskService};
|
use crate::task::{TaskOverview, TaskService};
|
||||||
use crate::theme::ActiveTheme;
|
use crate::theme::ActiveTheme;
|
||||||
|
use crate::ui::{card_style, SECTION_GAP, ROOT_PADDING};
|
||||||
use crate::view::sidebar::{Sidebar, TagItem};
|
use crate::view::sidebar::{Sidebar, TagItem};
|
||||||
use crate::view::status_bar::StatusBar;
|
use crate::view::status_bar::StatusBar;
|
||||||
use crate::view::task_table::TaskTable;
|
use crate::view::task_table::TaskTable;
|
||||||
@@ -24,31 +25,36 @@ impl gpui::Render for App {
|
|||||||
) -> impl gpui::IntoElement {
|
) -> impl gpui::IntoElement {
|
||||||
let theme = cx.theme();
|
let theme = cx.theme();
|
||||||
|
|
||||||
|
let sidebar = card_style(div(), theme)
|
||||||
|
.w(gpui::px(250.))
|
||||||
|
.h_full()
|
||||||
|
.flex_shrink_0()
|
||||||
|
.overflow_hidden()
|
||||||
|
.child(self.sidebar.clone());
|
||||||
|
|
||||||
|
let main = card_style(div(), theme)
|
||||||
|
.flex_1()
|
||||||
|
.h_full()
|
||||||
|
.min_w_0()
|
||||||
|
.overflow_hidden()
|
||||||
|
.p_0()
|
||||||
|
.child(self.task_table.clone());
|
||||||
|
|
||||||
let content = div()
|
let content = div()
|
||||||
.flex()
|
.flex()
|
||||||
.flex_1()
|
.flex_1()
|
||||||
.min_h_0()
|
.min_h_0()
|
||||||
.overflow_hidden()
|
.gap(SECTION_GAP)
|
||||||
.child(
|
.child(sidebar)
|
||||||
div()
|
.child(main);
|
||||||
.w(gpui::px(250.))
|
|
||||||
.h_full()
|
|
||||||
.flex_shrink_0()
|
|
||||||
.child(self.sidebar.clone()),
|
|
||||||
)
|
|
||||||
.child(
|
|
||||||
div()
|
|
||||||
.flex_1()
|
|
||||||
.h_full()
|
|
||||||
.min_w_0()
|
|
||||||
.child(self.task_table.clone()),
|
|
||||||
);
|
|
||||||
|
|
||||||
div()
|
div()
|
||||||
.flex()
|
.flex()
|
||||||
.flex_col()
|
.flex_col()
|
||||||
.size_full()
|
.size_full()
|
||||||
.bg(theme.background)
|
.bg(theme.background)
|
||||||
|
.p(ROOT_PADDING)
|
||||||
|
.gap(SECTION_GAP)
|
||||||
.child(content)
|
.child(content)
|
||||||
.child(self.status_bar.clone())
|
.child(self.status_bar.clone())
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -300,6 +300,7 @@ impl gpui::Render for Dropdown {
|
|||||||
.bg(theme.background)
|
.bg(theme.background)
|
||||||
.text_sm()
|
.text_sm()
|
||||||
.text_color(theme.foreground)
|
.text_color(theme.foreground)
|
||||||
|
.whitespace_nowrap()
|
||||||
.child(Label::new(label.clone()))
|
.child(Label::new(label.clone()))
|
||||||
.child(Label::new(arrow).text_color(theme.muted));
|
.child(Label::new(arrow).text_color(theme.muted));
|
||||||
|
|
||||||
@@ -308,14 +309,14 @@ impl gpui::Render for Dropdown {
|
|||||||
} else {
|
} else {
|
||||||
trigger = trigger
|
trigger = trigger
|
||||||
.cursor_pointer()
|
.cursor_pointer()
|
||||||
.hover(|s: gpui::StyleRefinement| s.bg(theme.selection));
|
.hover(|s: gpui::StyleRefinement| s.bg(theme.hover));
|
||||||
}
|
}
|
||||||
|
|
||||||
trigger.into_any_element()
|
trigger.into_any_element()
|
||||||
};
|
};
|
||||||
|
|
||||||
let mut trigger_wrap = gpui::div()
|
let mut trigger_wrap = gpui::div()
|
||||||
.min_w(gpui::rems(12.0))
|
.flex_shrink_0()
|
||||||
.child(trigger);
|
.child(trigger);
|
||||||
if !disabled {
|
if !disabled {
|
||||||
trigger_wrap = trigger_wrap.on_mouse_down(
|
trigger_wrap = trigger_wrap.on_mouse_down(
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ mod components;
|
|||||||
mod models;
|
mod models;
|
||||||
mod task;
|
mod task;
|
||||||
mod theme;
|
mod theme;
|
||||||
|
mod ui;
|
||||||
mod view;
|
mod view;
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
|
|||||||
+144
-41
@@ -1,79 +1,182 @@
|
|||||||
|
use gpui::prelude::*;
|
||||||
|
|
||||||
pub type Color = gpui::Rgba;
|
pub type Color = gpui::Rgba;
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub struct Theme {
|
pub struct Theme {
|
||||||
pub background: Color,
|
pub background: Color,
|
||||||
pub foreground: Color,
|
|
||||||
pub panel: Color,
|
pub panel: Color,
|
||||||
|
pub card: Color,
|
||||||
|
pub raised: Color,
|
||||||
|
|
||||||
|
pub foreground: Color,
|
||||||
pub muted: Color,
|
pub muted: Color,
|
||||||
|
pub disabled_fg: Color,
|
||||||
|
|
||||||
pub accent: Color,
|
pub accent: Color,
|
||||||
|
pub focus_ring: Color,
|
||||||
|
|
||||||
pub border: Color,
|
pub border: Color,
|
||||||
|
pub divider: Color,
|
||||||
|
|
||||||
|
pub field_bg: Color,
|
||||||
|
pub field_border: Color,
|
||||||
|
pub field_placeholder: Color,
|
||||||
|
|
||||||
|
pub hover: Color,
|
||||||
|
pub selection: Color,
|
||||||
|
pub selection_foreground: Color,
|
||||||
|
|
||||||
|
pub backdrop: Color,
|
||||||
|
|
||||||
pub error: Color,
|
pub error: Color,
|
||||||
pub success: Color,
|
pub success: Color,
|
||||||
pub warning: Color,
|
pub warning: Color,
|
||||||
pub info: Color,
|
pub info: Color,
|
||||||
pub selection: Color,
|
|
||||||
pub selection_foreground: Color,
|
|
||||||
pub text: Color,
|
|
||||||
pub text_size: Option<gpui::Size<u32>>,
|
|
||||||
|
|
||||||
pub high: Color,
|
pub high: Color,
|
||||||
pub medium: Color,
|
pub medium: Color,
|
||||||
pub low: Color,
|
pub low: Color,
|
||||||
|
|
||||||
|
pub text_size: Option<gpui::Size<u32>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Theme {
|
impl Theme {
|
||||||
|
pub fn alpha(c: Color, a: f32) -> Color {
|
||||||
|
gpui::Rgba { r: c.r, g: c.g, b: c.b, a: a.clamp(0.0, 1.0) }
|
||||||
|
}
|
||||||
|
|
||||||
pub fn dark() -> Self {
|
pub fn dark() -> Self {
|
||||||
|
let background = gpui::rgb(0x0A0E14);
|
||||||
|
let panel = gpui::rgb(0x0F1419);
|
||||||
|
let foreground = gpui::rgb(0xB3B1AD);
|
||||||
|
let muted = gpui::rgb(0x5C6773);
|
||||||
|
let accent = gpui::rgb(0xFFB454);
|
||||||
|
let border = gpui::rgb(0x1F2430);
|
||||||
|
|
||||||
|
let card = gpui::rgb(0x111823);
|
||||||
|
let raised = gpui::rgb(0x151E2B);
|
||||||
|
|
||||||
|
let divider = Self::alpha(foreground, 0.10);
|
||||||
|
let hover = Self::alpha(foreground, 0.05);
|
||||||
|
let field_bg = raised;
|
||||||
|
let field_border = Self::alpha(foreground, 0.14);
|
||||||
|
let field_placeholder = Self::alpha(foreground, 0.45);
|
||||||
|
let focus_ring = Self::alpha(accent, 0.75);
|
||||||
|
let disabled_fg = Self::alpha(foreground, 0.35);
|
||||||
|
let backdrop = gpui::rgba(0x0000008C);
|
||||||
|
|
||||||
|
let selection = gpui::rgb(0x273747);
|
||||||
|
let selection_foreground = gpui::rgb(0xE6E1CF);
|
||||||
|
|
||||||
|
let error = gpui::rgb(0xF07178);
|
||||||
|
let success = gpui::rgb(0xAAD94C);
|
||||||
|
let warning = gpui::rgb(0xFFB454);
|
||||||
|
let info = gpui::rgb(0x59C2FF);
|
||||||
|
|
||||||
Self {
|
Self {
|
||||||
// ayu-dark
|
background,
|
||||||
background: gpui::rgb(0x0A0E14),
|
panel,
|
||||||
panel: gpui::rgb(0x0F1419),
|
card,
|
||||||
foreground: gpui::rgb(0xB3B1AD),
|
raised,
|
||||||
muted: gpui::rgb(0x5C6773),
|
|
||||||
accent: gpui::rgb(0xFFB454),
|
|
||||||
border: gpui::rgb(0x1F2430),
|
|
||||||
|
|
||||||
error: gpui::rgb(0xF07178),
|
foreground,
|
||||||
success: gpui::rgb(0xAAD94C),
|
muted,
|
||||||
warning: gpui::rgb(0xFFB454),
|
disabled_fg,
|
||||||
info: gpui::rgb(0x59C2FF),
|
|
||||||
|
|
||||||
selection: gpui::rgb(0x273747),
|
accent,
|
||||||
selection_foreground: gpui::rgb(0xE6E1CF),
|
focus_ring,
|
||||||
|
|
||||||
|
border,
|
||||||
|
divider,
|
||||||
|
|
||||||
|
field_bg,
|
||||||
|
field_border,
|
||||||
|
field_placeholder,
|
||||||
|
|
||||||
|
hover,
|
||||||
|
selection,
|
||||||
|
selection_foreground,
|
||||||
|
|
||||||
|
backdrop,
|
||||||
|
|
||||||
|
error,
|
||||||
|
success,
|
||||||
|
warning,
|
||||||
|
info,
|
||||||
|
|
||||||
|
high: error,
|
||||||
|
medium: warning,
|
||||||
|
low: success,
|
||||||
|
|
||||||
text: gpui::rgb(0xB3B1AD),
|
|
||||||
text_size: Some(gpui::Size::new(14, 14)),
|
text_size: Some(gpui::Size::new(14, 14)),
|
||||||
|
|
||||||
high: gpui::rgb(0xF07178),
|
|
||||||
medium: gpui::rgb(0xFFB454),
|
|
||||||
low: gpui::rgb(0xAAD94C),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn light() -> Self {
|
pub fn light() -> Self {
|
||||||
|
let background = gpui::rgb(0xFAFAFA);
|
||||||
|
let panel = gpui::rgb(0xFFFFFF);
|
||||||
|
let foreground = gpui::rgb(0x5C6166);
|
||||||
|
let muted = gpui::rgb(0x8A9199);
|
||||||
|
let accent = gpui::rgb(0xFF8F40);
|
||||||
|
let border = gpui::rgb(0xE6E6E6);
|
||||||
|
|
||||||
|
let card = gpui::rgb(0xF6F7F9);
|
||||||
|
let raised = gpui::rgb(0xEFF1F5);
|
||||||
|
|
||||||
|
let divider = Self::alpha(foreground, 0.12);
|
||||||
|
let hover = Self::alpha(foreground, 0.06);
|
||||||
|
let field_bg = gpui::rgb(0xFFFFFF);
|
||||||
|
let field_border = Self::alpha(foreground, 0.18);
|
||||||
|
let field_placeholder = Self::alpha(foreground, 0.55);
|
||||||
|
let focus_ring = Self::alpha(accent, 0.70);
|
||||||
|
let disabled_fg = Self::alpha(foreground, 0.40);
|
||||||
|
let backdrop = gpui::rgba(0x00000040);
|
||||||
|
|
||||||
|
let selection = gpui::rgb(0xD3EBFF);
|
||||||
|
let selection_foreground = foreground;
|
||||||
|
|
||||||
|
let error = gpui::rgb(0xE65050);
|
||||||
|
let success = gpui::rgb(0x86B300);
|
||||||
|
let warning = gpui::rgb(0xF2AE49);
|
||||||
|
let info = gpui::rgb(0x399EE6);
|
||||||
|
|
||||||
Self {
|
Self {
|
||||||
// ayu-light
|
background,
|
||||||
background: gpui::rgb(0xFAFAFA),
|
panel,
|
||||||
panel: gpui::rgb(0xFFFFFF),
|
card,
|
||||||
foreground: gpui::rgb(0x5C6166),
|
raised,
|
||||||
muted: gpui::rgb(0x8A9199),
|
|
||||||
accent: gpui::rgb(0xFF8F40),
|
|
||||||
border: gpui::rgb(0xE6E6E6),
|
|
||||||
|
|
||||||
error: gpui::rgb(0xE65050),
|
foreground,
|
||||||
success: gpui::rgb(0x86B300),
|
muted,
|
||||||
warning: gpui::rgb(0xF2AE49),
|
disabled_fg,
|
||||||
info: gpui::rgb(0x399EE6),
|
|
||||||
|
|
||||||
selection: gpui::rgb(0xD3EBFF),
|
accent,
|
||||||
selection_foreground: gpui::rgb(0x5C6166),
|
focus_ring,
|
||||||
|
|
||||||
|
border,
|
||||||
|
divider,
|
||||||
|
|
||||||
|
field_bg,
|
||||||
|
field_border,
|
||||||
|
field_placeholder,
|
||||||
|
|
||||||
|
hover,
|
||||||
|
selection,
|
||||||
|
selection_foreground,
|
||||||
|
|
||||||
|
backdrop,
|
||||||
|
|
||||||
|
error,
|
||||||
|
success,
|
||||||
|
warning,
|
||||||
|
info,
|
||||||
|
|
||||||
|
high: error,
|
||||||
|
medium: warning,
|
||||||
|
low: success,
|
||||||
|
|
||||||
text: gpui::rgb(0x5C6166),
|
|
||||||
text_size: Some(gpui::Size::new(14, 14)),
|
text_size: Some(gpui::Size::new(14, 14)),
|
||||||
|
|
||||||
high: gpui::rgb(0xE65050),
|
|
||||||
medium: gpui::rgb(0xF2AE49),
|
|
||||||
low: gpui::rgb(0x86B300),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,82 @@
|
|||||||
|
use gpui::prelude::*;
|
||||||
|
use gpui::{px, Pixels};
|
||||||
|
|
||||||
|
use crate::theme::Theme;
|
||||||
|
|
||||||
|
pub const CARD_RADIUS: Pixels = px(6.0);
|
||||||
|
pub const CARD_PADDING: Pixels = px(8.0);
|
||||||
|
pub const SECTION_GAP: Pixels = px(12.0);
|
||||||
|
pub const INSET_GAP: Pixels = px(8.0);
|
||||||
|
pub const ROOT_PADDING: Pixels = px(12.0);
|
||||||
|
|
||||||
|
pub fn card_style(div: gpui::Div, theme: &Theme) -> gpui::Div {
|
||||||
|
div.bg(theme.card)
|
||||||
|
.border_1()
|
||||||
|
.border_color(theme.divider)
|
||||||
|
.rounded(CARD_RADIUS)
|
||||||
|
.p(CARD_PADDING)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn raised_style(div: gpui::Div, theme: &Theme) -> gpui::Div {
|
||||||
|
div.bg(theme.raised)
|
||||||
|
.border_1()
|
||||||
|
.border_color(theme.divider)
|
||||||
|
.rounded(CARD_RADIUS)
|
||||||
|
.p(CARD_PADDING)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn panel_style(div: gpui::Div, theme: &Theme) -> gpui::Div {
|
||||||
|
div.bg(theme.panel)
|
||||||
|
.border_1()
|
||||||
|
.border_color(theme.border)
|
||||||
|
.rounded(CARD_RADIUS)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn field_style(div: gpui::Div, theme: &Theme, focused: bool) -> gpui::Div {
|
||||||
|
div.bg(theme.field_bg)
|
||||||
|
.border_1()
|
||||||
|
.border_color(if focused {
|
||||||
|
theme.focus_ring
|
||||||
|
} else {
|
||||||
|
theme.field_border
|
||||||
|
})
|
||||||
|
.rounded(CARD_RADIUS)
|
||||||
|
.px_2()
|
||||||
|
.py_1()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn divider_h(theme: &Theme) -> gpui::Div {
|
||||||
|
gpui::div().w_full().h(px(1.0)).bg(theme.divider)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn divider_v(theme: &Theme) -> gpui::Div {
|
||||||
|
gpui::div().h_full().w(px(1.0)).bg(theme.divider)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn section_header(label: &str, theme: &Theme) -> gpui::Div {
|
||||||
|
gpui::div()
|
||||||
|
.px_2()
|
||||||
|
.py_1()
|
||||||
|
.text_xs()
|
||||||
|
.text_color(theme.muted)
|
||||||
|
.child(label.to_uppercase())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn priority_badge(priority: &str, theme: &Theme) -> gpui::Div {
|
||||||
|
let (bg, fg) = match priority {
|
||||||
|
"High" | "H" => (Theme::alpha(theme.high, 0.15), theme.high),
|
||||||
|
"Medium" | "M" => (Theme::alpha(theme.medium, 0.15), theme.medium),
|
||||||
|
"Low" | "L" => (Theme::alpha(theme.low, 0.15), theme.low),
|
||||||
|
_ => (gpui::rgba(0x00000000), theme.muted),
|
||||||
|
};
|
||||||
|
|
||||||
|
gpui::div()
|
||||||
|
.px_2()
|
||||||
|
.py(px(2.0))
|
||||||
|
.rounded(px(4.0))
|
||||||
|
.bg(bg)
|
||||||
|
.text_color(fg)
|
||||||
|
.text_xs()
|
||||||
|
.font_weight(gpui::FontWeight::MEDIUM)
|
||||||
|
.child(priority.to_string())
|
||||||
|
}
|
||||||
+12
-28
@@ -1,6 +1,6 @@
|
|||||||
use crate::components::{label::Label, panel::Panel};
|
|
||||||
use crate::models::{FilterState, ProjectTree};
|
use crate::models::{FilterState, ProjectTree};
|
||||||
use crate::theme::ActiveTheme;
|
use crate::theme::ActiveTheme;
|
||||||
|
use crate::ui::{divider_h, section_header};
|
||||||
use gpui::{Context, Div, Entity, IntoElement, Window, div, prelude::*, px};
|
use gpui::{Context, Div, Entity, IntoElement, Window, div, prelude::*, px};
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
@@ -100,9 +100,10 @@ impl Sidebar {
|
|||||||
.gap_2()
|
.gap_2()
|
||||||
.px_3()
|
.px_3()
|
||||||
.py_1()
|
.py_1()
|
||||||
|
.rounded_sm()
|
||||||
.cursor_pointer()
|
.cursor_pointer()
|
||||||
.when(is_all_selected, |this| this.bg(theme.selection))
|
.when(is_all_selected, |this| this.bg(theme.selection))
|
||||||
.hover(|style| style.bg(theme.panel))
|
.when(!is_all_selected, |this| this.hover(|style| style.bg(theme.hover)))
|
||||||
.on_mouse_down(
|
.on_mouse_down(
|
||||||
gpui::MouseButton::Left,
|
gpui::MouseButton::Left,
|
||||||
cx.listener(|view, _event, window, cx| {
|
cx.listener(|view, _event, window, cx| {
|
||||||
@@ -149,9 +150,10 @@ impl Sidebar {
|
|||||||
.gap_1()
|
.gap_1()
|
||||||
.px_3()
|
.px_3()
|
||||||
.py_1()
|
.py_1()
|
||||||
|
.rounded_sm()
|
||||||
.cursor_pointer()
|
.cursor_pointer()
|
||||||
.when(is_selected, |this| this.bg(theme.selection))
|
.when(is_selected, |this| this.bg(theme.selection))
|
||||||
.hover(|style| style.bg(theme.panel))
|
.when(!is_selected, |this| this.hover(|style| style.bg(theme.hover)))
|
||||||
.child(div().w(px(indent as f32)))
|
.child(div().w(px(indent as f32)))
|
||||||
.child(
|
.child(
|
||||||
div()
|
div()
|
||||||
@@ -230,9 +232,10 @@ impl Sidebar {
|
|||||||
.gap_2()
|
.gap_2()
|
||||||
.px_3()
|
.px_3()
|
||||||
.py_1()
|
.py_1()
|
||||||
|
.rounded_sm()
|
||||||
.cursor_pointer()
|
.cursor_pointer()
|
||||||
.when(is_active, |this| this.bg(theme.selection))
|
.when(is_active, |this| this.bg(theme.selection))
|
||||||
.hover(|style| style.bg(theme.panel))
|
.when(!is_active, |this| this.hover(|style| style.bg(theme.hover)))
|
||||||
.on_mouse_down(
|
.on_mouse_down(
|
||||||
gpui::MouseButton::Left,
|
gpui::MouseButton::Left,
|
||||||
cx.listener(move |view, _event, window, cx| {
|
cx.listener(move |view, _event, window, cx| {
|
||||||
@@ -279,12 +282,10 @@ impl Render for Sidebar {
|
|||||||
let has_project = filter.selected_project.is_some();
|
let has_project = filter.selected_project.is_some();
|
||||||
let has_tags = !filter.active_tags.is_empty();
|
let has_tags = !filter.active_tags.is_empty();
|
||||||
|
|
||||||
Panel::new("Sidebar").border(1.0).padding(0.0).child(
|
|
||||||
div()
|
div()
|
||||||
.flex()
|
.flex()
|
||||||
.flex_col()
|
.flex_col()
|
||||||
.size_full()
|
.size_full()
|
||||||
.bg(theme.background)
|
|
||||||
.child(
|
.child(
|
||||||
div()
|
div()
|
||||||
.flex()
|
.flex()
|
||||||
@@ -298,16 +299,9 @@ impl Render for Sidebar {
|
|||||||
.flex()
|
.flex()
|
||||||
.items_center()
|
.items_center()
|
||||||
.justify_between()
|
.justify_between()
|
||||||
.px_3()
|
.px_2()
|
||||||
.py_2()
|
.py_2()
|
||||||
.border_b_1()
|
.child(section_header("Projects", &theme))
|
||||||
.border_color(theme.border)
|
|
||||||
.child(
|
|
||||||
Label::new("PROJECTS")
|
|
||||||
.text_sm()
|
|
||||||
.font_weight(gpui::FontWeight::BOLD)
|
|
||||||
.text_color(theme.foreground),
|
|
||||||
)
|
|
||||||
.when(has_project, |this| {
|
.when(has_project, |this| {
|
||||||
this.child(
|
this.child(
|
||||||
div()
|
div()
|
||||||
@@ -331,12 +325,11 @@ impl Render for Sidebar {
|
|||||||
.id("sidebar-projects")
|
.id("sidebar-projects")
|
||||||
.flex_1()
|
.flex_1()
|
||||||
.min_h_0()
|
.min_h_0()
|
||||||
.py_1()
|
|
||||||
.overflow_y_scroll()
|
.overflow_y_scroll()
|
||||||
.children(projects),
|
.children(projects),
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
.child(div().flex_shrink_0().h_px().bg(theme.border))
|
.child(divider_h(&theme).my_1())
|
||||||
.child(
|
.child(
|
||||||
div()
|
div()
|
||||||
.flex()
|
.flex()
|
||||||
@@ -350,16 +343,9 @@ impl Render for Sidebar {
|
|||||||
.flex()
|
.flex()
|
||||||
.items_center()
|
.items_center()
|
||||||
.justify_between()
|
.justify_between()
|
||||||
.px_3()
|
.px_2()
|
||||||
.py_2()
|
.py_2()
|
||||||
.border_b_1()
|
.child(section_header("Tags", &theme))
|
||||||
.border_color(theme.border)
|
|
||||||
.child(
|
|
||||||
Label::new("TAGS")
|
|
||||||
.text_sm()
|
|
||||||
.font_weight(gpui::FontWeight::BOLD)
|
|
||||||
.text_color(theme.foreground),
|
|
||||||
)
|
|
||||||
.when(has_tags, |this| {
|
.when(has_tags, |this| {
|
||||||
this.child(
|
this.child(
|
||||||
div()
|
div()
|
||||||
@@ -383,11 +369,9 @@ impl Render for Sidebar {
|
|||||||
.id("sidebar-tags")
|
.id("sidebar-tags")
|
||||||
.flex_1()
|
.flex_1()
|
||||||
.min_h_0()
|
.min_h_0()
|
||||||
.py_1()
|
|
||||||
.overflow_y_scroll()
|
.overflow_y_scroll()
|
||||||
.children(tags),
|
.children(tags),
|
||||||
),
|
),
|
||||||
),
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
use gpui::{Context, IntoElement, Render, Window, div, prelude::*, px};
|
use gpui::{Context, IntoElement, Render, Window, div, prelude::*, px};
|
||||||
|
|
||||||
use crate::theme::ActiveTheme;
|
use crate::theme::ActiveTheme;
|
||||||
|
use crate::ui::{card_style, CARD_RADIUS};
|
||||||
|
|
||||||
pub struct StatusBar {
|
pub struct StatusBar {
|
||||||
// TODO: Add vim_mode, sync_state, last_sync, etc.
|
// TODO: Add vim_mode, sync_state, last_sync, etc.
|
||||||
@@ -16,16 +17,14 @@ impl Render for StatusBar {
|
|||||||
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 theme = cx.theme().clone();
|
let theme = cx.theme().clone();
|
||||||
|
|
||||||
div()
|
card_style(div(), &theme)
|
||||||
.flex()
|
.flex()
|
||||||
.items_center()
|
.items_center()
|
||||||
.justify_between()
|
.justify_between()
|
||||||
.w_full()
|
.w_full()
|
||||||
.h(px(30.0))
|
.h(px(28.0))
|
||||||
.px_4()
|
.px_3()
|
||||||
.py_1()
|
.py_1()
|
||||||
.bg(theme.panel)
|
.rounded(CARD_RADIUS)
|
||||||
.border_b_1()
|
|
||||||
.border_color(theme.border)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+25
-23
@@ -13,6 +13,7 @@ use crate::{
|
|||||||
models::{DueFilter, FilterState, PriorityFilter, StatusFilter},
|
models::{DueFilter, FilterState, PriorityFilter, StatusFilter},
|
||||||
task::{self, TaskFilter, TaskService},
|
task::{self, TaskFilter, TaskService},
|
||||||
theme::{self, ActiveTheme},
|
theme::{self, ActiveTheme},
|
||||||
|
ui::priority_badge,
|
||||||
};
|
};
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||||
@@ -604,15 +605,14 @@ impl TaskTable {
|
|||||||
let bar = gpui::div()
|
let bar = gpui::div()
|
||||||
.id("filter-bar")
|
.id("filter-bar")
|
||||||
.flex()
|
.flex()
|
||||||
.flex_wrap()
|
.gap_3()
|
||||||
.gap_2()
|
|
||||||
.items_center()
|
.items_center()
|
||||||
.px_4()
|
.px_4()
|
||||||
.py_2()
|
.py_2()
|
||||||
.bg(theme.panel)
|
|
||||||
.child(
|
.child(
|
||||||
gpui::div()
|
gpui::div()
|
||||||
.w(gpui::rems(14.0))
|
.flex_1()
|
||||||
|
.min_w(gpui::rems(12.0))
|
||||||
.child(self.search_input.clone()),
|
.child(self.search_input.clone()),
|
||||||
)
|
)
|
||||||
.child(self.status_dropdown.clone())
|
.child(self.status_dropdown.clone())
|
||||||
@@ -622,13 +622,14 @@ impl TaskTable {
|
|||||||
this.child(
|
this.child(
|
||||||
gpui::div()
|
gpui::div()
|
||||||
.id("clear-all-filters")
|
.id("clear-all-filters")
|
||||||
|
.flex_shrink_0()
|
||||||
.px_2()
|
.px_2()
|
||||||
.py_1()
|
.py_1()
|
||||||
.rounded_md()
|
.rounded_md()
|
||||||
.text_sm()
|
.text_sm()
|
||||||
.text_color(theme.error)
|
.text_color(theme.error)
|
||||||
.cursor_pointer()
|
.cursor_pointer()
|
||||||
.hover(|s| s.bg(theme.selection))
|
.hover(|s| s.bg(theme.hover))
|
||||||
.on_mouse_down(
|
.on_mouse_down(
|
||||||
gpui::MouseButton::Left,
|
gpui::MouseButton::Left,
|
||||||
cx.listener(|table, _, _, cx| {
|
cx.listener(|table, _, _, cx| {
|
||||||
@@ -702,10 +703,9 @@ impl TaskTable {
|
|||||||
.gap_2()
|
.gap_2()
|
||||||
.px_4()
|
.px_4()
|
||||||
.py_2()
|
.py_2()
|
||||||
.border_t_1()
|
.bg(theme.raised)
|
||||||
.border_color(theme.border)
|
|
||||||
.border_b_1()
|
.border_b_1()
|
||||||
.bg(theme.panel)
|
.border_color(theme.divider)
|
||||||
.text_sm()
|
.text_sm()
|
||||||
.font_weight(gpui::FontWeight::MEDIUM)
|
.font_weight(gpui::FontWeight::MEDIUM)
|
||||||
.child(
|
.child(
|
||||||
@@ -756,12 +756,12 @@ impl TaskTable {
|
|||||||
.px_4()
|
.px_4()
|
||||||
.py_1()
|
.py_1()
|
||||||
.border_b_1()
|
.border_b_1()
|
||||||
.border_color(theme.border)
|
.border_color(theme.divider)
|
||||||
.text_color(theme.foreground)
|
.text_color(theme.foreground)
|
||||||
.when(selected, |d| {
|
.when(selected, |d| {
|
||||||
d.bg(theme.selection).text_color(theme.selection_foreground)
|
d.bg(theme.selection).text_color(theme.selection_foreground)
|
||||||
})
|
})
|
||||||
.when(!selected, |d| d.hover(|s| s.bg(theme.panel)))
|
.when(!selected, |d| d.hover(|s| s.bg(theme.hover)))
|
||||||
.cursor_pointer()
|
.cursor_pointer()
|
||||||
.on_mouse_down(
|
.on_mouse_down(
|
||||||
gpui::MouseButton::Left,
|
gpui::MouseButton::Left,
|
||||||
@@ -802,11 +802,9 @@ impl TaskTable {
|
|||||||
components::label::Label::new(row.due.clone()).text_color(self.due_color(row, cx)),
|
components::label::Label::new(row.due.clone()).text_color(self.due_color(row, cx)),
|
||||||
))
|
))
|
||||||
.child(
|
.child(
|
||||||
gpui::div().w(gpui::rems(5.0)).child(
|
gpui::div()
|
||||||
components::label::Label::new(row.priority.clone())
|
.w(gpui::rems(5.0))
|
||||||
.text_color(self.priority_color(row, cx))
|
.child(priority_badge(&row.priority, theme)),
|
||||||
.font_weight(gpui::FontWeight::BOLD),
|
|
||||||
),
|
|
||||||
)
|
)
|
||||||
.child(
|
.child(
|
||||||
gpui::div().w(gpui::rems(6.0)).child(
|
gpui::div().w(gpui::rems(6.0)).child(
|
||||||
@@ -831,8 +829,8 @@ impl TaskTable {
|
|||||||
.px_4()
|
.px_4()
|
||||||
.py_2()
|
.py_2()
|
||||||
.border_t_1()
|
.border_t_1()
|
||||||
.border_color(theme.border)
|
.border_color(theme.divider)
|
||||||
.bg(theme.panel)
|
.bg(theme.raised)
|
||||||
.text_sm()
|
.text_sm()
|
||||||
.child(
|
.child(
|
||||||
components::label::Label::new(format!(
|
components::label::Label::new(format!(
|
||||||
@@ -866,13 +864,15 @@ impl TaskTable {
|
|||||||
.px_2()
|
.px_2()
|
||||||
.py_1()
|
.py_1()
|
||||||
.rounded_md()
|
.rounded_md()
|
||||||
|
.border_1()
|
||||||
|
.border_color(theme.divider)
|
||||||
.text_color(if can_prev {
|
.text_color(if can_prev {
|
||||||
theme.foreground
|
theme.foreground
|
||||||
} else {
|
} else {
|
||||||
theme.muted
|
theme.disabled_fg
|
||||||
})
|
})
|
||||||
.when(can_prev, |d| {
|
.when(can_prev, |d| {
|
||||||
d.cursor_pointer().hover(|s| s.bg(theme.selection))
|
d.cursor_pointer().hover(|s| s.bg(theme.hover))
|
||||||
})
|
})
|
||||||
.when(!can_prev, |d| d.cursor_not_allowed())
|
.when(!can_prev, |d| d.cursor_not_allowed())
|
||||||
.on_mouse_down(
|
.on_mouse_down(
|
||||||
@@ -887,13 +887,15 @@ impl TaskTable {
|
|||||||
.px_2()
|
.px_2()
|
||||||
.py_1()
|
.py_1()
|
||||||
.rounded_md()
|
.rounded_md()
|
||||||
|
.border_1()
|
||||||
|
.border_color(theme.divider)
|
||||||
.text_color(if can_next {
|
.text_color(if can_next {
|
||||||
theme.foreground
|
theme.foreground
|
||||||
} else {
|
} else {
|
||||||
theme.muted
|
theme.disabled_fg
|
||||||
})
|
})
|
||||||
.when(can_next, |d| {
|
.when(can_next, |d| {
|
||||||
d.cursor_pointer().hover(|s| s.bg(theme.selection))
|
d.cursor_pointer().hover(|s| s.bg(theme.hover))
|
||||||
})
|
})
|
||||||
.when(!can_next, |d| d.cursor_not_allowed())
|
.when(!can_next, |d| d.cursor_not_allowed())
|
||||||
.on_mouse_down(
|
.on_mouse_down(
|
||||||
@@ -924,7 +926,7 @@ impl gpui::Render for TaskTable {
|
|||||||
.size_full()
|
.size_full()
|
||||||
.items_center()
|
.items_center()
|
||||||
.justify_center()
|
.justify_center()
|
||||||
.child(components::label::Label::new("Loading...").text_color(theme.text));
|
.child(components::label::Label::new("Loading...").text_color(theme.foreground));
|
||||||
}
|
}
|
||||||
|
|
||||||
let current_page = self.get_current_page_rows();
|
let current_page = self.get_current_page_rows();
|
||||||
@@ -945,7 +947,7 @@ impl gpui::Render for TaskTable {
|
|||||||
.min_h_0()
|
.min_h_0()
|
||||||
.overflow_hidden()
|
.overflow_hidden()
|
||||||
.bg(theme.background)
|
.bg(theme.background)
|
||||||
.child(gpui::div().h(self.filter_bar_height))
|
.child(gpui::div().h(self.filter_bar_height).mb_4())
|
||||||
.child(header)
|
.child(header)
|
||||||
.child(
|
.child(
|
||||||
gpui::div()
|
gpui::div()
|
||||||
|
|||||||
Reference in New Issue
Block a user