inline table

This commit is contained in:
Ben Kunkle
2025-06-10 11:55:27 +02:00
parent 08ba877df2
commit fc6d3c8fc9
2 changed files with 157 additions and 10 deletions

View File

@@ -2,12 +2,13 @@ use std::{cell::RefCell, fmt::Write as _, rc::Rc};
use db::anyhow::anyhow;
use gpui::{
AppContext as _, Context, Entity, EventEmitter, FocusHandle, Focusable, Global, Keymap,
Subscription, Task, actions,
AnyElement, AppContext as _, Context, Entity, EventEmitter, FocusHandle, Focusable, FontWeight,
Global, IntoElement, Keymap, Length, Subscription, Task, actions, div,
};
use ui::{
ActiveTheme as _, App, BorrowAppContext, ParentElement as _, Render, SharedString, Styled as _,
Table, Window, div, string_cell,
ActiveTheme as _, App, BorrowAppContext, Indicator, ParentElement as _, Render, SharedString,
Styled as _, Window, prelude::*,
};
use workspace::{Item, SerializableItem, Workspace, register_serializable_item};
@@ -30,10 +31,6 @@ pub fn init(cx: &mut App) {
register_serializable_item::<KeymapEditor>(cx);
}
enum KeymapEvent {
KeymapChanged,
}
pub struct KeymapEventChannel {}
impl EventEmitter<KeymapEvent> for KeymapEventChannel {}
@@ -212,6 +209,156 @@ impl Render for KeymapEditor {
}
}
/// A table component
#[derive(IntoElement)]
pub struct Table {
column_headers: Vec<SharedString>,
rows: Vec<Vec<TableCell>>,
column_count: usize,
striped: bool,
width: Length,
}
impl Table {
/// Create a new table with a column count equal to the
/// number of headers provided.
pub fn new(headers: Vec<impl Into<SharedString>>) -> Self {
let column_count = headers.len();
Table {
column_headers: headers.into_iter().map(Into::into).collect(),
column_count,
rows: Vec::new(),
striped: false,
width: Length::Auto,
}
}
/// Adds a row to the table.
///
/// The row must have the same number of columns as the table.
pub fn row(mut self, items: Vec<impl Into<TableCell>>) -> Self {
if items.len() == self.column_count {
self.rows.push(items.into_iter().map(Into::into).collect());
} else {
log::error!("Row length mismatch");
}
self
}
/// Adds multiple rows to the table.
///
/// Each row must have the same number of columns as the table.
/// Rows that don't match the column count are ignored.
pub fn rows(mut self, rows: Vec<Vec<impl Into<TableCell>>>) -> Self {
for row in rows {
self = self.row(row);
}
self
}
fn base_cell_style(cx: &mut App) -> Div {
div()
.px_1p5()
.flex_1()
.justify_start()
.text_ui(cx)
.whitespace_nowrap()
.text_ellipsis()
.overflow_hidden()
}
/// Enables row striping.
pub fn striped(mut self) -> Self {
self.striped = true;
self
}
/// Sets the width of the table.
pub fn width(mut self, width: impl Into<Length>) -> Self {
self.width = width.into();
self
}
}
impl RenderOnce for Table {
fn render(self, _: &mut Window, cx: &mut App) -> impl IntoElement {
let header = div()
.flex()
.flex_row()
.items_center()
.justify_between()
.w_full()
.p_2()
.border_b_1()
.border_color(cx.theme().colors().border)
.children(self.column_headers.into_iter().map(|h| {
Table::base_cell_style(cx)
.font_weight(FontWeight::SEMIBOLD)
.child(h.clone())
}));
let row_count = self.rows.len();
let rows = self.rows.into_iter().enumerate().map(|(ix, row)| {
let is_last = ix == row_count - 1;
let bg = if ix % 2 == 1 && self.striped {
Some(cx.theme().colors().text.opacity(0.05))
} else {
None
};
div()
.w_full()
.flex()
.flex_row()
.items_center()
.justify_between()
.px_1p5()
.py_1()
.when_some(bg, |row, bg| row.bg(bg))
.when(!is_last, |row| {
row.border_b_1().border_color(cx.theme().colors().border)
})
.children(row.into_iter().map(|cell| match cell {
TableCell::String(s) => Self::base_cell_style(cx).child(s),
TableCell::Element(e) => Self::base_cell_style(cx).child(e),
}))
});
div()
.w(self.width)
.overflow_hidden()
.child(header)
.children(rows)
}
}
/// Represents a cell in a table.
pub enum TableCell {
/// A cell containing a string value.
String(SharedString),
/// A cell containing a UI element.
Element(AnyElement),
}
/// Creates a `TableCell` containing a string value.
pub fn string_cell(s: impl Into<SharedString>) -> TableCell {
TableCell::String(s.into())
}
/// Creates a `TableCell` containing an element.
pub fn element_cell(e: impl Into<AnyElement>) -> TableCell {
TableCell::Element(e.into())
}
impl<E> From<E> for TableCell
where
E: Into<SharedString>,
{
fn from(e: E) -> Self {
TableCell::String(e.into())
}
}
mod persistence {
use db::{define_connection, query, sqlez_macros::sql};
use workspace::WorkspaceDb;

View File

@@ -85,9 +85,9 @@ impl RenderOnce for Table {
.border_b_1()
.border_color(cx.theme().colors().border)
.children(self.column_headers.into_iter().map(|h| {
Self::base_cell_style(cx)
Table::base_cell_style(cx)
.font_weight(FontWeight::SEMIBOLD)
.child(h)
.child(h.clone())
}));
let row_count = self.rows.len();