#[non_exhaustive]pub struct GridLayout { /* private fields */ }Expand description
A saved grid layout: the columns by ColumnId in display order, each
with its width, visibility and zone, the sort, and the grouping with the
expansion of its groups.
Plain data without a GPUI entity and without the application’s column
type, so one value fits every grid of the same view. Serialize it with
serde or a LayoutCodec. The grid resolves it against its schema when
it is applied: unknown columns are ignored and new ones are added.
The format is version 1 (GridLayout::VERSION). Defaults are not
written: no view_id when there is none, no visible for a visible
column, no frozen for a scrolling one, no sort or group_by when it
is empty, and no expansion when every group is expanded and none is
toggled. Reading ignores unknown fields, so a newer build can add an
optional field without a new version, as group_by and expansion were
added; an older build drops that field when it saves again.
Deserializing with serde keeps the version the data carries. The codec refuses a version this build does not read, and so does the grid when it applies a layout.
use gpui_kit_grid::{
ColumnZone, GridLayout, JsonLayoutCodec, LayoutCodec, LayoutColumn, LayoutSort,
SortDirection,
};
let layout = GridLayout::new()
.with_view_id("trades")
.with_column(LayoutColumn::new("id", 80.).with_zone(ColumnZone::FrozenLeft))
.with_column(LayoutColumn::new("price", 100.))
.with_column(LayoutColumn::new("fee", 90.).with_visible(false))
.with_sort([LayoutSort::new("price", SortDirection::Descending)]);
let codec = JsonLayoutCodec::default();
let text = codec.encode(&layout)?;
assert_eq!(
text,
concat!(
r#"{"version":1,"view_id":"trades","columns":["#,
r#"{"id":"id","width":80.0,"frozen":"left"},"#,
r#"{"id":"price","width":100.0},"#,
r#"{"id":"fee","width":90.0,"visible":false}],"#,
r#""sort":[{"column":"price","direction":"descending"}]}"#,
)
);
assert_eq!(codec.decode(&text)?, layout);§Grouping and expansion
group_by names the grouping levels by column, the top level first. A
layout without levels has no grouping: applying it removes the grid’s
grouping. expansion keeps the default depth and the groups the user
toggled against it, each as its key values from the top level down; a
value is never an index, and the levels of the same layout name its
columns.
use gpui_kit_grid::{
ExpandDepth, GridLayout, JsonLayoutCodec, LayoutCodec, LayoutExpansion, LayoutGroup,
OwnedValue, SortDirection,
};
let aapl = OwnedValue::Text("AAPL".into());
let layout = GridLayout::new()
.with_group_by([
LayoutGroup::new("instrument", SortDirection::Ascending),
LayoutGroup::new("side", SortDirection::Descending),
])
.with_expansion(LayoutExpansion::new(ExpandDepth::All).with_toggled([aapl]));
let codec = JsonLayoutCodec::default();
let text = codec.encode(&layout)?;
assert_eq!(
text,
concat!(
r#"{"version":1,"columns":[],"group_by":["#,
r#"{"column":"instrument","direction":"ascending"},"#,
r#"{"column":"side","direction":"descending"}],"#,
r#""expansion":{"depth":"all","toggled":[["AAPL"]]}}"#,
)
);
assert_eq!(codec.decode(&text)?, layout);§Saving a grid in a dock panel
The grid does no I/O and does not depend on the dock. A panel hands its
grid’s layout to the dock in Panel::dump, and the panel’s builder in the
registry (register_panel) gives it back to with_layout.
LiveGridState::layout reads state only, so a panel in a tab that was
never shown saves its layout too. The complete, tested version is
crates/grid/tests/dock.rs.
const TRADES: &str = "trades";
impl TradesPanel {
fn new(layout: Option<GridLayout>, cx: &mut Context<Self>) -> Self {
let grid = cx.new(|cx| {
let grid = LiveGridState::new_standalone(schema(), Trades, StoreOptions::default(), cx)
.with_view_id(TRADES);
match layout {
Some(layout) => grid.with_layout(layout),
None => grid,
}
});
Self { grid, focus_handle: cx.focus_handle() }
}
}
impl BasePanel for TradesPanel {
fn panel_name(&self) -> &'static str {
TRADES
}
/// The dock pulls the layout when it saves; the grid needs no render.
fn dump(&self, cx: &App) -> PanelState {
let mut state = PanelState::new(TRADES);
let layout = self.grid.read(cx).layout();
state.info = PanelInfo::panel(serde_json::json!({ "grid": layout }));
state
}
}
// At startup, before `DockArea::load`. A bad layout opens the panel with
// the schema's layout instead of failing the load; `with_layout` ignores
// a layout of a newer version.
register_panel(cx, TRADES, |context, _window, cx| {
let layout = match context.info() {
PanelInfo::Panel(value) => value
.get("grid")
.and_then(|grid| serde_json::from_value::<GridLayout>(grid.clone()).ok()),
_ => None,
};
panel_handle(cx.new(|cx| TradesPanel::new(layout, cx)))
});The dock saves on its own edits only (DockEvent::LayoutChanged), so
changes inside a panel must trigger a save too. They are the grid’s
LayoutChange, SortChange, GroupChange and ExpansionChange events,
which come at once, once per finished gesture or call. The application
delays the write (0.5–1 s), since groups are expanded and collapsed as
often as a chevron is clicked, and also writes when it quits.
cx.subscribe(&grid, |app, _, event: &LiveGridEvent<u64, TradeColumn>, cx| {
if matches!(
event,
LiveGridEvent::LayoutChange { .. }
| LiveGridEvent::SortChange { .. }
| LiveGridEvent::GroupChange { .. }
| LiveGridEvent::ExpansionChange { .. }
) {
app.schedule_save(cx);
}
})
.detach();Implementations§
Source§impl GridLayout
impl GridLayout
Sourcepub fn with_view_id(self, view_id: impl Into<String>) -> Self
pub fn with_view_id(self, view_id: impl Into<String>) -> Self
Names the view the layout belongs to (“trades”). A grid with another view id refuses the layout.
Sourcepub fn with_column(self, column: LayoutColumn) -> Self
pub fn with_column(self, column: LayoutColumn) -> Self
Appends a column; the order of columns is the display order.
Sourcepub fn with_sort(self, sort: impl IntoIterator<Item = LayoutSort>) -> Self
pub fn with_sort(self, sort: impl IntoIterator<Item = LayoutSort>) -> Self
Replaces the sort; the first key sorts first.
Sourcepub fn with_group_by(
self,
levels: impl IntoIterator<Item = LayoutGroup>,
) -> Self
pub fn with_group_by( self, levels: impl IntoIterator<Item = LayoutGroup>, ) -> Self
Replaces the grouping levels; the first level groups first. No levels is no grouping.
Sourcepub fn with_expansion(self, expansion: LayoutExpansion) -> Self
pub fn with_expansion(self, expansion: LayoutExpansion) -> Self
Replaces the expansion of groups. The default, every group expanded and none toggled, is not written.
Sourcepub fn columns(&self) -> &[LayoutColumn]
pub fn columns(&self) -> &[LayoutColumn]
The columns in display order, as saved: they may include columns the schema no longer has, and repeats.
Sourcepub fn column(&self, id: &str) -> Option<&LayoutColumn>
pub fn column(&self, id: &str) -> Option<&LayoutColumn>
The first saved column with this id.
Sourcepub fn sort(&self) -> &[LayoutSort]
pub fn sort(&self) -> &[LayoutSort]
The sort keys, first key first.
Sourcepub fn group_by(&self) -> &[LayoutGroup]
pub fn group_by(&self) -> &[LayoutGroup]
The grouping levels, the top level first, as saved: they may name columns the schema no longer has or cannot group, and repeats.
Sourcepub fn expansion(&self) -> Option<&LayoutExpansion>
pub fn expansion(&self) -> Option<&LayoutExpansion>
The expansion of groups; None when the layout keeps the default:
every group expanded and none toggled.
Trait Implementations§
Source§impl Clone for GridLayout
impl Clone for GridLayout
Source§fn clone(&self) -> GridLayout
fn clone(&self) -> GridLayout
1.0.0 · Source§fn clone_from(&mut self, source: &Self)
fn clone_from(&mut self, source: &Self)
source. Read moreSource§impl Debug for GridLayout
impl Debug for GridLayout
Source§impl<'de> Deserialize<'de> for GridLayout
impl<'de> Deserialize<'de> for GridLayout
Source§fn deserialize<__D>(__deserializer: __D) -> Result<Self, __D::Error>where
__D: Deserializer<'de>,
fn deserialize<__D>(__deserializer: __D) -> Result<Self, __D::Error>where
__D: Deserializer<'de>,
Source§impl PartialEq for GridLayout
impl PartialEq for GridLayout
Source§impl Serialize for GridLayout
impl Serialize for GridLayout
impl StructuralPartialEq for GridLayout
Auto Trait Implementations§
impl Freeze for GridLayout
impl !RefUnwindSafe for GridLayout
impl Send for GridLayout
impl Sync for GridLayout
impl Unpin for GridLayout
impl UnsafeUnpin for GridLayout
impl !UnwindSafe for GridLayout
Blanket Implementations§
Source§impl<T> BorrowMut<T> for Twhere
T: ?Sized,
impl<T> BorrowMut<T> for Twhere
T: ?Sized,
Source§fn borrow_mut(&mut self) -> &mut T
fn borrow_mut(&mut self) -> &mut T
Source§impl<T> CloneToUninit for Twhere
T: Clone,
impl<T> CloneToUninit for Twhere
T: Clone,
§impl<T> Downcast for Twhere
T: Any,
impl<T> Downcast for Twhere
T: Any,
§fn into_any(self: Box<T>) -> Box<dyn Any>
fn into_any(self: Box<T>) -> Box<dyn Any>
Box<dyn Trait> (where Trait: Downcast) to Box<dyn Any>. Box<dyn Any> can
then be further downcast into Box<ConcreteType> where ConcreteType implements Trait.§fn into_any_rc(self: Rc<T>) -> Rc<dyn Any>
fn into_any_rc(self: Rc<T>) -> Rc<dyn Any>
Rc<Trait> (where Trait: Downcast) to Rc<Any>. Rc<Any> can then be
further downcast into Rc<ConcreteType> where ConcreteType implements Trait.§fn as_any(&self) -> &(dyn Any + 'static)
fn as_any(&self) -> &(dyn Any + 'static)
&Trait (where Trait: Downcast) to &Any. This is needed since Rust cannot
generate &Any’s vtable from &Trait’s.§fn as_any_mut(&mut self) -> &mut (dyn Any + 'static)
fn as_any_mut(&mut self) -> &mut (dyn Any + 'static)
&mut Trait (where Trait: Downcast) to &Any. This is needed since Rust cannot
generate &mut Any’s vtable from &mut Trait’s.§impl<T> DowncastSync for T
impl<T> DowncastSync for T
§impl<T> Instrument for T
impl<T> Instrument for T
§fn instrument(self, span: Span) -> Instrumented<Self>
fn instrument(self, span: Span) -> Instrumented<Self>
§fn in_current_span(self) -> Instrumented<Self>
fn in_current_span(self) -> Instrumented<Self>
Source§impl<T> IntoEither for T
impl<T> IntoEither for T
Source§fn into_either(self, into_left: bool) -> Either<Self, Self>
fn into_either(self, into_left: bool) -> Either<Self, Self>
self into a Left variant of Either<Self, Self>
if into_left is true.
Converts self into a Right variant of Either<Self, Self>
otherwise. Read moreSource§fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
self into a Left variant of Either<Self, Self>
if into_left(&self) returns true.
Converts self into a Right variant of Either<Self, Self>
otherwise. Read more