diff --git a/Cargo.lock b/Cargo.lock index e67c2f4365f..3ca2f30624a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2203,6 +2203,7 @@ dependencies = [ "enso-text", "enso-web", "ensogl", + "ensogl-breadcrumbs", "ensogl-component", "ensogl-drop-manager", "ensogl-dynamic-assets", @@ -2629,6 +2630,19 @@ dependencies = [ "ensogl-text", ] +[[package]] +name = "ensogl-breadcrumbs" +version = "0.1.0" +dependencies = [ + "enso-frp", + "ensogl-core", + "ensogl-derive-theme", + "ensogl-grid-view", + "ensogl-hardcoded-theme", + "ensogl-icons", + "ensogl-text", +] + [[package]] name = "ensogl-button" version = "0.1.0" @@ -4328,11 +4342,11 @@ dependencies = [ "enso-frp", "enso-prelude", "ensogl", + "ensogl-breadcrumbs", "ensogl-gui-component", "ensogl-hardcoded-theme", "ensogl-text", "ide-view-component-list-panel", - "ide-view-component-list-panel-breadcrumbs", "ide-view-documentation", "ide-view-graph-editor", ] @@ -4357,19 +4371,6 @@ dependencies = [ "ordered-float", ] -[[package]] -name = "ide-view-component-list-panel-breadcrumbs" -version = "0.1.0" -dependencies = [ - "enso-frp", - "ensogl-core", - "ensogl-derive-theme", - "ensogl-grid-view", - "ensogl-hardcoded-theme", - "ensogl-icons", - "ensogl-text", -] - [[package]] name = "ide-view-component-list-panel-grid" version = "0.1.0" @@ -4398,11 +4399,11 @@ dependencies = [ "enso-profiler", "enso-suggestion-database", "ensogl", + "ensogl-breadcrumbs", "ensogl-component", "ensogl-hardcoded-theme", "horrorshow", "ide-ci", - "ide-view-component-list-panel-breadcrumbs", "ide-view-graph-editor", "serde_json", "tokio", diff --git a/app/gui/Cargo.toml b/app/gui/Cargo.toml index b3c963f4b62..c5a1e8307c0 100644 --- a/app/gui/Cargo.toml +++ b/app/gui/Cargo.toml @@ -33,6 +33,7 @@ ensogl-dynamic-assets = { path = "../../lib/rust/ensogl/component/dynamic-assets ensogl-text-msdf = { path = "../../lib/rust/ensogl/component/text/src/font/msdf" } ensogl-hardcoded-theme = { path = "../../lib/rust/ensogl/app/theme/hardcoded" } ensogl-drop-manager = { path = "../../lib/rust/ensogl/component/drop-manager" } +ensogl-breadcrumbs = { path = "../../lib/rust/ensogl/component/breadcrumbs" } fuzzly = { path = "../../lib/rust/fuzzly" } ast = { path = "language/ast/impl" } parser = { path = "language/parser" } diff --git a/app/gui/src/controller/searcher.rs b/app/gui/src/controller/searcher.rs index e3999fda874..8a183dd84e9 100644 --- a/app/gui/src/controller/searcher.rs +++ b/app/gui/src/controller/searcher.rs @@ -5,6 +5,7 @@ use crate::prelude::*; use crate::controller::graph::ImportType; use crate::controller::graph::RequiredImport; +use crate::controller::searcher::breadcrumbs::BreadcrumbEntry; use crate::model::execution_context::GroupQualifiedName; use crate::model::module::NodeEditStatus; use crate::model::suggestion_database; @@ -354,16 +355,7 @@ impl Searcher { } /// Enter the specified module. The displayed content of the browser will be updated. - pub fn enter_entry(&self, entry: usize) -> FallibleResult { - let id = { - let data = self.data.borrow(); - let error = || NoSuchComponent { index: entry }; - let component = data.components.displayed().get(entry).ok_or_else(error)?; - component.id().ok_or(NotEnterable { index: entry })? - }; - let bc_builder = breadcrumbs::Builder::new(&self.database); - let breadcrumbs = bc_builder.build(id); - self.breadcrumbs.set_content(breadcrumbs); + pub fn enter_entry(&self, _entry: usize) -> FallibleResult { self.reload_list(); Ok(()) } @@ -374,11 +366,43 @@ impl Searcher { self.breadcrumbs.names() } - /// Select the breadcrumb with the index [`id`]. The displayed content of the browser will be - /// updated. + /// Set the selected breadcrumb. The `id` is the index of the breadcrumb from left to right. pub fn select_breadcrumb(&self, id: usize) { self.breadcrumbs.select(id); - self.reload_list(); + } + + /// Set the breadcrumbs to match the component at the given `index`. The index refers to the + /// displayed list of components. Returns the full breadcrumb for the entry, if there is one. + pub fn update_breadcrumbs(&self, index: usize) -> Option> { + let data = self.data.borrow(); + if let Some(component) = data.components.displayed().get(index) { + if let Some(id) = component.id() { + let bc_builder = breadcrumbs::Builder::new(&self.database); + let breadcrumbs = bc_builder.build(id).collect_vec(); + assert!(breadcrumbs.iter().all(|e| self.database.lookup(e.id()).is_ok())); + self.breadcrumbs.set_content(breadcrumbs.clone().into_iter()); + Some(breadcrumbs) + } else { + warn!( + "Cannot update breadcrumbs with component that has no suggestion database \ + entry. Invalid component: {:?}", + component + ); + None + } + } else { + warn!("Update breadcrumbs called with invalid index: {}", index); + None + } + } + + /// Return the documentation for the breadcrumb. + pub fn documentation_for_selected_breadcrumb(&self) -> Option { + let selected = self.breadcrumbs.selected(); + let component = selected?; + assert!(self.database.lookup(component).is_ok()); + let docs = self.database.documentation_for_entry(component); + Some(docs) } /// Set the Searcher Input. @@ -1217,30 +1241,6 @@ pub mod test { assert_eq!(notification, Some(Notification::NewComponentList)); } - #[test] - fn entering_module() { - let mut fixture = - Fixture::new_custom(suggestion_database_with_mock_entries, |data, client| { - data.expect_completion(client, None, &(0..11).collect_vec()); - data.expect_completion(client, None, &(0..11).collect_vec()); - }); - - let searcher = &fixture.searcher; - searcher.reload_list(); - fixture.test.run_until_stalled(); - // There are two virtual entries and two top-modules. - assert_eq!(searcher.components().displayed().len(), 4); - - let mut subscriber = searcher.subscribe(); - searcher.enter_entry(3).expect("Entering entry failed"); - fixture.test.run_until_stalled(); - let list = searcher.components(); - assert_eq!(list.displayed().len(), 1); - assert_eq!(list.displayed()[0].suggestion, fixture.test_method_3_suggestion()); - let notification = subscriber.next().boxed_local().expect_ready(); - assert_eq!(notification, Some(Notification::NewComponentList)); - } - #[test] fn picked_completions_list_maintaining() { let fixture = Fixture::new_custom(suggestion_database_with_mock_entries, |data, client| { diff --git a/app/gui/src/controller/searcher/breadcrumbs.rs b/app/gui/src/controller/searcher/breadcrumbs.rs index 4b06897acc9..b126dcefc87 100644 --- a/app/gui/src/controller/searcher/breadcrumbs.rs +++ b/app/gui/src/controller/searcher/breadcrumbs.rs @@ -6,6 +6,7 @@ use crate::model::suggestion_database; use double_representation::name::QualifiedName; use double_representation::name::QualifiedNameRef; +use ensogl_icons::icon; use model::suggestion_database::Entry; @@ -32,9 +33,11 @@ impl Breadcrumbs { /// Set the list of breadcrumbs to be displayed in the breadcrumbs panel. pub fn set_content(&self, breadcrumbs: impl Iterator) { + let breadcrumbs: Vec<_> = breadcrumbs.collect(); let mut borrowed = self.list.borrow_mut(); - *borrowed = breadcrumbs.collect(); - self.select(borrowed.len()); + *borrowed = breadcrumbs; + let len = borrowed.len(); + self.select(len.saturating_sub(1)); } /// A list of breadcrumbs' text labels to be displayed in the panel. @@ -60,12 +63,8 @@ impl Breadcrumbs { /// Returns a currently selected breadcrumb id. Returns [`None`] if the top level breadcrumb /// is selected. pub fn selected(&self) -> Option { - if self.is_top_module() { - None - } else { - let index = self.selected.get(); - self.list.borrow().get(index - 1).map(BreadcrumbEntry::id) - } + let index = self.selected.get(); + self.list.borrow().get(index).map(BreadcrumbEntry::id) } } @@ -81,6 +80,7 @@ pub struct BreadcrumbEntry { displayed_name: ImString, component_id: suggestion_database::entry::Id, qualified_name: QualifiedName, + icon: Option, } impl BreadcrumbEntry { @@ -98,18 +98,33 @@ impl BreadcrumbEntry { pub fn qualified_name(&self) -> &QualifiedName { &self.qualified_name } + + /// An icon of the entry. + pub fn icon(&self) -> Option { + self.icon + } + + /// Return a [`ensogl_breadcrumbs::Breadcrumb`] with the entries name and icon. + pub fn view_without_icon(&self) -> ensogl_breadcrumbs::Breadcrumb { + ensogl_breadcrumbs::Breadcrumb::new(self.name().as_str(), None) + } + + /// Return a [`ensogl_breadcrumbs::Breadcrumb`] with the entries name but no icon. + pub fn view_with_icon(&self) -> ensogl_breadcrumbs::Breadcrumb { + ensogl_breadcrumbs::Breadcrumb::new(self.name().as_str(), self.icon()) + } } impl From<(suggestion_database::entry::Id, Rc)> for BreadcrumbEntry { fn from((component_id, entry): (suggestion_database::entry::Id, Rc)) -> Self { let qualified_name = entry.qualified_name(); let displayed_name = entry.name.clone(); - BreadcrumbEntry { displayed_name, component_id, qualified_name } + let icon = Some(entry.as_ref().icon()); + BreadcrumbEntry { displayed_name, component_id, qualified_name, icon } } } - // =============== // === Builder === // =============== diff --git a/app/gui/src/presenter/searcher/component_browser.rs b/app/gui/src/presenter/searcher/component_browser.rs index 10778c85acb..da1ff878c50 100644 --- a/app/gui/src/presenter/searcher/component_browser.rs +++ b/app/gui/src/presenter/searcher/component_browser.rs @@ -86,10 +86,7 @@ impl Model { ) -> Option<(ViewNodeId, text::Range, ImString)> { let new_code = self.controller.use_suggestion_by_index(id); match new_code { - Ok(text::Change { range, text }) => { - self.update_breadcrumbs(); - Some((self.input_view, range, text.into())) - } + Ok(text::Change { range, text }) => Some((self.input_view, range, text.into())), Err(err) => { error!("Error while applying suggestion: {err}."); None @@ -101,20 +98,23 @@ impl Model { self.controller.select_breadcrumb(id); } - fn update_breadcrumbs(&self) { - let names = self.controller.breadcrumbs().into_iter(); - let browser = &self.view; - // We only update the breadcrumbs starting from the second element because the first - // one is reserved as a section name. - let from = 1; - let breadcrumbs_from = (names.map(Into::into).collect(), from); - browser.model().documentation.breadcrumbs.set_entries_from(breadcrumbs_from); + fn module_entered(&self, entry: component_grid::EntryId) { + if let Err(error) = self.controller.enter_entry(entry) { + error!("Failed to enter entry in Component Browser: {error}") + } } - fn module_entered(&self, entry: component_grid::EntryId) { - match self.controller.enter_entry(entry) { - Ok(()) => self.update_breadcrumbs(), - Err(error) => error!("Failed to enter entry in Component Browser: {error}"), + fn update_breadcrumbs(&self, target_entry: component_grid::EntryId) { + let breadcrumbs = self.controller.update_breadcrumbs(target_entry); + if let Some(breadcrumbs) = breadcrumbs { + let browser = &self.view; + let breadcrumbs_count = breadcrumbs.len(); + let without_icon = + breadcrumbs[0..breadcrumbs_count - 1].iter().map(|crumb| crumb.view_without_icon()); + let with_icon = + breadcrumbs[breadcrumbs_count - 1..].iter().map(|crumb| crumb.view_with_icon()); + let all = without_icon.chain(with_icon).collect_vec(); + browser.model().documentation.breadcrumbs.set_entries(all); } } @@ -138,9 +138,19 @@ impl Model { self.controller.documentation_for_entry(id) } + fn docs_for_breadcrumb(&self) -> Option { + self.controller.documentation_for_selected_breadcrumb() + } + fn should_select_first_entry(&self) -> bool { self.controller.is_filtering() || self.controller.is_input_empty() } + + fn on_entry_for_docs_selected(&self, id: Option) { + if let Some(id) = id { + self.update_breadcrumbs(id); + } + } } /// The Searcher presenter, synchronizing state between searcher view and searcher controller. @@ -267,12 +277,17 @@ impl ComponentBrowserSearcher { docs <- docs_params.filter_map(f!([model]((_, entry)) { entry.map(|entry_id| model.documentation_of_component(entry_id)) })); + docs_from_breadcrumbs <- breadcrumbs.selected.map(f!((selected){ + model.breadcrumb_selected(*selected); + model.docs_for_breadcrumb() + })).unwrap(); + docs <- any(docs,docs_from_breadcrumbs); documentation.frp.display_documentation <+ docs; + eval grid.active ((entry) model.on_entry_for_docs_selected(*entry)); eval_ grid.suggestion_accepted([]analytics::remote_log_event("component_browser::suggestion_accepted")); eval grid.active((entry) model.suggestion_selected(*entry)); eval grid.module_entered((id) model.module_entered(*id)); - eval breadcrumbs.selected((id) model.breadcrumb_selected(*id)); } let weak_model = Rc::downgrade(&model); diff --git a/app/gui/suggestion-database/src/documentation_ir.rs b/app/gui/suggestion-database/src/documentation_ir.rs index 3a0c4e3d6c8..f269628211c 100644 --- a/app/gui/suggestion-database/src/documentation_ir.rs +++ b/app/gui/suggestion-database/src/documentation_ir.rs @@ -71,7 +71,7 @@ impl Default for EntryDocumentation { pub struct LinkedDocPage { /// The name of the liked entry. It is used to produce a unique ID for the link. pub name: Rc, - /// The intermediate reprentation of the linked entry's documentation. + /// The intermediate representation of the linked entry's documentation. pub page: EntryDocumentation, } diff --git a/app/gui/suggestion-database/src/entry.rs b/app/gui/suggestion-database/src/entry.rs index 1ffdfbd670a..5358bc3daa8 100644 --- a/app/gui/suggestion-database/src/entry.rs +++ b/app/gui/suggestion-database/src/entry.rs @@ -19,6 +19,7 @@ use enso_doc_parser::doc_sections::HtmlString; use enso_doc_parser::DocSection; use enso_doc_parser::Tag; use enso_text::Location; +use ensogl_icons::icon; use language_server::types::FieldAction; @@ -528,6 +529,17 @@ impl Entry { // === Other Properties === +macro_rules! kind_to_icon { + ([ $( $variant:ident ),* ] $kind:ident) => { + { + use ensogl_icons::icon::Id; + match $kind { + $( Kind::$variant => Id::$variant, )* + } + } + } +} + impl Entry { /// Return the Method Id of suggested method. /// @@ -621,6 +633,14 @@ impl Entry { }) .flatten() } + + /// Returns the icon of the entry. + pub fn icon(&self) -> icon::Id { + let kind = self.kind; + let icon_name = self.icon_name.as_ref(); + let icon = icon_name.and_then(|n| n.to_pascal_case().parse().ok()); + icon.unwrap_or_else(|| for_each_kind_variant!(kind_to_icon(kind))) + } } diff --git a/app/gui/view/component-browser/Cargo.toml b/app/gui/view/component-browser/Cargo.toml index 6c46ebb0a59..1b7c63674d9 100644 --- a/app/gui/view/component-browser/Cargo.toml +++ b/app/gui/view/component-browser/Cargo.toml @@ -13,7 +13,7 @@ ensogl = { path = "../../../../lib/rust/ensogl" } ensogl-gui-component = { path = "../../../../lib/rust/ensogl/component/gui" } ensogl-hardcoded-theme = { path = "../../../../lib/rust/ensogl/app/theme/hardcoded" } enso-prelude = { path = "../../../../lib/rust/prelude" } -ide-view-component-list-panel-breadcrumbs = { path = "../../../../lib/rust/ensogl/component/breadcrumbs" } +ensogl-breadcrumbs = { path = "../../../../lib/rust/ensogl/component/breadcrumbs" } ide-view-component-list-panel = { path = "component-list-panel" } ide-view-documentation = { path = "../documentation" } ide-view-graph-editor = { path = "../graph-editor" } diff --git a/app/gui/view/component-browser/src/lib.rs b/app/gui/view/component-browser/src/lib.rs index 2bec73c56f0..38f9bedbdf2 100644 --- a/app/gui/view/component-browser/src/lib.rs +++ b/app/gui/view/component-browser/src/lib.rs @@ -35,8 +35,8 @@ use ide_view_graph_editor::component::node::HEIGHT as NODE_HEIGHT; // === Export === // ============== +pub use ensogl_breadcrumbs as breadcrumbs; pub use ide_view_component_list_panel as component_list_panel; -pub use ide_view_component_list_panel_breadcrumbs as breadcrumbs; diff --git a/app/gui/view/documentation/Cargo.toml b/app/gui/view/documentation/Cargo.toml index f925a0d0bac..7babdd1383c 100644 --- a/app/gui/view/documentation/Cargo.toml +++ b/app/gui/view/documentation/Cargo.toml @@ -16,7 +16,7 @@ ensogl = { path = "../../../../lib/rust/ensogl" } ensogl-component = { path = "../../../../lib/rust/ensogl/component" } ensogl-hardcoded-theme = { path = "../../../../lib/rust/ensogl/app/theme/hardcoded" } ide-view-graph-editor = { path = "../graph-editor" } -ide-view-component-list-panel-breadcrumbs = { path = "../../../../lib/rust/ensogl/component/breadcrumbs" } +ensogl-breadcrumbs = { path = "../../../../lib/rust/ensogl/component/breadcrumbs" } wasm-bindgen = { workspace = true } serde_json = { workspace = true } horrorshow = "0.8.4" diff --git a/app/gui/view/documentation/src/lib.rs b/app/gui/view/documentation/src/lib.rs index e1da015f97e..2688aa4c623 100644 --- a/app/gui/view/documentation/src/lib.rs +++ b/app/gui/view/documentation/src/lib.rs @@ -44,7 +44,7 @@ use ide_view_graph_editor as graph_editor; pub mod html; -pub use ide_view_component_list_panel_breadcrumbs as breadcrumbs; +pub use ensogl_breadcrumbs as breadcrumbs; @@ -151,8 +151,8 @@ impl Model { fn set_initial_breadcrumbs(&self) { let breadcrumb = breadcrumbs::Breadcrumb::new(INITIAL_SECTION_NAME, None); - self.breadcrumbs.set_entries_from((vec![breadcrumb], 0)); - self.breadcrumbs.show_ellipsis(true); + self.breadcrumbs.set_entries(vec![breadcrumb]); + self.breadcrumbs.show_ellipsis(false); } /// Set size of the documentation view. diff --git a/app/gui/view/examples/component-list-panel-view/src/lib.rs b/app/gui/view/examples/component-list-panel-view/src/lib.rs index 72729639763..98c686cb335 100644 --- a/app/gui/view/examples/component-list-panel-view/src/lib.rs +++ b/app/gui/view/examples/component-list-panel-view/src/lib.rs @@ -145,7 +145,7 @@ pub fn main() { panel.show(); let breadcrumbs = app.new_view::(); - breadcrumbs.set_y(400.0); + breadcrumbs.set_y(500.0); breadcrumbs.frp().set_size(Vector2(500.0, 100.0)); breadcrumbs.set_entries_from(( vec![ diff --git a/lib/rust/ensogl/app/theme/hardcoded/src/lib.rs b/lib/rust/ensogl/app/theme/hardcoded/src/lib.rs index 080d7f967d0..f36cba235d9 100644 --- a/lib/rust/ensogl/app/theme/hardcoded/src/lib.rs +++ b/lib/rust/ensogl/app/theme/hardcoded/src/lib.rs @@ -383,6 +383,8 @@ define_themes! { [light:0, dark:1] text_y_offset = 6.0, 6.0; text_padding_left = 0.0, 0.0; text_size = 11.5, 11.5; + icon_x_offset = 2.0, 2.0; + icon_y_offset = 6.0, 6.0; selected_color = Rgba(1.0, 1.0, 1.0, 1.0), Rgba(1.0, 1.0, 1.0, 1.0); highlight_corners_radius = 15.0, 15.0; greyed_out_color = Rgba(1.0, 1.0, 1.0, 0.15), Rgba(1.0, 1.0, 1.0, 0.15); diff --git a/lib/rust/ensogl/component/breadcrumbs/Cargo.toml b/lib/rust/ensogl/component/breadcrumbs/Cargo.toml index 2db24a089b6..6f83af02937 100644 --- a/lib/rust/ensogl/component/breadcrumbs/Cargo.toml +++ b/lib/rust/ensogl/component/breadcrumbs/Cargo.toml @@ -1,5 +1,5 @@ [package] -name = "ide-view-component-list-panel-breadcrumbs" +name = "ensogl-breadcrumbs" version = "0.1.0" authors = ["Enso Team "] edition = "2021" diff --git a/lib/rust/ensogl/component/breadcrumbs/src/entry.rs b/lib/rust/ensogl/component/breadcrumbs/src/entry.rs index 947e2fda56f..8dfb1cfcd9c 100644 --- a/lib/rust/ensogl/component/breadcrumbs/src/entry.rs +++ b/lib/rust/ensogl/component/breadcrumbs/src/entry.rs @@ -113,6 +113,8 @@ pub struct Style { pub text_y_offset: f32, pub text_padding_left: f32, pub text_size: f32, + pub icon_x_offset: f32, + pub icon_y_offset: f32, pub selected_color: color::Rgba, pub highlight_corners_radius: f32, pub greyed_out_color: color::Rgba, @@ -125,7 +127,7 @@ pub struct Style { /// A model for the entry in the breadcrumbs list. #[allow(missing_docs)] -#[derive(Clone, CloneRef, Debug, Default)] +#[derive(Clone, CloneRef, Debug, Default, PartialEq)] pub enum Model { #[default] Ellipsis, @@ -186,8 +188,8 @@ impl EntryData { let separator = separator::View::new(); let state = default(); let icon: any_icon::View = default(); - icon.set_size((ICON_WIDTH, ICON_WIDTH)); ellipsis.set_size((ellipsis::ICON_WIDTH, ellipsis::ICON_WIDTH)); + icon.set_size((ICON_WIDTH, ICON_WIDTH)); display_object.add_child(&icon); display_object.add_child(&ellipsis); Self { display_object, state, text, ellipsis, separator, icon } @@ -223,7 +225,6 @@ impl EntryData { if let Some(icon) = icon { self.display_object.add_child(&self.icon); self.icon.icon.set(icon.any_cached_shape_location()); - self.text.set_x(ICON_WIDTH); } else { self.icon.unset_parent(); self.text.set_x(0.0); @@ -256,15 +257,22 @@ impl EntryData { } } - fn update_layout(&self, contour: Contour, text_padding: f32, text_y_offset: f32) { + fn update_layout( + &self, + contour: Contour, + text_padding: f32, + text_y_offset: f32, + icon_x_offset: f32, + icon_y_offset: f32, + ) { let size = contour.size; let icon_offset = if self.has_icon() { ICON_WIDTH } else { 0.0 }; self.text.set_xy(Vector2(icon_offset + text_padding - size.x / 2.0, text_y_offset)); self.separator.set_size(Vector2(separator::ICON_WIDTH, size.y)); self.ellipsis.set_size(Vector2(ellipsis::ICON_WIDTH, size.y)); self.icon.set_size(Vector2(ICON_WIDTH, size.y)); - self.icon.set_x(-ICON_WIDTH); - self.icon.set_y(-2.0); + self.icon.set_x(-size.x / 2.0 - icon_x_offset); + self.icon.set_y(-ICON_WIDTH - icon_y_offset); } fn set_default_color(&self, color: color::Lcha) { @@ -372,10 +380,16 @@ impl ensogl_grid_view::Entry for Entry { text_color <- input.set_params.map(|p| p.style.selected_color).cloned_into().on_change(); text_y_offset <- input.set_params.map(|p| p.style.text_y_offset).on_change(); text_size <- input.set_params.map(|p| p.style.text_size).on_change(); + icon_x_offset <- input.set_params.map(|p| p.style.icon_x_offset).on_change(); + icon_y_offset <- input.set_params.map(|p| p.style.icon_y_offset).on_change(); greyed_out_color <- input.set_params.map(|p| p.style.greyed_out_color).cloned_into().on_change(); highlight_corners_radius <- input.set_params.map(|p| p.style.highlight_corners_radius).on_change(); greyed_out_from <- input.set_params.map(|p| p.greyed_out_start).on_change(); transparent_color <- init.constant(color::Lcha::transparent()); + new_model <- input.set_model.on_change(); + + + // === Appearance === col <- input.set_location._1(); should_grey_out <- all_with(&col, &greyed_out_from, @@ -397,7 +411,6 @@ impl ensogl_grid_view::Entry for Entry { size: *size - Vector2(*margin, *margin) * 2.0, corners_radius: 0.0, }); - eval color((c) data.set_default_color(*c)); eval font((f) data.set_font(f.to_string())); eval text_size((s) data.set_default_text_size(*s)); @@ -410,24 +423,35 @@ impl ensogl_grid_view::Entry for Entry { out.hover_highlight_color <+ hover_color; out.selection_highlight_color <+ init.constant(color::Lcha::transparent()); - - // === Override column width === - - // We need to adjust the width of the grid view column depending on the width of - // the entry. - out.override_column_width <+ input.set_model.map2(&text_padding, - f!([data](model, text_padding) { - data.set_model(model); - data.width(*text_padding) - }) - ); // For text entries, we also listen for [`Text::width`] changes. text_width <- data.text.width.filter(f_!(data.is_text_displayed())); entry_width <- text_width.map2(&text_padding, f!((w, o) data.text_width(*w, *o))); out.override_column_width <+ entry_width; - layout <- all(contour, text_padding, text_y_offset, input.set_model); - eval layout ((&(c, to, tyo, _)) data.update_layout(c, to, tyo)); + + // === Layout === + + override_column_width <- new_model.map2(&text_padding, + f!([data](model, text_padding) { + data.set_model(model); + data.width(*text_padding) + }) + ); + + layout <- all6( + &contour, + &text_padding, + &text_y_offset, + &new_model, + &icon_x_offset, + &icon_y_offset); + eval layout ((&(c, to, tyo, _, ix, iy)) data.update_layout(c, to, tyo, ix, iy)); + + // === Override column width === + + // We need to adjust the width of the grid view column depending on the width of + // the entry. + out.override_column_width <+ override_column_width; } init.emit(()); Self { frp, data } diff --git a/lib/rust/ensogl/component/breadcrumbs/src/lib.rs b/lib/rust/ensogl/component/breadcrumbs/src/lib.rs index 08424559d69..154fd4eb0ef 100644 --- a/lib/rust/ensogl/component/breadcrumbs/src/lib.rs +++ b/lib/rust/ensogl/component/breadcrumbs/src/lib.rs @@ -477,6 +477,8 @@ ensogl_core::define_endpoints_2! { push(Breadcrumb), /// Set the displayed breadcrumbs starting from the specific index. set_entries_from((Vec, BreadcrumbId)), + /// Set the displayed breadcrumbs. + set_entries(Vec), /// Set the breadcrumb at a specified index. set_entry((BreadcrumbId, Breadcrumb)), /// Enable or disable displaying of the ellipsis icon at the end of the list. @@ -534,7 +536,10 @@ impl Breadcrumbs { eval_ input.clear(model.clear()); selected <- selected_grid_col.map(|(_, col)| col / 2); eval input.push((b) model.push(b)); - eval input.set_entries_from(((entries, from)) model.set_entries(entries, *from)); + + set_entries_from_zero <- input.set_entries.map(|entries| (entries.clone(), 0)); + set_entries_from <- any(set_entries_from_zero, input.set_entries_from); + eval set_entries_from(((entries, from)) model.set_entries(entries, *from)); eval input.set_entry(((index, entry)) model.set_entry(entry, *index)); out.selected <+ selected;