mirror of
https://github.com/enso-org/enso.git
synced 2024-12-22 23:51:31 +03:00
Sync breadcrumbs and documentation panel. (#7508)
Implements #7310. ![Peek 2023-08-09 16-07](https://github.com/enso-org/enso/assets/1428930/1a244e38-5c34-4c8b-8885-1cf84ac7b6a7)
This commit is contained in:
parent
7a272ec152
commit
7f19b09d13
31
Cargo.lock
generated
31
Cargo.lock
generated
@ -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",
|
||||
|
@ -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" }
|
||||
|
@ -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<Vec<BreadcrumbEntry>> {
|
||||
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<EntryDocumentation> {
|
||||
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| {
|
||||
|
@ -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<Item = BreadcrumbEntry>) {
|
||||
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<suggestion_database::entry::Id> {
|
||||
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<icon::Id>,
|
||||
}
|
||||
|
||||
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<icon::Id> {
|
||||
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<Entry>)> for BreadcrumbEntry {
|
||||
fn from((component_id, entry): (suggestion_database::entry::Id, Rc<Entry>)) -> 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 ===
|
||||
// ===============
|
||||
|
@ -86,10 +86,7 @@ impl Model {
|
||||
) -> Option<(ViewNodeId, text::Range<text::Byte>, 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<EntryDocumentation> {
|
||||
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<component_grid::EntryId>) {
|
||||
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);
|
||||
|
@ -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<QualifiedName>,
|
||||
/// The intermediate reprentation of the linked entry's documentation.
|
||||
/// The intermediate representation of the linked entry's documentation.
|
||||
pub page: EntryDocumentation,
|
||||
}
|
||||
|
||||
|
@ -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)))
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
@ -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" }
|
||||
|
@ -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;
|
||||
|
||||
|
||||
|
||||
|
@ -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"
|
||||
|
@ -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.
|
||||
|
@ -145,7 +145,7 @@ pub fn main() {
|
||||
panel.show();
|
||||
|
||||
let breadcrumbs = app.new_view::<Breadcrumbs>();
|
||||
breadcrumbs.set_y(400.0);
|
||||
breadcrumbs.set_y(500.0);
|
||||
breadcrumbs.frp().set_size(Vector2(500.0, 100.0));
|
||||
breadcrumbs.set_entries_from((
|
||||
vec![
|
||||
|
@ -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);
|
||||
|
@ -1,5 +1,5 @@
|
||||
[package]
|
||||
name = "ide-view-component-list-panel-breadcrumbs"
|
||||
name = "ensogl-breadcrumbs"
|
||||
version = "0.1.0"
|
||||
authors = ["Enso Team <contact@enso.org>"]
|
||||
edition = "2021"
|
||||
|
@ -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 }
|
||||
|
@ -477,6 +477,8 @@ ensogl_core::define_endpoints_2! {
|
||||
push(Breadcrumb),
|
||||
/// Set the displayed breadcrumbs starting from the specific index.
|
||||
set_entries_from((Vec<Breadcrumb>, BreadcrumbId)),
|
||||
/// Set the displayed breadcrumbs.
|
||||
set_entries(Vec<Breadcrumb>),
|
||||
/// 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;
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user