feat: Add sidebar filtering and fix layout issues
Connect sidebar project/tag clicks to filter the task table. App now observes FilterState changes and triggers task reload. Removed redundant observer from TaskTable since App handles it. Fix Panel component that was overwriting its own styles, breaking flex layouts. Sidebar sections now split space equally and scroll properly.
This commit is contained in:
+17
-3
@@ -55,6 +55,13 @@ impl gpui::Render for App {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl App {
|
impl App {
|
||||||
|
fn reload_tasks(&mut self, cx: &mut gpui::Context<Self>) {
|
||||||
|
let task_service = &mut self.task_service;
|
||||||
|
self.task_table.update(cx, |table, cx| {
|
||||||
|
table.reload_tasks(task_service, cx);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
pub fn run() -> () {
|
pub fn run() -> () {
|
||||||
let app = gpui::Application::new();
|
let app = gpui::Application::new();
|
||||||
|
|
||||||
@@ -102,13 +109,20 @@ impl App {
|
|||||||
table.reload_tasks(&mut task_service, cx);
|
table.reload_tasks(&mut task_service, cx);
|
||||||
});
|
});
|
||||||
|
|
||||||
App {
|
let app = App {
|
||||||
sidebar,
|
sidebar,
|
||||||
filter_state,
|
filter_state: filter_state.clone(),
|
||||||
status_bar,
|
status_bar,
|
||||||
task_table,
|
task_table,
|
||||||
task_service,
|
task_service,
|
||||||
}
|
};
|
||||||
|
|
||||||
|
cx.observe(&filter_state, |app, _, cx| {
|
||||||
|
app.reload_tasks(cx);
|
||||||
|
})
|
||||||
|
.detach();
|
||||||
|
|
||||||
|
app
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -78,21 +78,15 @@ impl gpui::RenderOnce for Panel {
|
|||||||
|
|
||||||
let children: Vec<gpui::AnyElement> = self.content.drain(..).collect();
|
let children: Vec<gpui::AnyElement> = self.content.drain(..).collect();
|
||||||
|
|
||||||
let mut div = gpui::div()
|
gpui::div()
|
||||||
.size_full()
|
.size_full()
|
||||||
.bg(theme.panel)
|
.bg(theme.panel)
|
||||||
.border(gpui::px(self.border))
|
.border(gpui::px(self.border))
|
||||||
.border_color(theme.border)
|
.border_color(theme.border)
|
||||||
.rounded_md()
|
.rounded_md()
|
||||||
.p(gpui::px(self.padding))
|
.p(gpui::px(self.padding))
|
||||||
.flex()
|
.overflow_hidden()
|
||||||
.flex_col()
|
|
||||||
.h_full()
|
|
||||||
.children(header)
|
.children(header)
|
||||||
.children(children);
|
.children(children)
|
||||||
|
|
||||||
*div.style() = self.style;
|
|
||||||
|
|
||||||
div
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+16
-38
@@ -2,7 +2,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 gpui::{Context, Div, Entity, IntoElement, Window, div, prelude::*, px};
|
use gpui::{Context, Div, Entity, IntoElement, Window, div, prelude::*, px};
|
||||||
use std::sync::Arc;
|
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub struct TagItem {
|
pub struct TagItem {
|
||||||
@@ -14,7 +13,6 @@ pub struct Sidebar {
|
|||||||
project_tree: ProjectTree,
|
project_tree: ProjectTree,
|
||||||
tags: Vec<TagItem>,
|
tags: Vec<TagItem>,
|
||||||
filter_state: Entity<FilterState>,
|
filter_state: Entity<FilterState>,
|
||||||
on_filter_change: Option<Arc<dyn Fn(FilterState, &mut Window, &mut Context<Self>) + 'static>>,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Sidebar {
|
impl Sidebar {
|
||||||
@@ -33,18 +31,9 @@ impl Sidebar {
|
|||||||
project_tree,
|
project_tree,
|
||||||
tags,
|
tags,
|
||||||
filter_state,
|
filter_state,
|
||||||
on_filter_change: None,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn on_filter_change<F>(mut self, callback: F) -> Self
|
|
||||||
where
|
|
||||||
F: Fn(FilterState, &mut Window, &mut Context<Self>) + 'static,
|
|
||||||
{
|
|
||||||
self.on_filter_change = Some(Arc::new(callback));
|
|
||||||
self
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn update_projects(&mut self, project_tree: ProjectTree, cx: &mut Context<Self>) {
|
pub fn update_projects(&mut self, project_tree: ProjectTree, cx: &mut Context<Self>) {
|
||||||
self.project_tree = project_tree;
|
self.project_tree = project_tree;
|
||||||
cx.notify();
|
cx.notify();
|
||||||
@@ -58,18 +47,14 @@ impl Sidebar {
|
|||||||
fn handle_project_click(
|
fn handle_project_click(
|
||||||
&mut self,
|
&mut self,
|
||||||
full_path: Option<String>,
|
full_path: Option<String>,
|
||||||
window: &mut Window,
|
_window: &mut Window,
|
||||||
cx: &mut Context<Self>,
|
cx: &mut Context<Self>,
|
||||||
) {
|
) {
|
||||||
self.filter_state.update(cx, |filter, _cx| {
|
self.filter_state.update(cx, |filter, cx| {
|
||||||
filter.select_project(full_path);
|
filter.select_project(full_path);
|
||||||
|
cx.notify();
|
||||||
});
|
});
|
||||||
|
|
||||||
if let Some(callback) = &self.on_filter_change {
|
|
||||||
let filter = self.filter_state.read(cx).clone();
|
|
||||||
callback(filter, window, cx);
|
|
||||||
}
|
|
||||||
|
|
||||||
cx.notify();
|
cx.notify();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -78,16 +63,11 @@ impl Sidebar {
|
|||||||
cx.notify();
|
cx.notify();
|
||||||
}
|
}
|
||||||
|
|
||||||
fn handle_tag_click(&mut self, tag_name: String, window: &mut Window, cx: &mut Context<Self>) {
|
fn handle_tag_click(&mut self, tag_name: String, _window: &mut Window, cx: &mut Context<Self>) {
|
||||||
self.filter_state.update(cx, |filter, _cx| {
|
self.filter_state.update(cx, |filter, cx| {
|
||||||
filter.toggle_tag(tag_name);
|
filter.toggle_tag(tag_name);
|
||||||
|
cx.notify();
|
||||||
});
|
});
|
||||||
|
|
||||||
if let Some(callback) = &self.on_filter_change {
|
|
||||||
let filter = self.filter_state.read(cx).clone();
|
|
||||||
callback(filter, window, cx);
|
|
||||||
}
|
|
||||||
|
|
||||||
cx.notify();
|
cx.notify();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -284,16 +264,18 @@ impl Render for Sidebar {
|
|||||||
div()
|
div()
|
||||||
.flex()
|
.flex()
|
||||||
.flex_col()
|
.flex_col()
|
||||||
.h_full()
|
.size_full()
|
||||||
.bg(theme.background)
|
.bg(theme.background)
|
||||||
.child(
|
.child(
|
||||||
div()
|
div()
|
||||||
.flex()
|
.flex()
|
||||||
.flex_col()
|
.flex_col()
|
||||||
.h(gpui::relative(0.5))
|
.flex_1()
|
||||||
|
.min_h_0()
|
||||||
.overflow_hidden()
|
.overflow_hidden()
|
||||||
.child(
|
.child(
|
||||||
div()
|
div()
|
||||||
|
.flex_shrink_0()
|
||||||
.px_3()
|
.px_3()
|
||||||
.py_2()
|
.py_2()
|
||||||
.border_b_1()
|
.border_b_1()
|
||||||
@@ -308,25 +290,24 @@ impl Render for Sidebar {
|
|||||||
.child(
|
.child(
|
||||||
div()
|
div()
|
||||||
.id("sidebar-projects")
|
.id("sidebar-projects")
|
||||||
.flex()
|
|
||||||
.flex_col()
|
|
||||||
.flex_1()
|
.flex_1()
|
||||||
.min_h_0()
|
.min_h_0()
|
||||||
.py_2()
|
.py_1()
|
||||||
.overflow_y_scroll()
|
.overflow_y_scroll()
|
||||||
.scrollbar_width(gpui::px(6.0))
|
|
||||||
.children(projects),
|
.children(projects),
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
.child(div().h_px().bg(theme.border))
|
.child(div().flex_shrink_0().h_px().bg(theme.border))
|
||||||
.child(
|
.child(
|
||||||
div()
|
div()
|
||||||
.flex()
|
.flex()
|
||||||
.flex_col()
|
.flex_col()
|
||||||
.h(gpui::relative(0.5))
|
.flex_1()
|
||||||
|
.min_h_0()
|
||||||
.overflow_hidden()
|
.overflow_hidden()
|
||||||
.child(
|
.child(
|
||||||
div()
|
div()
|
||||||
|
.flex_shrink_0()
|
||||||
.px_3()
|
.px_3()
|
||||||
.py_2()
|
.py_2()
|
||||||
.border_b_1()
|
.border_b_1()
|
||||||
@@ -341,13 +322,10 @@ impl Render for Sidebar {
|
|||||||
.child(
|
.child(
|
||||||
div()
|
div()
|
||||||
.id("sidebar-tags")
|
.id("sidebar-tags")
|
||||||
.flex()
|
|
||||||
.flex_col()
|
|
||||||
.flex_1()
|
.flex_1()
|
||||||
.min_h_0()
|
.min_h_0()
|
||||||
.py_2()
|
.py_1()
|
||||||
.overflow_y_scroll()
|
.overflow_y_scroll()
|
||||||
.scrollbar_width(gpui::px(6.0))
|
|
||||||
.children(tags),
|
.children(tags),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
|||||||
+4
-14
@@ -222,14 +222,8 @@ impl TaskTable {
|
|||||||
pub fn new(
|
pub fn new(
|
||||||
id: impl Into<gpui::ElementId>,
|
id: impl Into<gpui::ElementId>,
|
||||||
filter_state: gpui::Entity<FilterState>,
|
filter_state: gpui::Entity<FilterState>,
|
||||||
cx: &mut gpui::Context<Self>,
|
_cx: &mut gpui::Context<Self>,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
cx.observe(&filter_state, |table, _filter, cx| {
|
|
||||||
table.need_reload = true;
|
|
||||||
cx.notify();
|
|
||||||
})
|
|
||||||
.detach();
|
|
||||||
|
|
||||||
Self {
|
Self {
|
||||||
id: id.into(),
|
id: id.into(),
|
||||||
filter_state,
|
filter_state,
|
||||||
@@ -315,13 +309,9 @@ impl TaskTable {
|
|||||||
self.cached_tasks = filtered_tasks;
|
self.cached_tasks = filtered_tasks;
|
||||||
self.apply_sort();
|
self.apply_sort();
|
||||||
self.pagination.total_items(self.cached_tasks.len());
|
self.pagination.total_items(self.cached_tasks.len());
|
||||||
|
self.pagination.current_page(1);
|
||||||
if let Some(idx) = self.selected_global_idx {
|
self.selected_global_idx = None;
|
||||||
if idx >= self.cached_tasks.len() {
|
self.selected_page_idx = None;
|
||||||
self.selected_global_idx = None;
|
|
||||||
self.selected_page_idx = None;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
self.recalculate_rows();
|
self.recalculate_rows();
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user