mirror of
https://github.com/enso-org/enso.git
synced 2024-12-22 16:41:45 +03:00
Update component browser design (#3935)
A continuation and replacement for #3832 This PR updates the design of the component browser to match the latest Figma design file. <img width="588" alt="Screenshot 2022-12-02 at 16 52 51" src="https://user-images.githubusercontent.com/6566674/205297344-d8d46e68-8c46-4e5a-b7f5-5e23014df23f.png"> https://user-images.githubusercontent.com/6566674/205297307-659c633c-a977-4c9f-9903-db72958895b7.mp4 # Important Notes - Invalid color of the section navigator highlight is caused by a regression [#183915546](https://www.pivotaltracker.com/story/show/183915546)
This commit is contained in:
parent
768747a55e
commit
410204a3d9
14
Cargo.lock
generated
14
Cargo.lock
generated
@ -1644,6 +1644,7 @@ dependencies = [
|
||||
"ensogl",
|
||||
"ensogl-hardcoded-theme",
|
||||
"ide-view-component-list-panel-grid",
|
||||
"ide-view-component-list-panel-icons",
|
||||
"wasm-bindgen",
|
||||
]
|
||||
|
||||
@ -4063,7 +4064,6 @@ dependencies = [
|
||||
"ensogl-grid-view",
|
||||
"ensogl-gui-component",
|
||||
"ensogl-hardcoded-theme",
|
||||
"ensogl-list-view",
|
||||
"ensogl-scroll-area",
|
||||
"ensogl-selector",
|
||||
"ensogl-shadow",
|
||||
@ -4071,6 +4071,7 @@ dependencies = [
|
||||
"ensogl-tooltip",
|
||||
"ide-view-component-list-panel-breadcrumbs",
|
||||
"ide-view-component-list-panel-grid",
|
||||
"ide-view-component-list-panel-icons",
|
||||
"ordered-float",
|
||||
]
|
||||
|
||||
@ -4080,6 +4081,7 @@ version = "0.1.0"
|
||||
dependencies = [
|
||||
"enso-frp",
|
||||
"ensogl-core",
|
||||
"ensogl-derive-theme",
|
||||
"ensogl-grid-view",
|
||||
"ensogl-hardcoded-theme",
|
||||
"ensogl-text",
|
||||
@ -4095,13 +4097,21 @@ dependencies = [
|
||||
"ensogl-grid-view",
|
||||
"ensogl-gui-component",
|
||||
"ensogl-hardcoded-theme",
|
||||
"ensogl-list-view",
|
||||
"ensogl-shadow",
|
||||
"ensogl-text",
|
||||
"failure",
|
||||
"ide-view-component-list-panel-icons",
|
||||
"num_enum",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ide-view-component-list-panel-icons"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"ensogl-core",
|
||||
"failure",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ide-view-graph-editor"
|
||||
version = "0.1.0"
|
||||
|
@ -12,7 +12,6 @@ enso-frp = { path = "../../../../../lib/rust/frp" }
|
||||
ensogl-core = { path = "../../../../../lib/rust/ensogl/core" }
|
||||
ensogl-gui-component = { path = "../../../../../lib/rust/ensogl/component/gui/" }
|
||||
ensogl-grid-view = { path = "../../../../../lib/rust/ensogl/component/grid-view/" }
|
||||
ensogl-list-view = { path = "../../../../../lib/rust/ensogl/component/list-view/" }
|
||||
ensogl-hardcoded-theme = { path = "../../../../../lib/rust/ensogl/app/theme/hardcoded" }
|
||||
ensogl-derive-theme = { path = "../../../../../lib/rust/ensogl/app/theme/derive" }
|
||||
ensogl-scroll-area = { path = "../../../../../lib/rust/ensogl/component/scroll-area" }
|
||||
@ -22,6 +21,7 @@ ensogl-text = { path = "../../../../../lib/rust/ensogl/component/text" }
|
||||
ensogl-tooltip = { path = "../../../../../lib/rust/ensogl/component/tooltip/" }
|
||||
ide-view-component-list-panel-breadcrumbs = { path = "breadcrumbs" }
|
||||
ide-view-component-list-panel-grid = { path = "grid" }
|
||||
ide-view-component-list-panel-icons = { path = "icons" }
|
||||
ordered-float = "3.0.0"
|
||||
|
||||
[dev-dependencies]
|
||||
|
@ -7,6 +7,7 @@ edition = "2021"
|
||||
[dependencies]
|
||||
enso-frp = { path = "../../../../../../lib/rust/frp" }
|
||||
ensogl-core = { path = "../../../../../../lib/rust/ensogl/core" }
|
||||
ensogl-derive-theme = { path = "../../../../../../lib/rust/ensogl/app/theme/derive" }
|
||||
ensogl-hardcoded-theme = { path = "../../../../../../lib/rust/ensogl/app/theme/hardcoded" }
|
||||
ensogl-text = { path = "../../../../../../lib/rust/ensogl/component/text" }
|
||||
ensogl-grid-view = { path = "../../../../../../lib/rust/ensogl/component/grid-view" }
|
||||
|
@ -10,6 +10,7 @@ use ensogl_core::data::color;
|
||||
use ensogl_core::display;
|
||||
use ensogl_core::display::scene::Layer;
|
||||
use ensogl_core::Animation;
|
||||
use ensogl_derive_theme::FromTheme;
|
||||
use ensogl_grid_view::entry::Contour;
|
||||
use ensogl_grid_view::entry::EntryFrp;
|
||||
use ensogl_grid_view::Col;
|
||||
@ -27,15 +28,20 @@ pub mod separator {
|
||||
use super::*;
|
||||
use std::f32::consts::PI;
|
||||
|
||||
pub const ICON_WIDTH: f32 = 16.0;
|
||||
pub const ICON_WIDTH: f32 = 30.0;
|
||||
|
||||
ensogl_core::shape! {
|
||||
above = [ensogl_grid_view::entry::shape];
|
||||
pointer_events = false;
|
||||
(style: Style, color: Vector4) {
|
||||
(style: Style) {
|
||||
let color = style.get_color(theme::separator::color);
|
||||
let width = style.get_number(theme::separator::width);
|
||||
let height = style.get_number(theme::separator::height);
|
||||
let triangle = Triangle(width, height).rotate((PI/2.0).radians());
|
||||
let offset_x = style.get_number(theme::separator::offset_x).px();
|
||||
let offset_y = style.get_number(theme::separator::offset_y).px();
|
||||
let triangle = triangle.translate_x(offset_x);
|
||||
let triangle = triangle.translate_y(offset_y);
|
||||
let shape = triangle.fill(color);
|
||||
shape.into()
|
||||
}
|
||||
@ -47,21 +53,19 @@ pub mod separator {
|
||||
pub mod ellipsis {
|
||||
use super::*;
|
||||
|
||||
pub const ICON_WIDTH: f32 = 32.0;
|
||||
pub const ICON_WIDTH: f32 = 28.0;
|
||||
|
||||
ensogl_core::shape! {
|
||||
above = [ensogl_grid_view::entry::shape];
|
||||
pointer_events = false;
|
||||
(style: Style, alpha: f32) {
|
||||
(style: Style) {
|
||||
let radius = style.get_number(theme::ellipsis::circles_radius).px();
|
||||
let gap = style.get_number(theme::ellipsis::circles_gap).px();
|
||||
let background_width = style.get_number(theme::ellipsis::background_width);
|
||||
let background_height = style.get_number(theme::ellipsis::background_height);
|
||||
let background_corners_radius = style.get_number(theme::ellipsis::background_corners_radius);
|
||||
let col = style.get_color(theme::ellipsis::circles_color);
|
||||
let circles_color = Var::<color::Rgba>::rgba(col.red,col.green,col.blue,alpha.clone());
|
||||
let bg_col = style.get_color(theme::ellipsis::background_color);
|
||||
let background_color = Var::<color::Rgba>::rgba(bg_col.red,bg_col.green,bg_col.blue,alpha);
|
||||
let circles_color = style.get_color(theme::ellipsis::circles_color);
|
||||
let background_color = style.get_color(theme::ellipsis::background_color);
|
||||
|
||||
let tile_size = radius.clone() * 2.0 + gap;
|
||||
let circles = Circle(radius).repeat((tile_size.clone(), tile_size.clone()));
|
||||
@ -71,6 +75,10 @@ pub mod ellipsis {
|
||||
let background = background.corners_radius(background_corners_radius.px());
|
||||
let background = background.fill(background_color);
|
||||
let shape = background + circles;
|
||||
let offset_x = style.get_number(theme::ellipsis::offset_x).px();
|
||||
let offset_y = style.get_number(theme::ellipsis::offset_y).px();
|
||||
let shape = shape.translate_x(offset_x);
|
||||
let shape = shape.translate_y(offset_y);
|
||||
shape.into()
|
||||
}
|
||||
}
|
||||
@ -78,6 +86,30 @@ pub mod ellipsis {
|
||||
|
||||
|
||||
|
||||
// =============
|
||||
// === Style ===
|
||||
// =============
|
||||
|
||||
/// Stylesheet-defined portion of the entries' parameters.
|
||||
#[allow(missing_docs)]
|
||||
#[derive(Clone, Debug, Default, FromTheme)]
|
||||
#[base_path = "theme::entry"]
|
||||
pub struct Style {
|
||||
/// The margin of the entry's [`Contour`]. The [`Contour`] specifies the size of the
|
||||
/// clickable area of the entry. If the margin is zero, the contour covers the entire entry.
|
||||
pub margin: f32,
|
||||
pub hover_color: color::Rgba,
|
||||
#[theme_path = "theme::entry::font"]
|
||||
pub font_name: ImString,
|
||||
pub text_y_offset: f32,
|
||||
pub text_padding_left: f32,
|
||||
pub text_size: f32,
|
||||
pub selected_color: color::Rgba,
|
||||
pub highlight_corners_radius: f32,
|
||||
pub greyed_out_color: color::Rgba,
|
||||
}
|
||||
|
||||
|
||||
// =============
|
||||
// === Model ===
|
||||
// =============
|
||||
@ -178,25 +210,24 @@ impl EntryData {
|
||||
}
|
||||
}
|
||||
|
||||
fn update_layout(&self, contour: Contour, text_size: text::Size, text_padding: f32) {
|
||||
fn update_layout(&self, contour: Contour, text_padding: f32, text_y_offset: f32) {
|
||||
let size = contour.size;
|
||||
self.text.set_xy(Vector2(text_padding - size.x / 2.0, text_size.value / 2.0));
|
||||
self.separator.size.set(size);
|
||||
self.ellipsis.size.set(size);
|
||||
self.text.set_xy(Vector2(text_padding - size.x / 2.0, text_y_offset));
|
||||
self.separator.size.set(Vector2(separator::ICON_WIDTH, size.y));
|
||||
self.ellipsis.size.set(Vector2(ellipsis::ICON_WIDTH, size.y));
|
||||
}
|
||||
|
||||
fn set_default_color(&self, color: color::Lcha) {
|
||||
self.text.set_property_default(color);
|
||||
self.ellipsis.alpha.set(color.alpha);
|
||||
self.separator.color.set(color::Rgba::from(color).into());
|
||||
}
|
||||
|
||||
fn set_font(&self, font: String) {
|
||||
self.text.set_font(font);
|
||||
}
|
||||
|
||||
fn set_default_text_size(&self, size: text::Size) {
|
||||
self.text.set_property_default(size);
|
||||
fn set_default_text_size(&self, size: f32) {
|
||||
self.text.set_property_default(text::Size::new(size));
|
||||
self.text.set_property_default(text::Weight::Medium);
|
||||
}
|
||||
|
||||
fn is_state_change(&self, model: &Model) -> bool {
|
||||
@ -231,26 +262,19 @@ impl EntryData {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// === Params ===
|
||||
|
||||
/// The style parameters of Breadcrumbs' entries. See [`ensogl_grid_view::Frp::set_entries_params`].
|
||||
#[allow(missing_docs)]
|
||||
#[derive(Clone, Debug, Default)]
|
||||
pub struct Params {
|
||||
/// The margin of the entry's [`Contour`]. The [`Contour`] specifies the size of the
|
||||
/// clickable area of the entry. If the margin is zero, the contour covers the entire entry.
|
||||
pub margin: f32,
|
||||
pub hover_color: color::Lcha,
|
||||
pub font_name: ImString,
|
||||
pub text_padding: f32,
|
||||
pub text_size: text::Size,
|
||||
pub selected_color: color::Lcha,
|
||||
pub highlight_corners_radius: f32,
|
||||
pub greyed_out_color: color::Lcha,
|
||||
pub style: Style,
|
||||
/// The first greyed out column. All columns to the right will also be greyed out.
|
||||
pub greyed_out_start: Option<Col>,
|
||||
pub greyed_out_start: Option<Col>,
|
||||
}
|
||||
|
||||
|
||||
// === Entry ===
|
||||
|
||||
/// A Breadcrumbs entry.
|
||||
@ -279,15 +303,16 @@ impl ensogl_grid_view::Entry for Entry {
|
||||
enso_frp::extend! { network
|
||||
init <- source_();
|
||||
size <- input.set_size.on_change();
|
||||
margin <- input.set_params.map(|p| p.margin).on_change();
|
||||
hover_color <- input.set_params.map(|p| p.hover_color).on_change();
|
||||
font <- input.set_params.map(|p| p.font_name.clone_ref()).on_change();
|
||||
text_padding <- input.set_params.map(|p| p.text_padding).on_change();
|
||||
text_color <- input.set_params.map(|p| p.selected_color).on_change();
|
||||
text_size <- input.set_params.map(|p| p.text_size).on_change();
|
||||
greyed_out_color <- input.set_params.map(|p| p.greyed_out_color).on_change();
|
||||
margin <- input.set_params.map(|p| p.style.margin).on_change();
|
||||
hover_color <- input.set_params.map(|p| p.style.hover_color).cloned_into().on_change();
|
||||
font <- input.set_params.map(|p| p.style.font_name.clone_ref()).on_change();
|
||||
text_padding <- input.set_params.map(|p| p.style.text_padding_left).on_change();
|
||||
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();
|
||||
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();
|
||||
highlight_corners_radius <- input.set_params.map(|p| p.highlight_corners_radius).on_change();
|
||||
transparent_color <- init.constant(color::Lcha::transparent());
|
||||
|
||||
col <- input.set_location._1();
|
||||
@ -310,8 +335,8 @@ impl ensogl_grid_view::Entry for Entry {
|
||||
size: *size - Vector2(*margin, *margin) * 2.0,
|
||||
corners_radius: 0.0,
|
||||
});
|
||||
layout <- all(contour, text_size, text_padding);
|
||||
eval layout ((&(c, ts, to)) data.update_layout(c, ts, to));
|
||||
layout <- all(contour, text_padding, text_y_offset);
|
||||
eval layout ((&(c, to, tyo)) data.update_layout(c, to, tyo));
|
||||
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));
|
||||
@ -335,8 +360,8 @@ impl ensogl_grid_view::Entry for Entry {
|
||||
data.width(*text_padding)
|
||||
})
|
||||
);
|
||||
text_width <- data.text.width.filter(f_!(data.is_text_displayed()));
|
||||
// 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;
|
||||
}
|
||||
|
@ -52,7 +52,6 @@ use ensogl_core::Animation;
|
||||
use ensogl_grid_view as grid_view;
|
||||
use ensogl_grid_view::Viewport;
|
||||
use ensogl_hardcoded_theme::application::component_browser as component_browser_theme;
|
||||
use ensogl_text as text;
|
||||
use entry::Entry;
|
||||
use grid_view::Col;
|
||||
|
||||
@ -175,57 +174,17 @@ impl Model {
|
||||
);
|
||||
grid.model_for_entry <+ requested_entry;
|
||||
}
|
||||
let style = StyleWatchFrp::new(&app.display.default_scene.style_sheet);
|
||||
let params = Self::params_from_style(&style, &network, init.clone_ref());
|
||||
let style_frp = StyleWatchFrp::new(&app.display.default_scene.style_sheet);
|
||||
let style = entry::Style::from_theme(&network, &style_frp);
|
||||
frp::extend! { network
|
||||
params <- style.update.map(|s| entry::Params { style: s.clone(), greyed_out_start: None });
|
||||
grid.set_entries_params <+ params;
|
||||
}
|
||||
init.emit(());
|
||||
style.init.emit(());
|
||||
Self { display_object, grid, entries, network, mask, show_ellipsis }
|
||||
}
|
||||
|
||||
/// Prepare an [`frp::Sampler`] that emits the parameters for the grid view's entries on
|
||||
/// style sheet changes.
|
||||
fn params_from_style(
|
||||
style: &StyleWatchFrp,
|
||||
network: &frp::Network,
|
||||
init: frp::Source<()>,
|
||||
) -> frp::Sampler<entry::Params> {
|
||||
frp::extend! { network
|
||||
let margin = style.get_number(theme::entry::margin);
|
||||
let hover_color = style.get_color(theme::entry::hover_color);
|
||||
let font = style.get_text(theme::entry::font);
|
||||
let text_padding = style.get_number(theme::entry::text_padding_left);
|
||||
let text_size = style.get_number(theme::entry::text_size);
|
||||
let selected_color = style.get_color(theme::entry::selected_color);
|
||||
let highlight_corners_radius = style.get_number(theme::entry::highlight_corners_radius);
|
||||
let greyed_out_color = style.get_color(theme::entry::greyed_out_color);
|
||||
greyed_out_start <- init.constant(None);
|
||||
text_params <- all4(&init, &text_padding,&text_size,&font);
|
||||
colors <- all4(&init, &hover_color,&selected_color,&greyed_out_color);
|
||||
params <- all_with6(&init,&margin,&text_params,&colors,&highlight_corners_radius,
|
||||
&greyed_out_start,
|
||||
|_,&margin,text_params,colors,&highlight_corners_radius,&greyed_out_start| {
|
||||
let (_, text_padding,text_size,font) = text_params;
|
||||
let (_, hover_color,selected_color,greyed_out_color) = colors;
|
||||
entry::Params {
|
||||
margin,
|
||||
text_padding: *text_padding,
|
||||
text_size: text::Size::from(*text_size),
|
||||
hover_color: hover_color.into(),
|
||||
font_name: font.clone(),
|
||||
selected_color: selected_color.into(),
|
||||
highlight_corners_radius,
|
||||
greyed_out_color: greyed_out_color.into(),
|
||||
greyed_out_start
|
||||
}
|
||||
}
|
||||
);
|
||||
params_sampler <- params.sampler();
|
||||
}
|
||||
params_sampler
|
||||
}
|
||||
|
||||
|
||||
fn set_layers(&self, layers: Layers) {
|
||||
layers.mask.add(&self.mask);
|
||||
@ -498,8 +457,8 @@ impl Breadcrumbs {
|
||||
eval input.set_entry(((index, entry)) model.set_entry(entry, *index));
|
||||
out.selected <+ selected;
|
||||
|
||||
scroll_anim.target <+ all_with3(&model.grid.content_size, &input.set_size, &model.grid
|
||||
.entry_selected,
|
||||
scroll_anim.target <+ all_with3(
|
||||
&model.grid.content_size, &input.set_size, &model.grid.entry_selected,
|
||||
f!((content_size, size, _) {
|
||||
model.update_layout(*content_size, *size);
|
||||
model.offset(*content_size, *size)
|
||||
|
@ -11,8 +11,8 @@ ensogl-derive-theme = { path = "../../../../../../lib/rust/ensogl/app/theme/deri
|
||||
ensogl-hardcoded-theme = { path = "../../../../../../lib/rust/ensogl/app/theme/hardcoded" }
|
||||
ensogl-text = { path = "../../../../../../lib/rust/ensogl/component/text" }
|
||||
ensogl-grid-view = { path = "../../../../../../lib/rust/ensogl/component/grid-view" }
|
||||
ensogl-list-view = { path = "../../../../../../lib/rust/ensogl/component/list-view" }
|
||||
ensogl-gui-component = { path = "../../../../../../lib/rust/ensogl/component/gui" }
|
||||
ensogl-shadow = { version = "0.1.0", path = "../../../../../../lib/rust/ensogl/component/shadow" }
|
||||
ide-view-component-list-panel-icons = { path = "../icons" }
|
||||
failure = "0.1.8"
|
||||
num_enum = "0.5.1"
|
||||
|
@ -5,8 +5,8 @@ use ensogl_core::display::shape::*;
|
||||
|
||||
use crate::content::GroupId;
|
||||
use crate::content::SectionId;
|
||||
use crate::entry::style::ColorIntensities;
|
||||
use crate::entry::style::Colors;
|
||||
use crate::entry::style::ResolvedColors;
|
||||
use crate::GroupColors;
|
||||
use crate::Style as GridStyle;
|
||||
|
||||
@ -196,11 +196,11 @@ impl DimmedGroups {
|
||||
#[allow(missing_docs)]
|
||||
#[derive(Clone, Debug, Default)]
|
||||
pub struct Params {
|
||||
pub style: Style,
|
||||
pub grid_style: GridStyle,
|
||||
pub color_intensities: ColorIntensities,
|
||||
pub group_colors: GroupColors,
|
||||
pub dimmed_groups: DimmedGroups,
|
||||
pub style: Style,
|
||||
pub grid_style: GridStyle,
|
||||
pub colors: Colors,
|
||||
pub group_colors: GroupColors,
|
||||
pub dimmed_groups: DimmedGroups,
|
||||
}
|
||||
|
||||
|
||||
@ -216,8 +216,7 @@ pub struct Params {
|
||||
#[derive(Debug)]
|
||||
struct CurrentIcon {
|
||||
display_object: display::object::Instance,
|
||||
vivid_color: color::Lcha,
|
||||
dull_color: color::Lcha,
|
||||
color: color::Lcha,
|
||||
shape: Option<icon::Any>,
|
||||
id: Option<icon::Id>,
|
||||
}
|
||||
@ -226,8 +225,7 @@ impl Default for CurrentIcon {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
display_object: display::object::Instance::new(),
|
||||
vivid_color: default(),
|
||||
dull_color: default(),
|
||||
color: default(),
|
||||
shape: default(),
|
||||
id: default(),
|
||||
}
|
||||
@ -240,8 +238,7 @@ impl CurrentIcon {
|
||||
self.id = new_icon;
|
||||
if let Some(icon_id) = new_icon {
|
||||
let shape = icon_id.create_shape(Vector2(icon::SIZE, icon::SIZE));
|
||||
shape.set_vivid_color(color::Rgba::from(self.vivid_color).into());
|
||||
shape.set_dull_color(color::Rgba::from(self.dull_color).into());
|
||||
shape.set_color(color::Rgba::from(self.color).into());
|
||||
self.display_object.add_child(&shape);
|
||||
self.shape = Some(shape);
|
||||
} else {
|
||||
@ -250,17 +247,10 @@ impl CurrentIcon {
|
||||
}
|
||||
}
|
||||
|
||||
fn set_vivid_color(&mut self, color: color::Lcha) {
|
||||
self.vivid_color = color;
|
||||
fn set_color(&mut self, color: color::Lcha) {
|
||||
self.color = color;
|
||||
if let Some(shape) = &self.shape {
|
||||
shape.set_vivid_color(color::Rgba::from(color).into());
|
||||
}
|
||||
}
|
||||
|
||||
fn set_dull_color(&mut self, color: color::Lcha) {
|
||||
self.dull_color = color;
|
||||
if let Some(shape) = &self.shape {
|
||||
shape.set_dull_color(color::Rgba::from(color).into());
|
||||
shape.set_color(color::Rgba::from(color).into());
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -326,26 +316,30 @@ impl Data {
|
||||
Kind::LocalScopeEntry { .. } => entry_size.x,
|
||||
_ => grid_style.column_width(),
|
||||
};
|
||||
let bg_height = entry_size.y - gap_over_header + overlap;
|
||||
let bg_height = entry_size.y + overlap;
|
||||
// See comment in [`Self::update_shadow`] method.
|
||||
let shadow_addition = self.background.size.get().y - self.background.height.get();
|
||||
let bg_sprite_height = bg_height + shadow_addition;
|
||||
let bg_y = -gap_over_header / 2.0 + overlap / 2.0 + local_scope_offset;
|
||||
let bg_y = -gap_over_header + overlap / 2.0 + local_scope_offset;
|
||||
self.background.set_y(bg_y);
|
||||
self.background.size.set(Vector2(bg_width, bg_sprite_height));
|
||||
self.background.height.set(bg_height);
|
||||
let left = -entry_size.x / 2.0 + style.padding;
|
||||
let width = grid_style.column_width();
|
||||
let left = -width / 2.0 + style.padding;
|
||||
let icon_x = left + style.icon_size / 2.0;
|
||||
let icon_y = local_scope_offset;
|
||||
self.icon.borrow().set_xy(Vector2(icon_x, icon_y));
|
||||
let text_y_offset = match kind {
|
||||
Kind::Header => style.text_y_offset_header,
|
||||
_ => style.text_y_offset,
|
||||
};
|
||||
let text_x = Self::text_x_position(kind, style, grid_style);
|
||||
let text_y = style.text_size / 2.0 + local_scope_offset;
|
||||
let text_y = text_y_offset + local_scope_offset;
|
||||
self.label.set_xy(Vector2(text_x, text_y));
|
||||
}
|
||||
|
||||
fn contour(kind: Kind, grid_style: &GridStyle, entry_size: Vector2) -> Contour {
|
||||
let optional_gap = if kind == Kind::Header { grid_style.column_gap } else { 0.0 };
|
||||
let height = entry_size.y - optional_gap;
|
||||
fn contour(grid_style: &GridStyle, entry_size: Vector2) -> Contour {
|
||||
let height = entry_size.y;
|
||||
Contour::rectangular(Vector2(grid_style.column_width(), height))
|
||||
}
|
||||
|
||||
@ -356,7 +350,7 @@ impl Data {
|
||||
|
||||
fn contour_offset(kind: Kind, grid_style: &GridStyle) -> Vector2 {
|
||||
let y = match kind {
|
||||
Kind::Header => -grid_style.column_gap / 2.0,
|
||||
Kind::Header => -grid_style.column_gap,
|
||||
Kind::LocalScopeEntry { .. } => -grid_style.column_gap,
|
||||
_ => 0.0,
|
||||
};
|
||||
@ -372,7 +366,7 @@ impl Data {
|
||||
fn text_x_position(kind: Kind, style: &Style, grid_style: &GridStyle) -> f32 {
|
||||
let left = -grid_style.column_width() / 2.0 + style.padding;
|
||||
if kind == Kind::Header {
|
||||
left
|
||||
left + style.text_x_offset_header
|
||||
} else {
|
||||
left + style.icon_size + style.icon_text_padding
|
||||
}
|
||||
@ -444,7 +438,7 @@ impl grid_view::Entry for View {
|
||||
|
||||
kind <- input.set_model.map(|m| m.kind).on_change();
|
||||
style <- input.set_params.map(|p| p.style.clone()).on_change();
|
||||
color_intensities <- input.set_params.map(|p| p.color_intensities).on_change();
|
||||
colors <- input.set_params.map(|p| p.colors).on_change();
|
||||
group_colors <- input.set_params.map(|p| p.group_colors).on_change();
|
||||
grid_style <- input.set_params.map(|p| p.grid_style).on_change();
|
||||
kind_and_style <- all(kind, style, grid_style);
|
||||
@ -452,8 +446,8 @@ impl grid_view::Entry for View {
|
||||
eval layout_data ((((kind, style, grid_style), entry_sz))
|
||||
data.update_layout(*kind, style, grid_style, *entry_sz)
|
||||
);
|
||||
out.contour <+ layout_data.map(|((kind, _, grid_style), entry_sz)| {
|
||||
Data::contour(*kind, grid_style, *entry_sz)
|
||||
out.contour <+ all_with(&grid_style, &input.set_size, |grid_style, entry_sz| {
|
||||
Data::contour(grid_style, *entry_sz)
|
||||
});
|
||||
out.contour_offset <+ kind_and_style.map(|(k, _, gs)| Data::contour_offset(*k, gs));
|
||||
out.highlight_contour <+ out.contour.map2(&style, |c, s| Data::highlight_contour(*c, s));
|
||||
@ -467,11 +461,10 @@ impl grid_view::Entry for View {
|
||||
is_dimmed <- all_with(&input.set_model, &input.set_params, |m,p| {
|
||||
p.dimmed_groups.is_group_dimmed(m.group_id)
|
||||
});
|
||||
let colors = Colors::from_main_color(network, &data.style, &color, &color_intensities, &is_dimmed);
|
||||
let colors = ResolvedColors::from_main_color(network, &data.style, &color, &colors, &is_dimmed);
|
||||
eval colors.background ((c) data.background.color.set(color::Rgba::from(c).into()));
|
||||
data.label.set_property_default <+ colors.text.ref_into_some();
|
||||
eval colors.icon_strong ((c) data.icon.borrow_mut().set_vivid_color(*c));
|
||||
eval colors.icon_weak ((c) data.icon.borrow_mut().set_dull_color(*c));
|
||||
eval colors.icon ((c) data.icon.borrow_mut().set_color(*c));
|
||||
out.hover_highlight_color <+ colors.hover_highlight;
|
||||
// We want to animate only when params changed (the different section is highlighted).
|
||||
// Other case, where entry receives new model with new section means it is reused
|
||||
@ -494,15 +487,20 @@ impl grid_view::Entry for View {
|
||||
data.label.set_view_width <+ max_text_width.some();
|
||||
content_changed <- data.label.content.constant(());
|
||||
style_changed <- style.constant(());
|
||||
highlight_range <= all_with3(
|
||||
&input.set_model,
|
||||
&content_changed,
|
||||
&style_changed,
|
||||
|m, (), ()| m.highlighted.deref().clone()
|
||||
);
|
||||
label_updated <- all3(&input.set_model, &content_changed, &style_changed);
|
||||
highlight_range <= label_updated.map(|(m, (), ())| m.highlighted.deref().clone());
|
||||
data.label.set_property <+ highlight_range.map2(&style, |range, s| {
|
||||
(range.into(), Some(text::SdfWeight::new(s.highlight_bold).into()))
|
||||
});
|
||||
is_header <- label_updated.map(|(m, (), ())| m.kind == Kind::Header);
|
||||
is_not_header <- is_header.on_false();
|
||||
is_header <- is_header.on_true();
|
||||
data.label.set_property <+ is_header.map(|_| {
|
||||
((..).into(), Some(text::Weight::ExtraBold.into()))
|
||||
});
|
||||
data.label.set_property <+ is_not_header.map(|_| {
|
||||
((..).into(), Some(text::Weight::Medium.into()))
|
||||
});
|
||||
data.label.set_property_default <+ style.map(|s| text::Size::new(s.text_size)).cloned_into_some();
|
||||
eval icon ((&icon) data.icon.borrow_mut().update(icon));
|
||||
data.label.set_font <+ style.map(|s| s.font.clone_ref()).on_change();
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -1,94 +0,0 @@
|
||||
//! A module containing the list view entry type which represents an arbitrary icon.
|
||||
|
||||
use ensogl_core::prelude::*;
|
||||
|
||||
use crate::icon;
|
||||
|
||||
use enso_frp as frp;
|
||||
use ensogl_core::application::Application;
|
||||
use ensogl_core::data::color;
|
||||
use ensogl_core::display;
|
||||
use ensogl_core::display::scene::Layer;
|
||||
use ensogl_core::display::style::Path;
|
||||
use ensogl_list_view as list_view;
|
||||
|
||||
|
||||
|
||||
// =================
|
||||
// === IconEntry ===
|
||||
// =================
|
||||
|
||||
/// List view entry type which represents a single icon. We use list view with icons instead of
|
||||
/// three separate buttons to simplify the implementation.
|
||||
#[derive(Debug, Clone, CloneRef)]
|
||||
pub struct Entry {
|
||||
display_object: display::object::Instance,
|
||||
icon: Rc<RefCell<Option<icon::Any>>>,
|
||||
icon_id: Rc<Cell<Option<icon::Id>>>,
|
||||
params: Params,
|
||||
}
|
||||
|
||||
impl display::Object for Entry {
|
||||
fn display_object(&self) -> &display::object::Instance {
|
||||
&self.display_object
|
||||
}
|
||||
}
|
||||
|
||||
impl list_view::Entry for Entry {
|
||||
type Model = icon::Id;
|
||||
type Params = Params;
|
||||
|
||||
fn new(_app: &Application, _style_prefix: &Path, params: &Self::Params) -> Self {
|
||||
let display_object = display::object::Instance::new();
|
||||
let icon: Rc<RefCell<Option<icon::Any>>> = default();
|
||||
let icon_id = default();
|
||||
let network = frp::Network::new("searcher_list_panel::navigator::Icon");
|
||||
frp::extend! { network
|
||||
eval params.vivid_color((&c)
|
||||
icon.borrow().as_ref().map(|icon| icon.set_vivid_color(c.into()))
|
||||
);
|
||||
eval params.dull_color((&c)
|
||||
icon.borrow().as_ref().map(|icon| icon.set_dull_color(c.into()))
|
||||
);
|
||||
}
|
||||
|
||||
Self { display_object, icon, icon_id, params: params.clone_ref() }
|
||||
}
|
||||
|
||||
fn update(&self, model: &Self::Model) {
|
||||
if !self.icon_id.get().contains(model) {
|
||||
let size = Vector2(icon::SIZE, icon::SIZE);
|
||||
let icon = model.create_shape(size);
|
||||
icon.set_vivid_color(self.params.vivid_color.value().into());
|
||||
icon.set_dull_color(self.params.dull_color.value().into());
|
||||
self.display_object.add_child(&icon);
|
||||
*self.icon.borrow_mut() = Some(icon);
|
||||
self.icon_id.set(Some(*model));
|
||||
}
|
||||
}
|
||||
|
||||
fn set_max_width(&self, _max_width_px: f32) {}
|
||||
|
||||
fn set_label_layer(&self, _label_layer: &Layer) {}
|
||||
}
|
||||
|
||||
// === IconParams ===
|
||||
|
||||
/// Entry parameters of the icon.
|
||||
#[derive(Clone, CloneRef, Debug)]
|
||||
pub struct Params {
|
||||
/// Strong (darker, or more contrasting) color parameter.
|
||||
pub vivid_color: frp::Sampler<color::Rgba>,
|
||||
/// Weak (lighter, or less contrasting) color parameter.
|
||||
pub dull_color: frp::Sampler<color::Rgba>,
|
||||
}
|
||||
|
||||
impl Default for Params {
|
||||
fn default() -> Self {
|
||||
let network = frp::Network::new("searcher_list_panel::navigator::Params::default");
|
||||
frp::extend! { network
|
||||
default_color <- source::<color::Rgba>().sampler();
|
||||
}
|
||||
Self { vivid_color: default_color.clone_ref(), dull_color: default_color.clone_ref() }
|
||||
}
|
||||
}
|
@ -5,65 +5,141 @@ use crate::prelude::*;
|
||||
use enso_frp as frp;
|
||||
use ensogl_core::data::color;
|
||||
use ensogl_core::display::shape::StyleWatchFrp;
|
||||
use ensogl_core::Animation;
|
||||
use ensogl_core::display::style::data::DataMatch;
|
||||
use ensogl_derive_theme::FromTheme;
|
||||
use ensogl_hardcoded_theme::application::component_browser::component_list_panel as panel_theme;
|
||||
use ensogl_hardcoded_theme::application::component_browser::component_list_panel::grid as grid_theme;
|
||||
use entry_theme::highlight::selection as selection_theme;
|
||||
use grid_theme::entry as entry_theme;
|
||||
|
||||
|
||||
|
||||
// =============
|
||||
// === Color ===
|
||||
// =============
|
||||
|
||||
/// Color of the different parts of the entry.
|
||||
#[derive(Debug, Clone, Copy, PartialEq)]
|
||||
pub enum Color {
|
||||
/// The final color is "main" color of the component group with the specified alpha value.
|
||||
#[allow(missing_docs)]
|
||||
ComponentGroup { alpha_multiplier: f32 },
|
||||
/// The final color is defined as an arbitrary color in the stylesheet.
|
||||
Arbitrary(color::Lcha),
|
||||
}
|
||||
|
||||
impl Default for Color {
|
||||
fn default() -> Self {
|
||||
Self::Arbitrary(color::Lcha::black())
|
||||
}
|
||||
}
|
||||
|
||||
impl Color {
|
||||
/// Get the final color by either taking the value from [`Self::Arbitrary`] or by applying the
|
||||
/// specified transparency from [`Self::MainColorWithAlpha`] to [`main`] color.
|
||||
fn resolve(&self, main: &color::Lcha) -> color::Lcha {
|
||||
match self {
|
||||
Self::ComponentGroup { alpha_multiplier } => main.multiply_alpha(*alpha_multiplier),
|
||||
Self::Arbitrary(color) => *color,
|
||||
}
|
||||
}
|
||||
|
||||
/// A custom accessor for retrieving the color from the stylesheet using the [`FromTheme`]
|
||||
/// macro. In the stylesheet, the color can be defined as either a `color::Rgba` or a `float`
|
||||
/// value for the mixing coefficient. This accessor produces the corresponding variants of
|
||||
/// [`Color`] or returns a default value ([`Color::MainColorWithAlpha(0.0)`]) if there is no
|
||||
/// such property in the stylesheet.
|
||||
fn accessor<P: Into<ensogl_core::display::style::Path>>(
|
||||
network: &frp::Network,
|
||||
style: &StyleWatchFrp,
|
||||
path: P,
|
||||
) -> frp::Sampler<Self> {
|
||||
let path = path.into();
|
||||
let value = style.get(path.clone());
|
||||
frp::extend! { network
|
||||
init <- source_();
|
||||
color <- value.all_with(&init, move |data, _| {
|
||||
data.color().map(|color| {
|
||||
let color = color::Lcha::from(color);
|
||||
Color::Arbitrary(color)
|
||||
}).unwrap_or_else(|| {
|
||||
let alpha_multiplier = match data.number() {
|
||||
Some(number) => number,
|
||||
None => {
|
||||
error!("Neither color nor alpha defined for {path}.");
|
||||
0.0
|
||||
}
|
||||
};
|
||||
Color::ComponentGroup { alpha_multiplier }
|
||||
})
|
||||
});
|
||||
sampler <- color.sampler();
|
||||
}
|
||||
init.emit(());
|
||||
sampler
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
// =============
|
||||
// === Style ===
|
||||
// =============
|
||||
|
||||
// === Color Intensities ===
|
||||
// === Colors ===
|
||||
|
||||
/// The intensities of various parts of Component Entry view. The actual color is computed by mixing
|
||||
/// the main groups color with the application background - see [`Colors`] for more information.
|
||||
/// The colors of various parts of the Component Entry view. The actual color can be computed by
|
||||
/// modifying the transparency of the "main" color of the component group - see [`Color`] for more
|
||||
/// information.
|
||||
#[allow(missing_docs)]
|
||||
#[derive(Clone, Copy, Debug, Default, PartialEq, FromTheme)]
|
||||
pub struct ColorIntensities {
|
||||
#[theme_path = "entry_theme::text::color_intensity"]
|
||||
pub text: f32,
|
||||
#[theme_path = "entry_theme::background::color_intensity"]
|
||||
pub background: f32,
|
||||
#[theme_path = "entry_theme::highlight::hover::color_intensity"]
|
||||
pub hover_highlight: f32,
|
||||
#[theme_path = "entry_theme::dimmed::color_intensity"]
|
||||
pub dimmed: f32,
|
||||
/// The more contrasting parts of the [icon](crate::icon::Any).
|
||||
#[theme_path = "entry_theme::icon::strong_color_intensity"]
|
||||
pub icon_strong: f32,
|
||||
/// The less contrasting parts of the [icon](crate::icon::Any).
|
||||
#[theme_path = "entry_theme::icon::weak_color_intensity"]
|
||||
pub icon_weak: f32,
|
||||
pub struct Colors {
|
||||
#[theme_path = "entry_theme::text::color"]
|
||||
#[accessor = "Color::accessor"]
|
||||
pub text: Color,
|
||||
#[theme_path = "entry_theme::background::intensity"]
|
||||
pub background_intensity: f32,
|
||||
#[theme_path = "entry_theme::highlight::hover::color"]
|
||||
#[accessor = "Color::accessor"]
|
||||
pub hover_highlight: Color,
|
||||
#[theme_path = "entry_theme::icon::color"]
|
||||
#[accessor = "Color::accessor"]
|
||||
pub icon: Color,
|
||||
/// The "main color" of dimmed component groups. For dimmed component groups,
|
||||
/// [`ResolvedColors`] would use this value instead of "main" color of the component group.
|
||||
#[theme_path = "entry_theme::dimmed"]
|
||||
#[accessor = "Color::accessor"]
|
||||
pub dimmed: Color,
|
||||
}
|
||||
|
||||
/// The intensities of various parts of selected Component Entry view. A subset of
|
||||
/// [`ColorIntensities`], but `FromTheme` derive takes different style's paths, plus unrelated
|
||||
/// The colors of various parts of selected the Component Entry view. A subset of
|
||||
/// [`StyleColors`], but `FromTheme` derive takes different style's paths, plus unrelated
|
||||
/// entries are omitted.
|
||||
#[allow(missing_docs)]
|
||||
#[derive(Clone, Copy, Debug, Default, PartialEq, FromTheme)]
|
||||
pub struct SelectionColorIntensities {
|
||||
#[theme_path = "selection_theme::text::color_intensity"]
|
||||
pub text: f32,
|
||||
#[theme_path = "selection_theme::background::color_intensity"]
|
||||
pub background: f32,
|
||||
/// The more contrasting parts of the [icon](crate::icon::Any).
|
||||
#[theme_path = "selection_theme::icon_strong::color_intensity"]
|
||||
pub icon_strong: f32,
|
||||
/// The less contrasting parts of the [icon](crate::icon::Any).
|
||||
#[theme_path = "selection_theme::icon_weak::color_intensity"]
|
||||
pub icon_weak: f32,
|
||||
pub struct SelectionColors {
|
||||
#[theme_path = "selection_theme::text::color"]
|
||||
#[accessor = "Color::accessor"]
|
||||
pub text: Color,
|
||||
#[theme_path = "selection_theme::background::intensity"]
|
||||
pub background_intensity: f32,
|
||||
#[theme_path = "selection_theme::icon::color"]
|
||||
#[accessor = "Color::accessor"]
|
||||
pub icon: Color,
|
||||
/// The main color of dimmed component groups. Selection is never displayed in a dimmed
|
||||
/// component group. Still, we need to duplicate this parameter to avoid sudden color
|
||||
/// changes while the selection shape is animated and moves through different component
|
||||
/// groups.
|
||||
#[theme_path = "entry_theme::dimmed"]
|
||||
#[accessor = "Color::accessor"]
|
||||
pub dimmed: Color,
|
||||
}
|
||||
|
||||
impl From<SelectionColorIntensities> for ColorIntensities {
|
||||
fn from(selection: SelectionColorIntensities) -> Self {
|
||||
let SelectionColorIntensities { text, background, icon_strong, icon_weak } = selection;
|
||||
let dimmed = 1.0;
|
||||
let hover_highlight = background;
|
||||
Self { text, background, icon_weak, icon_strong, dimmed, hover_highlight }
|
||||
impl From<SelectionColors> for Colors {
|
||||
fn from(selection: SelectionColors) -> Self {
|
||||
let SelectionColors { text, background_intensity, icon, dimmed } = selection;
|
||||
let hover_highlight = default();
|
||||
Self { text, background_intensity, icon, dimmed, hover_highlight }
|
||||
}
|
||||
}
|
||||
|
||||
@ -82,6 +158,12 @@ pub struct Style {
|
||||
pub icon_size: f32,
|
||||
#[theme_path = "entry_theme::text::size"]
|
||||
pub text_size: f32,
|
||||
#[theme_path = "entry_theme::text::y_offset"]
|
||||
pub text_y_offset: f32,
|
||||
#[theme_path = "entry_theme::text::y_offset_header"]
|
||||
pub text_y_offset_header: f32,
|
||||
#[theme_path = "entry_theme::text::x_offset_header"]
|
||||
pub text_x_offset_header: f32,
|
||||
/// The distance between right edge of the icon and left edge of the caption.
|
||||
#[theme_path = "entry_theme::icon::text_padding"]
|
||||
pub icon_text_padding: f32,
|
||||
@ -97,75 +179,73 @@ pub struct Style {
|
||||
|
||||
|
||||
|
||||
// ==============
|
||||
// === Colors ===
|
||||
// ==============
|
||||
// ======================
|
||||
// === ResolvedColors ===
|
||||
// ======================
|
||||
|
||||
/// Colors used in the Component Group Entries.
|
||||
///
|
||||
/// This structure can be created from single "main color" input. Each of these colors will be
|
||||
/// computed by mixing "main color" with application background.
|
||||
///
|
||||
/// `icon_strong` and `icon_weak` parameters represent the more/less contrasting parts of the
|
||||
/// [icon](crate::icon::Any), they do not represent highlighted state of the icon.
|
||||
/// This structure can be created from a single "main color" input. Each of these colors can be
|
||||
/// computed by modifying the transparency of the "main color". See [`Color`] for more information.
|
||||
#[allow(missing_docs)]
|
||||
#[derive(Clone, CloneRef, Debug)]
|
||||
pub struct Colors {
|
||||
pub struct ResolvedColors {
|
||||
pub background: frp::Sampler<color::Lcha>,
|
||||
pub hover_highlight: frp::Sampler<color::Lcha>,
|
||||
pub text: frp::Sampler<color::Lcha>,
|
||||
pub icon_strong: frp::Sampler<color::Lcha>,
|
||||
pub icon_weak: frp::Sampler<color::Lcha>,
|
||||
pub icon: frp::Sampler<color::Lcha>,
|
||||
pub skip_animations: frp::Any,
|
||||
}
|
||||
|
||||
impl Colors {
|
||||
impl ResolvedColors {
|
||||
/// Create color outputs from given main color and style.
|
||||
///
|
||||
/// Each of these colors will be computed by mixing "main color" with application background
|
||||
/// according to the proper field in [`Style::color_intensities`]. The dimming of group is
|
||||
/// animated.
|
||||
/// Each of these colors can be computed by modifying the transparency of the "main color". See
|
||||
/// [`Color`] for more information. All color changes are animated.
|
||||
pub fn from_main_color(
|
||||
network: &frp::Network,
|
||||
style_watch: &StyleWatchFrp,
|
||||
color: &frp::Stream<color::Lcha>,
|
||||
color_intensities: &frp::Stream<ColorIntensities>,
|
||||
main_color: &frp::Stream<color::Lcha>,
|
||||
colors: &frp::Stream<Colors>,
|
||||
is_dimmed: &frp::Stream<bool>,
|
||||
) -> Self {
|
||||
fn mix((c1, c2): &(color::Lcha, color::Lcha), coefficient: &f32) -> color::Lcha {
|
||||
color::mix(*c1, *c2, *coefficient)
|
||||
}
|
||||
let app_bg = style_watch.get_color(ensogl_hardcoded_theme::application::background);
|
||||
let color_intensities = color_intensities.clone_ref();
|
||||
let panel_background = style_watch.get_color(panel_theme::background_color);
|
||||
let colors = colors.clone_ref();
|
||||
let color_anim = color::Animation::new(network);
|
||||
|
||||
let intensity = Animation::new(network);
|
||||
frp::extend! { network
|
||||
init <- source_();
|
||||
|
||||
text_intensity <- color_intensities.map(|c| c.text);
|
||||
bg_intensity <- color_intensities.map(|c| c.background);
|
||||
hover_hg_intensity <- color_intensities.map(|c| c.hover_highlight);
|
||||
dimmed_intensity <- color_intensities.map(|c| c.dimmed);
|
||||
icon_strong_intensity <- color_intensities.map(|c| c.icon_strong);
|
||||
icon_weak_intensity <- color_intensities.map(|c| c.icon_weak);
|
||||
|
||||
one <- init.constant(1.0);
|
||||
let is_dimmed = is_dimmed.clone_ref();
|
||||
intensity.target <+ is_dimmed.switch(&one, &dimmed_intensity);
|
||||
app_bg <- all_with(&app_bg, &init, |col, ()| color::Lcha::from(col));
|
||||
app_bg_and_input <- all(&app_bg, color);
|
||||
main <- app_bg_and_input.all_with(&intensity.value, mix);
|
||||
app_bg_and_main <- all(&app_bg, &main);
|
||||
background <- app_bg_and_main.all_with(&bg_intensity, mix).sampler();
|
||||
hover_highlight <- app_bg_and_main.all_with(&hover_hg_intensity, mix).sampler();
|
||||
text <- app_bg_and_main.all_with(&text_intensity, mix).sampler();
|
||||
icon_weak <- app_bg_and_main.all_with(&icon_weak_intensity, mix).sampler();
|
||||
icon_strong <- app_bg_and_main.all_with(&icon_strong_intensity, mix).sampler();
|
||||
dimmed <- all_with3(&init, main_color, &colors,
|
||||
|_, main, colors| colors.dimmed.resolve(main)
|
||||
);
|
||||
color_anim.target <+ switch(&is_dimmed, main_color, &dimmed);
|
||||
|
||||
// We do not support the semi-transparent background of entries. Because headers share
|
||||
// the same background color; therefore, the semi-transparent header's background
|
||||
// reveals the underlying entries. Instead, we mix the color of the component browser's
|
||||
// background and the main color of the component group.
|
||||
panel_bg <- all_with(&panel_background, &init, |col, ()| color::Lcha::from(col));
|
||||
panel_bg_and_main <- all(&panel_bg, &color_anim.value);
|
||||
bg_intensity <- colors.map(|c| c.background_intensity);
|
||||
background <- all_with(&panel_bg_and_main, &bg_intensity,
|
||||
|(bg, main), intensity| color::mix(*bg, *main, *intensity)
|
||||
).sampler();
|
||||
hover_highlight <- all_with(&color_anim.value, &colors,
|
||||
|main, colors| colors.hover_highlight.resolve(main)
|
||||
).sampler();
|
||||
text <- all_with(&color_anim.value, &colors,
|
||||
|main, colors| colors.text.resolve(main)
|
||||
).sampler();
|
||||
icon <- all_with(&color_anim.value, &colors,
|
||||
|main, colors| colors.icon.resolve(main)
|
||||
).sampler();
|
||||
|
||||
skip_animations <- any(...);
|
||||
intensity.skip <+ skip_animations;
|
||||
color_anim.skip <+ skip_animations;
|
||||
}
|
||||
init.emit(());
|
||||
Self { icon_weak, icon_strong, text, background, hover_highlight, skip_animations }
|
||||
Self { icon, text, background, hover_highlight, skip_animations }
|
||||
}
|
||||
}
|
||||
|
@ -524,10 +524,10 @@ impl Model {
|
||||
|
||||
fn entries_params(
|
||||
&self,
|
||||
(style, entry_style, color_intensities, group_colors): &(
|
||||
(style, entry_style, colors, group_colors): &(
|
||||
Style,
|
||||
entry::Style,
|
||||
entry::style::ColorIntensities,
|
||||
entry::style::Colors,
|
||||
GroupColors,
|
||||
),
|
||||
dimmed_groups: entry::DimmedGroups,
|
||||
@ -536,16 +536,16 @@ impl Model {
|
||||
style: entry_style.clone(),
|
||||
grid_style: *style,
|
||||
group_colors: *group_colors,
|
||||
color_intensities: *color_intensities,
|
||||
colors: *colors,
|
||||
dimmed_groups,
|
||||
}
|
||||
}
|
||||
|
||||
fn selection_entries_params(
|
||||
&self,
|
||||
(base_params, color_intensities): &(entry::Params, entry::style::SelectionColorIntensities),
|
||||
(base_params, colors): &(entry::Params, entry::style::SelectionColors),
|
||||
) -> entry::Params {
|
||||
entry::Params { color_intensities: (*color_intensities).into(), ..base_params.clone() }
|
||||
entry::Params { colors: (*colors).into(), ..base_params.clone() }
|
||||
}
|
||||
|
||||
fn navigation_scroll_margins(
|
||||
@ -599,9 +599,8 @@ impl component::Frp<Model> for Frp {
|
||||
let corners_radius = style_frp.get_number(panel_theme::corners_radius);
|
||||
let style = Style::from_theme(network, style_frp);
|
||||
let entry_style = entry::Style::from_theme(network, style_frp);
|
||||
let color_intensities = entry::style::ColorIntensities::from_theme(network, style_frp);
|
||||
let selection_color_intensities =
|
||||
entry::style::SelectionColorIntensities::from_theme(network, style_frp);
|
||||
let colors = entry::style::Colors::from_theme(network, style_frp);
|
||||
let selection_colors = entry::style::SelectionColors::from_theme(network, style_frp);
|
||||
frp::extend! { network
|
||||
// === Active and Hovered Entry ===
|
||||
|
||||
@ -637,7 +636,7 @@ impl component::Frp<Model> for Frp {
|
||||
group_colors <- all_with(&groups, &local_scope_group, |&((), g0, g1, g2, g3, g4, g5), ls| {
|
||||
GroupColors {
|
||||
variants: [g0, g1, g2, g3, g4, g5].map(color::Lcha::from),
|
||||
local_scope_group: ls.into()
|
||||
local_scope_group: ls.into(),
|
||||
}
|
||||
});
|
||||
|
||||
@ -650,10 +649,10 @@ impl component::Frp<Model> for Frp {
|
||||
None => entry::DimmedGroups::None,
|
||||
});
|
||||
entries_style <-
|
||||
all4(&style.update, &entry_style.update, &color_intensities.update, &group_colors);
|
||||
all4(&style.update, &entry_style.update, &colors.update, &group_colors);
|
||||
entries_params <-
|
||||
all_with(&entries_style, &dimmed_groups, f!((s, d) model.entries_params(s, *d)));
|
||||
selection_entries_style <- all(entries_params, selection_color_intensities.update);
|
||||
selection_entries_style <- all(entries_params, selection_colors.update);
|
||||
selection_entries_params <-
|
||||
selection_entries_style.map(f!((input) model.selection_entries_params(input)));
|
||||
grid_scroll_frp.resize <+ style_and_content_size.map(Model::grid_size);
|
||||
@ -732,8 +731,8 @@ impl component::Frp<Model> for Frp {
|
||||
grid.resize_grid(0, column::COUNT);
|
||||
style.init.emit(());
|
||||
entry_style.init.emit(());
|
||||
color_intensities.init.emit(());
|
||||
selection_color_intensities.init.emit(());
|
||||
colors.init.emit(());
|
||||
selection_colors.init.emit(());
|
||||
}
|
||||
|
||||
fn default_shortcuts() -> Vec<Shortcut> {
|
||||
|
@ -0,0 +1,9 @@
|
||||
[package]
|
||||
name = "ide-view-component-list-panel-icons"
|
||||
version = "0.1.0"
|
||||
authors = ["Enso Team <contact@enso.org>"]
|
||||
edition = "2021"
|
||||
|
||||
[dependencies]
|
||||
ensogl-core = { path = "../../../../../../lib/rust/ensogl/core" }
|
||||
failure = "0.1.8"
|
@ -36,9 +36,9 @@ pub fn grid(stroke_width: f32, cell_size: f32) -> AnyShape {
|
||||
|
||||
/// A cursor shape, looking roughly like a capital "I".
|
||||
pub fn cursor() -> AnyShape {
|
||||
let middle = Rect((1.0.px(), 15.0.px()));
|
||||
let top = Rect((5.0.px(), 1.0.px())).translate_y(7.5.px());
|
||||
let bottom = Rect((5.0.px(), 1.0.px())).translate_y((-7.5).px());
|
||||
let middle = Rect((2.0.px(), 16.0.px()));
|
||||
let top = Rect((6.0.px(), 2.0.px())).translate_y(7.0.px());
|
||||
let bottom = Rect((6.0.px(), 2.0.px())).translate_y((-7.0).px());
|
||||
(middle + top + bottom).into()
|
||||
}
|
||||
|
||||
@ -57,18 +57,24 @@ pub fn arc(outer_radius: f32, stroke_width: f32, start_angle: f32, end_angle: f3
|
||||
(circle * angle).into()
|
||||
}
|
||||
|
||||
/// The shape of a table, given by a grid with size `columns` x `rows`. The stroke width is 1.0 and
|
||||
/// the cell size 4.0. The origin is at the lower left corner.
|
||||
pub fn table(columns: i32, rows: i32) -> AnyShape {
|
||||
const STROKE_WIDTH: f32 = 1.0;
|
||||
const CELL_SIZE: f32 = 4.0;
|
||||
/// An 2d-array of squares of size `cell_size`.
|
||||
pub fn table(columns: i32, rows: i32, cell_size: f32) -> AnyShape {
|
||||
const GAP: f32 = 1.0;
|
||||
|
||||
let width = columns as f32 * CELL_SIZE + STROKE_WIDTH;
|
||||
let height = rows as f32 * CELL_SIZE + STROKE_WIDTH;
|
||||
let bounds =
|
||||
Rect((width.px(), height.px())).translate(((width / 2.0).px(), (height / 2.0).px()));
|
||||
let grid = grid(STROKE_WIDTH, CELL_SIZE);
|
||||
(grid * bounds).into()
|
||||
let cell = Rect((cell_size.px(), cell_size.px()));
|
||||
let table = cell.repeat(((cell_size + GAP).px(), (cell_size + GAP).px()));
|
||||
let table = table.translate_x((-cell_size / 2.0 - GAP).px());
|
||||
let table = table.translate_y((-cell_size / 2.0 - GAP).px());
|
||||
|
||||
let width = (cell_size + GAP) * columns as f32 - GAP;
|
||||
let height = (cell_size + GAP) * rows as f32 - GAP;
|
||||
|
||||
let bounds = Rect((width.px(), height.px()));
|
||||
let bounds = bounds.translate_x((width / 2.0).px());
|
||||
let bounds = bounds.translate_y((height / 2.0).px());
|
||||
|
||||
let shape = table * bounds;
|
||||
shape.into()
|
||||
}
|
||||
|
||||
/// A plus, consisting of two strokes of length `size` and width `stroke_width`, intersecting at the
|
@ -17,8 +17,7 @@
|
||||
/// use ensogl_core::prelude::*;
|
||||
/// use ensogl_core::display::shape::*;
|
||||
/// use ensogl_core::data::color;
|
||||
/// use ide_view_component_list_panel_grid::entry::icon;
|
||||
/// use ide_view_component_list_panel_grid::define_icons;
|
||||
/// use ide_view_component_list_panel_icons::define_icons;
|
||||
///
|
||||
/// define_icons! {
|
||||
/// /// The example of icon.
|
||||
@ -28,7 +27,7 @@
|
||||
/// //
|
||||
/// // `use super::*` import is added silently.
|
||||
/// ensogl_core::shape! {
|
||||
/// (style:Style, vivid_color: Vector4, dull_color: Vector4) {
|
||||
/// (style:Style, color: Vector4) {
|
||||
/// Plane().into()
|
||||
/// }
|
||||
/// }
|
||||
@ -36,8 +35,8 @@
|
||||
///
|
||||
/// pub mod icon2(Icon2) {
|
||||
/// ensogl_core::shape! {
|
||||
/// (style:Style, vivid_color: Vector4, dull_color: Vector4) {
|
||||
/// Plane().fill(vivid_color).into()
|
||||
/// (style:Style, color: Vector4) {
|
||||
/// Plane().fill(color).into()
|
||||
/// }
|
||||
/// }
|
||||
/// }
|
||||
@ -81,30 +80,22 @@ macro_rules! define_icons {
|
||||
|
||||
impl Id {
|
||||
/// Create icon's shape with given size.
|
||||
pub fn create_shape(&self, size: Vector2) -> $crate::entry::icon::Any {
|
||||
pub fn create_shape(&self, size: Vector2) -> $crate::Any {
|
||||
match self {$(
|
||||
Self::$variant => {
|
||||
let view = $name::View::new();
|
||||
view.size.set(size);
|
||||
let vivid_color_fn = Box::new(f!([view]()
|
||||
color::Lcha::from(color::Rgba::from(view.vivid_color.get()))
|
||||
let color_fn = Box::new(f!([view]()
|
||||
color::Lcha::from(color::Rgba::from(view.color.get()))
|
||||
));
|
||||
let dull_color_fn = Box::new(f!([view]()
|
||||
color::Lcha::from(color::Rgba::from(view.dull_color.get()))
|
||||
));
|
||||
let set_vivid_color_fn = Box::new(f!((c)
|
||||
view.vivid_color.set(color::Rgba::from(c).into())
|
||||
));
|
||||
let set_dull_color_fn = Box::new(f!((c)
|
||||
view.dull_color.set(color::Rgba::from(c).into())
|
||||
let set_color_fn = Box::new(f!((c)
|
||||
view.color.set(color::Rgba::from(c).into())
|
||||
));
|
||||
let view = Box::new(view);
|
||||
$crate::entry::icon::Any {
|
||||
$crate::Any {
|
||||
view,
|
||||
vivid_color_fn,
|
||||
dull_color_fn,
|
||||
set_vivid_color_fn,
|
||||
set_dull_color_fn
|
||||
color_fn,
|
||||
set_color_fn,
|
||||
}
|
||||
}
|
||||
)*}
|
||||
@ -124,7 +115,7 @@ macro_rules! define_icons {
|
||||
}
|
||||
|
||||
impl FromStr for Id {
|
||||
type Err = $crate::entry::icon::UnknownIcon;
|
||||
type Err = $crate::UnknownIcon;
|
||||
fn from_str(s: &str) -> Result<Id, Self::Err> {
|
||||
match s {
|
||||
$(stringify!($variant) => Ok(Self::$variant),)*
|
@ -0,0 +1,100 @@
|
||||
// === Standard Linter Configuration ===
|
||||
#![deny(non_ascii_idents)]
|
||||
#![warn(unsafe_code)]
|
||||
#![allow(clippy::bool_to_int_with_if)]
|
||||
#![allow(clippy::let_and_return)]
|
||||
|
||||
use prelude::*;
|
||||
|
||||
use ensogl_core::data::color;
|
||||
use ensogl_core::display;
|
||||
|
||||
|
||||
|
||||
mod prelude {
|
||||
pub use ensogl_core::application::traits::*;
|
||||
pub use ensogl_core::display::shape::*;
|
||||
pub use ensogl_core::prelude::*;
|
||||
}
|
||||
|
||||
pub mod common_part;
|
||||
mod define_macro;
|
||||
|
||||
|
||||
|
||||
// =================
|
||||
// === Constants ===
|
||||
// =================
|
||||
|
||||
/// The width and height of all icons.
|
||||
pub const SIZE: f32 = 16.0;
|
||||
|
||||
/// This constant exists for development purposes only, and is published for debug scene.
|
||||
/// Due to a rendering error, shapes appear too big when the camera is zoomed in very closely.
|
||||
/// (Documented here: https://github.com/enso-org/ide/issues/1698)
|
||||
/// In the user interface, this is not a big problem, since icon are usually shown at lower zoom
|
||||
/// levels. But it is a problem during development of icon when it becomes necessary to inspect
|
||||
/// them closely. In those situations, one can apply `.shrink(0.35)` to shapes to compensate for the
|
||||
/// bug and make them appear at the correct size while the camera is zoomed in. But that work-around
|
||||
/// will make them appear too thin on the default zoom level.
|
||||
///
|
||||
/// To make it easy to turn this shrinking on and off before and after working on icon, we define
|
||||
/// the constant `SHRINK_AMOUNT` and apply `.shrink(SHRINK_AMOUNT.px())` to all icon. In every
|
||||
/// commit, `SHRINK_AMOUNT` should be set to 0.0 to make icon look best in the user interface. But
|
||||
/// during work on the icon, it can temporarily be set to 0.35.
|
||||
pub const SHRINK_AMOUNT: f32 = 0.0;
|
||||
|
||||
|
||||
|
||||
// ==============
|
||||
// === Errors ===
|
||||
// ==============
|
||||
|
||||
/// Error occuring when we try parse string being an invalid icon name to icon Id.
|
||||
#[derive(Clone, Debug, Fail)]
|
||||
#[fail(display = "Unknown icon '{}'.", name)]
|
||||
pub struct UnknownIcon {
|
||||
/// The copied icon name from parsed string.
|
||||
pub name: String,
|
||||
}
|
||||
|
||||
|
||||
|
||||
// ===============
|
||||
// === AnyIcon ===
|
||||
// ===============
|
||||
|
||||
/// One of the icon generated from the [`define_icons`] macro. Returned from `create_shape` method.
|
||||
pub struct Any {
|
||||
/// The underlying icon shape.
|
||||
pub view: Box<dyn display::Object>,
|
||||
/// The primary color of the icon. Secondary colors are calculated by applying transparency to
|
||||
/// the primary color.
|
||||
pub color_fn: Box<dyn Fn() -> color::Lcha>,
|
||||
/// Setter for vivid (darker, or more contrasting) color parameter.
|
||||
pub set_color_fn: Box<dyn Fn(color::Lcha)>,
|
||||
}
|
||||
|
||||
/// See docs of [`Any`] to learn more.
|
||||
#[allow(missing_docs)]
|
||||
impl Any {
|
||||
pub fn color(&self) -> color::Lcha {
|
||||
(self.color_fn)()
|
||||
}
|
||||
|
||||
pub fn set_color(&self, color: color::Lcha) {
|
||||
(self.set_color_fn)(color)
|
||||
}
|
||||
}
|
||||
|
||||
impl Debug for Any {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
write!(f, "Any")
|
||||
}
|
||||
}
|
||||
|
||||
impl display::Object for Any {
|
||||
fn display_object(&self) -> &display::object::Instance {
|
||||
self.view.display_object()
|
||||
}
|
||||
}
|
@ -46,7 +46,6 @@
|
||||
use crate::prelude::*;
|
||||
use ensogl_core::display::shape::*;
|
||||
|
||||
use crate::navigator::navigator_shadow;
|
||||
use crate::navigator::Navigator as SectionNavigator;
|
||||
|
||||
use enso_frp as frp;
|
||||
@ -60,9 +59,9 @@ use ensogl_core::display::navigation::navigator::Navigator;
|
||||
use ensogl_core::display::object::ObjectOps;
|
||||
use ensogl_core::display::shape::StyleWatchFrp;
|
||||
use ensogl_derive_theme::FromTheme;
|
||||
use ensogl_grid_view as grid_view;
|
||||
use ensogl_gui_component::component;
|
||||
use ensogl_hardcoded_theme::application::component_browser::component_list_panel as theme;
|
||||
use ensogl_list_view as list_view;
|
||||
use ensogl_shadow as shadow;
|
||||
|
||||
|
||||
@ -148,7 +147,7 @@ impl AllStyles {
|
||||
fn breadcrumbs_pos(&self) -> Vector2 {
|
||||
let crop_left = self.panel.breadcrumbs_crop_left;
|
||||
let x = -self.grid.width / 2.0 + self.navigator.width / 2.0 + crop_left;
|
||||
let y = self.grid.height / 2.0 + self.panel.menu_height / 2.0 - self.grid.padding;
|
||||
let y = self.size().y / 2.0;
|
||||
Vector2(x, y)
|
||||
}
|
||||
|
||||
@ -179,36 +178,47 @@ mod background {
|
||||
use super::*;
|
||||
|
||||
ensogl_core::shape! {
|
||||
below = [grid::entry::background, list_view::overlay];
|
||||
below = [grid::entry::background, grid_view::entry::overlay, grid_view::selectable::highlight::shape];
|
||||
(style:Style,bg_color:Vector4) {
|
||||
let alpha = Var::<f32>::from(format!("({0}.w)",bg_color));
|
||||
let bg_color = &Var::<color::Rgba>::from(bg_color.clone());
|
||||
|
||||
let grid_padding = style.get_number(theme::grid::padding);
|
||||
let grid_width = style.get_number(theme::grid::width);
|
||||
let grid_height = style.get_number(theme::grid::height);
|
||||
let corners_radius = style.get_number(theme::corners_radius);
|
||||
let menu_divider_color = style.get_color(theme::menu_divider_color);
|
||||
let navigator_divider_color = style.get_color(theme::navigator_divider_color);
|
||||
let menu_divider_width = grid_width - grid_padding * 2.0;
|
||||
let menu_divider_height = style.get_number(theme::menu_divider_height);
|
||||
let navigator_divider_width = style.get_number(theme::navigator_divider_width);
|
||||
let menu_height = style.get_number(theme::menu_height);
|
||||
let navigator_width = style.get_number(theme::navigator::width);
|
||||
|
||||
let width = grid_width + navigator_width;
|
||||
let height = grid_height + menu_height;
|
||||
|
||||
let divider_x_pos = navigator_width / 2.0;
|
||||
let divider_y_pos = height / 2.0 - menu_height + menu_divider_height ;
|
||||
let menu_divider_x_pos = navigator_width / 2.0;
|
||||
let menu_divider_y_pos = height / 2.0 - menu_height + menu_divider_height;
|
||||
let navigator_divider_x = -width / 2.0 + navigator_width - navigator_divider_width / 2.0;
|
||||
let navigator_divider_y = 0.0;
|
||||
|
||||
let divider = Rect((grid_width.px(),menu_divider_height.px()));
|
||||
let divider = divider.fill(menu_divider_color);
|
||||
let divider = divider.translate_x(divider_x_pos.px());
|
||||
let divider = divider.translate_y(divider_y_pos.px());
|
||||
let menu_divider = Rect((menu_divider_width.px(),menu_divider_height.px()));
|
||||
let menu_divider = menu_divider.fill(menu_divider_color);
|
||||
let menu_divider = menu_divider.translate_x(menu_divider_x_pos.px());
|
||||
let menu_divider = menu_divider.translate_y(menu_divider_y_pos.px());
|
||||
|
||||
let navigator_divider = Rect((navigator_divider_width.px(), height.px()));
|
||||
let navigator_divider = navigator_divider.fill(navigator_divider_color);
|
||||
let navigator_divider = navigator_divider.translate_x(navigator_divider_x.px());
|
||||
let navigator_divider = navigator_divider.translate_y(navigator_divider_y.px());
|
||||
|
||||
let base_shape = Rect((width.px(), height.px()));
|
||||
let base_shape = base_shape.corners_radius(corners_radius.px());
|
||||
let background = base_shape.fill(bg_color);
|
||||
let shadow = shadow::from_shape_with_alpha(base_shape.into(),&alpha,style);
|
||||
|
||||
(shadow + background + divider).into()
|
||||
(shadow + background + menu_divider + navigator_divider).into()
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -225,13 +235,6 @@ mod background {
|
||||
pub struct Model {
|
||||
display_object: display::object::Instance,
|
||||
background: background::View,
|
||||
// FIXME[#182593513]: This separate shape for navigator shadow can be removed and replaced
|
||||
// with a shadow embedded into the [`background`] shape when the
|
||||
// [issue](https://www.pivotaltracker.com/story/show/182593513) is fixed.
|
||||
// To display the shadow correctly it needs to be clipped to the [`background`] shape, but
|
||||
// we can't do that because of a bug in the renderer. So instead we add the shadow as a
|
||||
// separate shape and clip it using `size.set(...)`.
|
||||
navigator_shadow: navigator_shadow::View,
|
||||
pub grid: grid::View,
|
||||
pub section_navigator: SectionNavigator,
|
||||
pub breadcrumbs: breadcrumbs::Breadcrumbs,
|
||||
@ -246,8 +249,6 @@ impl Model {
|
||||
|
||||
let background = background::View::new();
|
||||
display_object.add_child(&background);
|
||||
let navigator_shadow = navigator_shadow::View::new();
|
||||
display_object.add_child(&navigator_shadow);
|
||||
|
||||
let grid = app.new_view::<grid::View>();
|
||||
display_object.add_child(&grid);
|
||||
@ -259,15 +260,7 @@ impl Model {
|
||||
breadcrumbs.set_base_layer(&app.display.default_scene.layers.node_searcher);
|
||||
display_object.add_child(&breadcrumbs);
|
||||
|
||||
Self {
|
||||
display_object,
|
||||
background,
|
||||
navigator_shadow,
|
||||
grid,
|
||||
section_navigator,
|
||||
scene_navigator,
|
||||
breadcrumbs,
|
||||
}
|
||||
Self { display_object, background, grid, section_navigator, scene_navigator, breadcrumbs }
|
||||
}
|
||||
|
||||
fn set_initial_breadcrumbs(&self) {
|
||||
@ -281,11 +274,6 @@ impl Model {
|
||||
self.background.size.set(style.background_sprite_size());
|
||||
self.section_navigator.update_layout(style);
|
||||
|
||||
let navigator_shadow_x = -style.grid.width / 2.0;
|
||||
self.navigator_shadow.set_x(navigator_shadow_x);
|
||||
let section_navigator_shadow_size = Vector2(style.navigator.width, style.size().y);
|
||||
self.navigator_shadow.size.set(section_navigator_shadow_size);
|
||||
|
||||
self.breadcrumbs.set_xy(style.breadcrumbs_pos());
|
||||
self.breadcrumbs.set_size(style.breadcrumbs_size());
|
||||
self.grid.set_xy(style.grid_pos());
|
||||
@ -397,14 +385,6 @@ impl component::Frp<Model> for Frp {
|
||||
model.section_navigator.select_section <+ model.grid.active_section.on_change();
|
||||
|
||||
|
||||
// === Navigator icons colors ===
|
||||
|
||||
let vivid_color = style.get_color(theme::navigator::icon_strong_color);
|
||||
let dull_color = style.get_color(theme::navigator::icon_weak_color);
|
||||
let params = icon::Params { vivid_color, dull_color };
|
||||
model.section_navigator.set_bottom_buttons_entry_params(params);
|
||||
|
||||
|
||||
// === Breadcrumbs ===
|
||||
|
||||
eval_ input.show(model.set_initial_breadcrumbs());
|
||||
|
@ -8,6 +8,7 @@ use ensogl_core::prelude::*;
|
||||
|
||||
use crate::AllStyles;
|
||||
|
||||
use crate::grid::entry::icon;
|
||||
use enso_frp as frp;
|
||||
use ensogl_core::animation::animation::delayed::DelayedAnimation;
|
||||
use ensogl_core::application::tooltip;
|
||||
@ -15,57 +16,33 @@ use ensogl_core::application::Application;
|
||||
use ensogl_core::data::color;
|
||||
use ensogl_core::display;
|
||||
use ensogl_derive_theme::FromTheme;
|
||||
use ensogl_grid_view as grid;
|
||||
use ensogl_hardcoded_theme::application::component_browser::component_list_panel as list_panel_theme;
|
||||
use ensogl_list_view as list_view;
|
||||
use ensogl_list_view::entry::AnyModelProvider;
|
||||
use ensogl_shadow as shadow;
|
||||
use ensogl_tooltip::Tooltip;
|
||||
use ide_view_component_list_panel_grid::entry::icon;
|
||||
use grid::Col;
|
||||
use grid::Row;
|
||||
use ide_view_component_list_panel_grid::SectionId;
|
||||
use list_panel_theme::navigator as theme;
|
||||
|
||||
|
||||
|
||||
mod entry;
|
||||
|
||||
type Grid = grid::selectable::GridView<entry::View>;
|
||||
|
||||
|
||||
|
||||
// =================
|
||||
// === Constants ===
|
||||
// =================
|
||||
|
||||
const MARKETPLACE_BUTTON_INDEX: usize = 1;
|
||||
const MARKETPLACE_TOOLTIP_TEXT: &str = "Marketplace will be available soon.";
|
||||
const MARKETPLACE_TOOLTIP_HIDE_DELAY_MS: f32 = 3000.0;
|
||||
const MARKETPLACE_TOOLTIP_PLACEMENT: tooltip::Placement = tooltip::Placement::Bottom;
|
||||
const TOP_BUTTONS: [icon::Id; 2] = [icon::Id::Libraries, icon::Id::Marketplace];
|
||||
const MARKETPLACE_BUTTON_INDEX: usize = 1;
|
||||
const BOTTOM_BUTTONS: [icon::Id; 3] = [icon::Id::SubModules, icon::Id::Star, icon::Id::LocalScope];
|
||||
|
||||
|
||||
|
||||
// ==============
|
||||
// === Shadow ===
|
||||
// ==============
|
||||
|
||||
/// A shadow between the navigator bar and the main part of the Searcher List Panel.
|
||||
///
|
||||
/// We should have this shape embedded into the background shape, but we use a separate object
|
||||
/// because of https://www.pivotaltracker.com/story/show/182593513.
|
||||
pub mod navigator_shadow {
|
||||
use super::*;
|
||||
|
||||
ensogl_core::shape! {
|
||||
above = [crate::background];
|
||||
below = [list_view::overlay, list_view::selection];
|
||||
pointer_events = false;
|
||||
(style:Style) {
|
||||
let grid_height = style.get_number(list_panel_theme::grid::height);
|
||||
let menu_height = style.get_number(list_panel_theme::menu_height);
|
||||
let navigator_width = style.get_number(theme::width);
|
||||
let height = grid_height + menu_height;
|
||||
let width = navigator_width;
|
||||
let base_shape = Rect((width.px(), height.px() * 2.0)).translate_x(width.px());
|
||||
shadow::from_shape(base_shape.into(), style)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const TOP_BUTTONS_COUNT: usize = TOP_BUTTONS.len();
|
||||
const BOTTOM_BUTTONS_COUNT: usize = 3;
|
||||
|
||||
|
||||
// =============
|
||||
@ -75,39 +52,92 @@ pub mod navigator_shadow {
|
||||
#[derive(Copy, Clone, Debug, Default, FromTheme)]
|
||||
#[base_path = "theme"]
|
||||
pub struct Style {
|
||||
pub width: f32,
|
||||
pub list_view_width: f32,
|
||||
pub icon_strong_color: color::Rgba,
|
||||
pub icon_weak_color: color::Rgba,
|
||||
pub top_padding: f32,
|
||||
pub bottom_padding: f32,
|
||||
pub width: f32,
|
||||
pub button_size: f32,
|
||||
pub top_padding: f32,
|
||||
pub bottom_padding: f32,
|
||||
pub hover_color: color::Rgba,
|
||||
#[theme_path = "theme::highlight::color"]
|
||||
pub highlight_color: color::Rgba,
|
||||
#[theme_path = "theme::highlight::size"]
|
||||
pub highlight_size: f32,
|
||||
#[theme_path = "theme::highlight::corners_radius"]
|
||||
pub highlight_corners_radius: f32,
|
||||
}
|
||||
|
||||
|
||||
|
||||
// =========================================================
|
||||
// === Conversions Between SectionId and List View Index ===
|
||||
// =========================================================
|
||||
|
||||
/// Convert [`SectionId`] to index on [`Navigator::bottom_buttons`].
|
||||
fn section_id_to_list_index(id: SectionId) -> usize {
|
||||
match id {
|
||||
SectionId::Popular => 1,
|
||||
SectionId::LocalScope => 2,
|
||||
SectionId::SubModules => 0,
|
||||
impl From<Style> for entry::Params {
|
||||
fn from(style: Style) -> Self {
|
||||
Self {
|
||||
hover_color: style.hover_color.into(),
|
||||
selection_color: style.highlight_color.into(),
|
||||
selection_size: style.highlight_size,
|
||||
selection_corners_radius: style.highlight_corners_radius,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Convert the index on [`Navigator::bottom_buttons`] to [`SectionId`]. Prints error on invalid
|
||||
/// Colors of the buttons of the section navigator.
|
||||
/// Each of the section buttons can have a different "active" color, but they all share the same
|
||||
/// "inactive" color. "active" color is used when the button is highlighted, and the "inactive" is
|
||||
/// used as default.
|
||||
#[derive(Debug, Clone, Copy, Default, FromTheme)]
|
||||
#[base_path = "theme::buttons"]
|
||||
#[allow(missing_docs)]
|
||||
pub struct Colors {
|
||||
pub inactive: color::Rgba,
|
||||
#[theme_path = "theme::buttons::active::popular"]
|
||||
pub popular: color::Rgba,
|
||||
#[theme_path = "theme::buttons::active::local_scope"]
|
||||
pub local_scope: color::Rgba,
|
||||
#[theme_path = "theme::buttons::active::submodules"]
|
||||
pub submodules: color::Rgba,
|
||||
}
|
||||
|
||||
impl Colors {
|
||||
fn get(&self, section: SectionId) -> color::Rgba {
|
||||
match section {
|
||||
SectionId::Popular => self.popular,
|
||||
SectionId::LocalScope => self.local_scope,
|
||||
SectionId::SubModules => self.submodules,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Convert [`SectionId`] to the displayed icon id.
|
||||
fn section_id_to_icon_id(section: SectionId) -> icon::Id {
|
||||
match section {
|
||||
SectionId::Popular => icon::Id::Star,
|
||||
SectionId::LocalScope => icon::Id::LocalScope,
|
||||
SectionId::SubModules => icon::Id::SubModules,
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
// ============================================================
|
||||
// === Conversions Between SectionId and Grid View Location ===
|
||||
// ============================================================
|
||||
|
||||
/// Convert [`SectionId`] to location on [`Navigator::bottom_buttons`].
|
||||
fn section_id_to_grid_loc(id: SectionId) -> (Row, Col) {
|
||||
const COLUMN: Col = 0;
|
||||
match id {
|
||||
SectionId::Popular => (1, COLUMN),
|
||||
SectionId::LocalScope => (2, COLUMN),
|
||||
SectionId::SubModules => (0, COLUMN),
|
||||
}
|
||||
}
|
||||
|
||||
/// Convert the location on [`Navigator::bottom_buttons`] to [`SectionId`]. Prints error on invalid
|
||||
/// index and returns the id of topmost section.
|
||||
fn index_to_section_id(&index: &usize) -> SectionId {
|
||||
fn loc_to_section_id(&(row, _): &(Row, Col)) -> SectionId {
|
||||
let highest = SectionId::SubModules;
|
||||
match index {
|
||||
match row {
|
||||
0 => highest,
|
||||
1 => SectionId::Popular,
|
||||
2 => SectionId::LocalScope,
|
||||
index => {
|
||||
error!("Tried to create SectionId from too high Navigator List index ({}).", index);
|
||||
_ => {
|
||||
error!("Tried to create SectionId from too high Navigator List row ({}).", row);
|
||||
highest
|
||||
}
|
||||
}
|
||||
@ -129,8 +159,8 @@ fn index_to_section_id(&index: &usize) -> SectionId {
|
||||
pub struct Navigator {
|
||||
display_object: display::object::Instance,
|
||||
network: frp::Network,
|
||||
bottom_buttons: list_view::ListView<icon::Entry>,
|
||||
top_buttons: list_view::ListView<icon::Entry>,
|
||||
bottom_buttons: Grid,
|
||||
top_buttons: Grid,
|
||||
tooltip: Tooltip,
|
||||
pub select_section: frp::Any<Option<SectionId>>,
|
||||
pub chosen_section: frp::Stream<Option<SectionId>>,
|
||||
@ -139,40 +169,52 @@ pub struct Navigator {
|
||||
impl Navigator {
|
||||
pub fn new(app: &Application) -> Self {
|
||||
let display_object = display::object::Instance::new();
|
||||
let top_buttons = app.new_view::<list_view::ListView<icon::Entry>>();
|
||||
let bottom_buttons = app.new_view::<list_view::ListView<icon::Entry>>();
|
||||
let tooltip = Tooltip::new(app);
|
||||
top_buttons.set_style_prefix(list_panel_theme::navigator::list_view::HERE.str);
|
||||
bottom_buttons.set_style_prefix(list_panel_theme::navigator::list_view::HERE.str);
|
||||
top_buttons.show_background_shadow(false);
|
||||
bottom_buttons.show_background_shadow(false);
|
||||
bottom_buttons.disable_selecting_entries_with_mouse();
|
||||
let top_buttons = Grid::new(app);
|
||||
let bottom_buttons = Grid::new(app);
|
||||
display_object.add_child(&top_buttons);
|
||||
display_object.add_child(&bottom_buttons);
|
||||
let tooltip = Tooltip::new(app);
|
||||
app.display.default_scene.add_child(&tooltip);
|
||||
top_buttons.hide_selection();
|
||||
|
||||
top_buttons.set_entries(AnyModelProvider::new(TOP_BUTTONS.to_vec()));
|
||||
bottom_buttons.set_entries(AnyModelProvider::new(BOTTOM_BUTTONS.to_vec()));
|
||||
bottom_buttons.select_entry(Some(section_id_to_list_index(SectionId::Popular)));
|
||||
|
||||
let network = frp::Network::new("ComponentBrowser.Navigator");
|
||||
let style_frp = StyleWatchFrp::new(&app.display.default_scene.style_sheet);
|
||||
let colors = Colors::from_theme(&network, &style_frp);
|
||||
let tooltip_hide_timer = DelayedAnimation::new(&network);
|
||||
tooltip_hide_timer.set_delay(MARKETPLACE_TOOLTIP_HIDE_DELAY_MS);
|
||||
tooltip_hide_timer.set_duration(0.0);
|
||||
frp::extend! { network
|
||||
select_section <- any(...);
|
||||
bottom_buttons.select_entry <+
|
||||
select_section.map(|&s:&Option<SectionId>| s.map(section_id_to_list_index));
|
||||
chosen_section <-
|
||||
bottom_buttons.chosen_entry.map(|&id| id.as_ref().map(index_to_section_id));
|
||||
user_selected_section <- select_section.map(|&s:&Option<SectionId>| s.map(section_id_to_grid_loc));
|
||||
bottom_buttons.select_entry <+ user_selected_section;
|
||||
selected_button_and_section <- all(&bottom_buttons.entry_selected, &user_selected_section);
|
||||
different_section_chosen <- selected_button_and_section.filter(|(e, u)| *e != *u);
|
||||
chosen_section <- different_section_chosen._0().map(|loc| loc.as_ref().map(loc_to_section_id));
|
||||
|
||||
model <- bottom_buttons.model_for_entry_needed.map2(&colors.update, f!([]
|
||||
((row, col), colors) {
|
||||
let section_id = loc_to_section_id(&(*row, *col));
|
||||
let icon_id = section_id_to_icon_id(section_id);
|
||||
let active_colors = colors.get(section_id).into();
|
||||
let inactive_colors = colors.inactive.into();
|
||||
let model = entry::Model::new(icon_id, active_colors, inactive_colors);
|
||||
(*row, *col, model)
|
||||
}
|
||||
));
|
||||
bottom_buttons.model_for_entry <+ model;
|
||||
|
||||
model <- top_buttons.model_for_entry_needed.map2(&colors.update, f!([]
|
||||
((row, col), colors) {
|
||||
let icon_id = TOP_BUTTONS.get(*row).cloned().unwrap_or_default();
|
||||
let model = entry::Model::new(icon_id, colors.inactive.into(), colors.inactive.into());
|
||||
(*row, *col, model)
|
||||
}
|
||||
));
|
||||
top_buttons.model_for_entry <+ model;
|
||||
|
||||
// === Show tooltip when hovering the Marketplace button
|
||||
|
||||
let idx_of_marketplace_btn = |idx: &Option<_>| *idx == Some(MARKETPLACE_BUTTON_INDEX);
|
||||
marketplace_button_selected <- top_buttons.selected_entry.map(idx_of_marketplace_btn);
|
||||
marketplace_button_hovered <- marketplace_button_selected && top_buttons.is_mouse_over;
|
||||
let idx_of_marketplace_btn = |loc: &Option<(Row, Col)>| matches!(loc, Some((row, _)) if *row == MARKETPLACE_BUTTON_INDEX);
|
||||
marketplace_button_hovered <- top_buttons.entry_hovered.map(idx_of_marketplace_btn);
|
||||
marketplace_button_hovered <- marketplace_button_hovered.on_change();
|
||||
tooltip_hide_timer.start <+ marketplace_button_hovered.on_true();
|
||||
tooltip_hide_timer.reset <+ marketplace_button_hovered.on_false();
|
||||
@ -186,7 +228,10 @@ impl Navigator {
|
||||
}
|
||||
);
|
||||
}
|
||||
colors.init.emit(());
|
||||
tooltip_hide_timer.reset();
|
||||
bottom_buttons.reset_entries(BOTTOM_BUTTONS_COUNT, 1);
|
||||
top_buttons.reset_entries(TOP_BUTTONS_COUNT, 1);
|
||||
|
||||
Self {
|
||||
display_object,
|
||||
@ -199,27 +244,38 @@ impl Navigator {
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn set_bottom_buttons_entry_params(&self, params: icon::Params) {
|
||||
self.bottom_buttons.set_entry_params_and_recreate_entries(params);
|
||||
}
|
||||
|
||||
pub(crate) fn update_layout(&self, style: &AllStyles) {
|
||||
let list_view_width = style.navigator.list_view_width;
|
||||
let top_buttons_height = list_view::entry::HEIGHT * TOP_BUTTONS.len() as f32;
|
||||
let bottom_buttons_height = list_view::entry::HEIGHT * BOTTOM_BUTTONS.len() as f32;
|
||||
self.top_buttons.resize(Vector2(list_view_width, top_buttons_height));
|
||||
self.bottom_buttons.resize(Vector2(list_view_width, bottom_buttons_height));
|
||||
let size = style.navigator.button_size;
|
||||
let top_buttons_height = size * TOP_BUTTONS_COUNT as f32;
|
||||
let bottom_buttons_height = size * BOTTOM_BUTTONS_COUNT as f32;
|
||||
self.bottom_buttons.set_entries_size(Vector2(size, size));
|
||||
self.top_buttons.set_entries_size(Vector2(size, size));
|
||||
let (top, left, right) = (0.0, 0.0, size);
|
||||
let viewport = grid::Viewport { top, bottom: 0.0, left, right };
|
||||
let top_buttons_viewport = grid::Viewport { bottom: -top_buttons_height, ..viewport };
|
||||
let bottom_buttons_viewport = grid::Viewport { bottom: -bottom_buttons_height, ..viewport };
|
||||
self.top_buttons.set_viewport(top_buttons_viewport);
|
||||
self.bottom_buttons.set_viewport(bottom_buttons_viewport);
|
||||
let buttons_params = entry::Params::from(style.navigator);
|
||||
self.bottom_buttons.set_entries_params(buttons_params);
|
||||
let disabled_params = entry::Params {
|
||||
hover_color: color::Lcha::transparent(),
|
||||
selection_color: color::Lcha::transparent(),
|
||||
selection_size: 0.0,
|
||||
selection_corners_radius: 0.0,
|
||||
};
|
||||
self.top_buttons.set_entries_params(disabled_params);
|
||||
|
||||
let width = style.navigator.width;
|
||||
let height = style.grid.height + style.panel.menu_height;
|
||||
let top = height / 2.0;
|
||||
let bottom = -height / 2.0;
|
||||
let top_buttons_height = TOP_BUTTONS.len() as f32 * list_view::entry::HEIGHT;
|
||||
let bottom_buttons_height = BOTTOM_BUTTONS.len() as f32 * list_view::entry::HEIGHT;
|
||||
let bottom = (-height / 2.0).floor();
|
||||
let left = -style.grid.width / 2.0 - width / 2.0;
|
||||
let top_padding = style.navigator.top_padding;
|
||||
let bottom_padding = style.navigator.bottom_padding;
|
||||
let x_pos = -style.grid.width / 2.0;
|
||||
let top_buttons_y = top - top_buttons_height / 2.0 - top_padding;
|
||||
let bottom_buttons_y = bottom + bottom_buttons_height / 2.0 + bottom_padding;
|
||||
let x_pos = left + (width / 2.0).floor() - size / 2.0;
|
||||
let top_buttons_y = top - top_padding;
|
||||
let bottom_buttons_y = bottom + bottom_buttons_height + bottom_padding;
|
||||
self.top_buttons.set_xy(Vector2(x_pos, top_buttons_y));
|
||||
self.bottom_buttons.set_xy(Vector2(x_pos, bottom_buttons_y));
|
||||
}
|
||||
|
@ -0,0 +1,164 @@
|
||||
//! A module containing the grid view entry type which represents an arbitrary icon.
|
||||
|
||||
use ensogl_core::prelude::*;
|
||||
|
||||
use crate::grid::entry::icon;
|
||||
use enso_frp as frp;
|
||||
use ensogl_core::application::command::FrpNetworkProvider;
|
||||
use ensogl_core::application::frp::API;
|
||||
use ensogl_core::application::Application;
|
||||
use ensogl_core::data::color;
|
||||
use ensogl_core::display;
|
||||
use ensogl_core::display::scene::Layer;
|
||||
use ensogl_grid_view as grid;
|
||||
use ide_view_component_list_panel_icons::Any as AnyIcon;
|
||||
use ide_view_component_list_panel_icons::SIZE;
|
||||
|
||||
|
||||
|
||||
// =============
|
||||
// === Model ===
|
||||
// =============
|
||||
|
||||
/// Entry's model.
|
||||
#[derive(Debug, Clone, Copy, Default)]
|
||||
pub struct Model {
|
||||
icon_id: icon::Id,
|
||||
/// Color of the icon when the entry is selected.
|
||||
active: color::Lcha,
|
||||
/// Color of the icon when the entry is not selected.
|
||||
inactive: color::Lcha,
|
||||
}
|
||||
|
||||
impl Model {
|
||||
/// Constructor.
|
||||
pub const fn new(icon_id: icon::Id, active: color::Lcha, inactive: color::Lcha) -> Self {
|
||||
Self { icon_id, active, inactive }
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
// ==================
|
||||
// === IconParams ===
|
||||
// ==================
|
||||
|
||||
/// Entry parameters of the icon.
|
||||
#[derive(Clone, Copy, Debug, Default, PartialEq)]
|
||||
#[allow(missing_docs)]
|
||||
pub struct Params {
|
||||
pub hover_color: color::Lcha,
|
||||
pub selection_color: color::Lcha,
|
||||
pub selection_size: f32,
|
||||
pub selection_corners_radius: f32,
|
||||
}
|
||||
|
||||
|
||||
|
||||
// ============
|
||||
// === Data ===
|
||||
// ============
|
||||
|
||||
/// Grid view entry type which represents a single icon. We use grid view with icons instead of
|
||||
/// multiple buttons to simplify the implementation.
|
||||
#[derive(Debug, Clone, CloneRef)]
|
||||
pub struct Data {
|
||||
display_object: display::object::Instance,
|
||||
icon: Rc<RefCell<Option<AnyIcon>>>,
|
||||
color: Rc<Cell<color::Lcha>>,
|
||||
}
|
||||
|
||||
impl Data {
|
||||
pub fn new() -> Self {
|
||||
let display_object = display::object::Instance::new();
|
||||
let icon = default();
|
||||
let color = default();
|
||||
Self { display_object, icon, color }
|
||||
}
|
||||
|
||||
fn set_icon(&self, icon_id: icon::Id) {
|
||||
let size = Vector2(SIZE, SIZE);
|
||||
let icon = icon_id.create_shape(size);
|
||||
icon.set_color(color::Rgba::from(self.color.get()).into());
|
||||
self.display_object.add_child(&icon);
|
||||
*self.icon.borrow_mut() = Some(icon);
|
||||
}
|
||||
|
||||
fn set_color(&self, color: color::Lcha) {
|
||||
self.color.set(color);
|
||||
if let Some(icon) = self.icon.borrow().deref() {
|
||||
icon.set_color(color::Rgba::from(color).into());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
// ============
|
||||
// === View ===
|
||||
// ============
|
||||
|
||||
/// The grid view entry.
|
||||
#[derive(Clone, CloneRef, Debug)]
|
||||
pub struct View {
|
||||
frp: grid::entry::EntryFrp<Self>,
|
||||
data: Data,
|
||||
}
|
||||
|
||||
impl grid::entry::Entry for View {
|
||||
type Model = Model;
|
||||
type Params = Params;
|
||||
|
||||
fn new(_app: &Application, _text_layer: Option<&Layer>) -> Self {
|
||||
let frp = grid::entry::EntryFrp::<Self>::new();
|
||||
let data = Data::new();
|
||||
let network = frp.network();
|
||||
let input = &frp.private().input;
|
||||
let out = &frp.private().output;
|
||||
|
||||
let color_anim = color::Animation::new(network);
|
||||
|
||||
frp::extend! { network
|
||||
init <- source_();
|
||||
|
||||
icon <- input.set_model.map(|m| m.icon_id);
|
||||
eval icon((icon) data.set_icon(*icon));
|
||||
|
||||
active_color <- input.set_model.map(|m| m.active);
|
||||
inactive_color <- input.set_model.map(|m| m.inactive);
|
||||
|
||||
color_anim.target <+ inactive_color;
|
||||
|
||||
entry_selected <- input.set_selected.on_true();
|
||||
entry_deselected <- input.set_selected.on_false();
|
||||
color_anim.target <+ active_color.sample(&entry_selected);
|
||||
color_anim.target <+ inactive_color.sample(&entry_deselected);
|
||||
|
||||
eval color_anim.value((color) data.set_color(*color));
|
||||
|
||||
style <- input.set_params.on_change();
|
||||
selection_color <- style.map(|s| s.selection_color).on_change();
|
||||
hover_color <- style.map(|s| s.hover_color).on_change();
|
||||
|
||||
out.contour <+ input.set_size.map(|s| grid::entry::Contour::rectangular(*s));
|
||||
out.highlight_contour <+ out.contour.all_with(&style,
|
||||
|c,s| grid::entry::Contour { corners_radius: s.selection_corners_radius, ..*c }
|
||||
);
|
||||
out.hover_highlight_color <+ hover_color;
|
||||
out.selection_highlight_color <+ selection_color;
|
||||
}
|
||||
init.emit(());
|
||||
|
||||
Self { frp, data }
|
||||
}
|
||||
|
||||
fn frp(&self) -> &grid::entry::EntryFrp<Self> {
|
||||
&self.frp
|
||||
}
|
||||
}
|
||||
|
||||
impl display::Object for View {
|
||||
fn display_object(&self) -> &display::object::Instance {
|
||||
&self.data.display_object
|
||||
}
|
||||
}
|
@ -40,6 +40,8 @@ use ensogl_core::prelude::*;
|
||||
use wasm_bindgen::prelude::*;
|
||||
|
||||
use ensogl_core::application::Application;
|
||||
use ensogl_core::display;
|
||||
use ensogl_core::display::navigation::navigator::Navigator;
|
||||
use ensogl_core::display::object::ObjectOps;
|
||||
use ensogl_core::frp;
|
||||
use ensogl_hardcoded_theme as theme;
|
||||
@ -56,12 +58,17 @@ use ide_view_component_list_panel::grid::entry::icon;
|
||||
const PREPARED_ITEMS: &[(&str, icon::Id)] = &[
|
||||
// ("long sample entry with text overflowing the width", icon::Id::Star),
|
||||
("convert", icon::Id::Convert),
|
||||
("table input", icon::Id::DataInput),
|
||||
("text input", icon::Id::TextInput),
|
||||
("number input", icon::Id::NumberInput),
|
||||
("table output", icon::Id::TableEdit),
|
||||
("dataframe clean", icon::Id::DataframeClean),
|
||||
("data input", icon::Id::DataInput),
|
||||
("data output", icon::Id::DataOutput),
|
||||
("table input", icon::Id::TableEdit),
|
||||
("number input", icon::Id::NumberInput),
|
||||
("text input", icon::Id::TextInput),
|
||||
("add column", icon::Id::AddColumn),
|
||||
("select column", icon::Id::SelectColumn),
|
||||
("clean", icon::Id::DataframeClean),
|
||||
("add row", icon::Id::AddRow),
|
||||
("map row", icon::Id::DataframeMapRow),
|
||||
("map column", icon::Id::DataframeMapColumn),
|
||||
];
|
||||
|
||||
const fn make_group(section: grid::SectionId, index: usize, size: usize) -> grid::content::Group {
|
||||
@ -74,6 +81,18 @@ const fn make_group(section: grid::SectionId, index: usize, size: usize) -> grid
|
||||
}
|
||||
}
|
||||
|
||||
/// Use this groups setup to compare to Figma design.
|
||||
#[allow(dead_code)]
|
||||
const GROUPS_AS_IN_DESIGN: &[grid::content::Group] = &[
|
||||
make_group(grid::SectionId::Popular, 0, 7),
|
||||
make_group(grid::SectionId::Popular, 1, 7),
|
||||
make_group(grid::SectionId::Popular, 2, 5),
|
||||
make_group(grid::SectionId::SubModules, 3, 10),
|
||||
make_group(grid::SectionId::SubModules, 4, 3),
|
||||
make_group(grid::SectionId::SubModules, 5, 10),
|
||||
make_group(grid::SectionId::SubModules, 6, 10),
|
||||
];
|
||||
|
||||
const GROUPS: &[grid::content::Group] = &[
|
||||
make_group(grid::SectionId::Popular, 1, 3),
|
||||
make_group(grid::SectionId::Popular, 2, 2),
|
||||
@ -109,16 +128,24 @@ const LOCAL_SCOPE_GROUP_SIZE: usize = 1024;
|
||||
|
||||
fn content_info() -> grid::content::Info {
|
||||
grid::content::Info {
|
||||
groups: GROUPS.into(),
|
||||
groups: GROUPS_AS_IN_DESIGN.into(),
|
||||
local_scope_entry_count: LOCAL_SCOPE_GROUP_SIZE,
|
||||
}
|
||||
}
|
||||
|
||||
const GROUP_NAMES: &[&str] = &[
|
||||
"Input / Output",
|
||||
"Preparation",
|
||||
"Join",
|
||||
"Text",
|
||||
"Date and Time",
|
||||
"Transform",
|
||||
"Machine Learning",
|
||||
];
|
||||
|
||||
fn get_header_model(group: grid::GroupId) -> Option<(grid::GroupId, grid::HeaderModel)> {
|
||||
let model = grid::HeaderModel {
|
||||
caption: format!("Group {}", group.index).into(),
|
||||
can_be_entered: false,
|
||||
};
|
||||
let caption = GROUP_NAMES.get(group.index % GROUP_NAMES.len()).copied().unwrap_or("");
|
||||
let model = grid::HeaderModel { caption: caption.into(), can_be_entered: false };
|
||||
Some((group, model))
|
||||
}
|
||||
|
||||
@ -138,6 +165,17 @@ fn get_entry_model(entry: grid::GroupEntryId) -> Option<(grid::GroupEntryId, gri
|
||||
Some((entry, model))
|
||||
}
|
||||
|
||||
fn snap_to_pixel_offset(size: Vector2, scene_shape: &display::scene::Shape) -> Vector2 {
|
||||
let device_size = scene_shape.device_pixels();
|
||||
let origin_left_top_pos = Vector2(device_size.width, device_size.height) / 2.0;
|
||||
let origin_snapped = Vector2(origin_left_top_pos.x.floor(), origin_left_top_pos.y.floor());
|
||||
let origin_offset = origin_snapped - origin_left_top_pos;
|
||||
let panel_left_top_pos = (size * scene_shape.pixel_ratio) / 2.0;
|
||||
let panel_snapped = Vector2(panel_left_top_pos.x.floor(), panel_left_top_pos.y.floor());
|
||||
let panel_offset = panel_snapped - panel_left_top_pos;
|
||||
origin_offset - panel_offset
|
||||
}
|
||||
|
||||
|
||||
|
||||
// ===================
|
||||
@ -153,17 +191,26 @@ pub fn main() {
|
||||
theme::builtin::light::register(&app);
|
||||
theme::builtin::light::enable(&app);
|
||||
|
||||
|
||||
let world = &app.display;
|
||||
let scene = &world.default_scene;
|
||||
let navigator = Navigator::new(scene, &scene.layers.node_searcher.camera());
|
||||
let panel = app.new_view::<ide_view_component_list_panel::View>();
|
||||
scene.layers.node_searcher.add(&panel);
|
||||
panel.model().set_navigator(Some(navigator.clone_ref()));
|
||||
panel.show();
|
||||
let network = frp::Network::new("new_component_list_panel_view");
|
||||
//TODO[ao] should be done by panel itself.
|
||||
let grid = &panel.model().grid;
|
||||
frp::extend! { network
|
||||
init <- source_();
|
||||
grid.model_for_header <+ grid.model_for_header_needed.filter_map(|&id| get_header_model(id));
|
||||
grid.model_for_entry <+ grid.model_for_entry_needed.filter_map(|&id| get_entry_model(id));
|
||||
size <- all_with(&init, &panel.size, |(), panel_size| *panel_size);
|
||||
snap <- all_with(&size, &scene.frp.shape, |sz, sh| snap_to_pixel_offset(*sz, sh));
|
||||
eval snap((snap) panel.set_xy(*snap));
|
||||
}
|
||||
init.emit(());
|
||||
|
||||
grid.reset(content_info());
|
||||
scene.add_child(&panel);
|
||||
@ -171,5 +218,6 @@ pub fn main() {
|
||||
mem::forget(app);
|
||||
mem::forget(panel);
|
||||
mem::forget(network);
|
||||
mem::forget(navigator);
|
||||
})
|
||||
}
|
||||
|
@ -11,4 +11,5 @@ crate-type = ["cdylib", "rlib"]
|
||||
ensogl = { path = "../../../../../lib/rust/ensogl" }
|
||||
ensogl-hardcoded-theme = { path = "../../../../../lib/rust/ensogl/app/theme/hardcoded" }
|
||||
ide-view-component-list-panel-grid = { path = "../../component-browser/component-list-panel/grid" }
|
||||
ide-view-component-list-panel-icons = { path = "../../component-browser/component-list-panel/icons" }
|
||||
wasm-bindgen = { version = "=0.2.78" }
|
||||
|
@ -14,7 +14,8 @@ use ensogl::display::navigation::navigator::Navigator;
|
||||
use ensogl::display::DomSymbol;
|
||||
use ensogl::system::web;
|
||||
use ide_view_component_list_panel_grid::entry::icon;
|
||||
use ide_view_component_list_panel_grid::entry::icon::SHRINK_AMOUNT;
|
||||
use ide_view_component_list_panel_icons::SHRINK_AMOUNT;
|
||||
use ide_view_component_list_panel_icons::SIZE;
|
||||
|
||||
|
||||
|
||||
@ -29,7 +30,7 @@ mod frame {
|
||||
|
||||
ensogl::shape! {
|
||||
(style:Style) {
|
||||
let inner = Rect((icon::SIZE.px(), icon::SIZE.px()));
|
||||
let inner = Rect((SIZE.px(), SIZE.px()));
|
||||
let outer = inner.grow(0.2.px());
|
||||
let shape = (outer - inner).fill(color::Rgba::black());
|
||||
shape.shrink(SHRINK_AMOUNT.px()).into()
|
||||
@ -72,7 +73,7 @@ pub fn entry_point_searcher_icons() {
|
||||
let grid = DomSymbol::new(&grid_div);
|
||||
scene.dom.layers.back.manage(&grid);
|
||||
world.add_child(&grid);
|
||||
grid.set_size(Vector2(1000.0, icon::SIZE));
|
||||
grid.set_size(Vector2(1000.0, SIZE));
|
||||
mem::forget(grid);
|
||||
|
||||
|
||||
@ -80,9 +81,8 @@ pub fn entry_point_searcher_icons() {
|
||||
|
||||
let mut x = -300.0;
|
||||
icon::Id::for_each(|id| {
|
||||
let shape = id.create_shape(Vector2(icon::SIZE, icon::SIZE));
|
||||
shape.set_vivid_color(color::Rgba(0.243, 0.541, 0.160, 1.0).into());
|
||||
shape.set_dull_color(color::Rgba(0.655, 0.788, 0.624, 1.0).into());
|
||||
let shape = id.create_shape(Vector2(SIZE, SIZE));
|
||||
shape.set_color(color::Rgba(0.243, 0.541, 0.160, 1.0).into());
|
||||
shape.set_x(x);
|
||||
x += 20.0;
|
||||
world.add_child(&shape);
|
||||
|
@ -1,6 +1,6 @@
|
||||
# Options intended to be common for all developers.
|
||||
|
||||
wasm-size-limit: 15.05 MiB
|
||||
wasm-size-limit: 15.09 MiB
|
||||
|
||||
required-versions:
|
||||
cargo-watch: ^8.1.1
|
||||
|
@ -20,6 +20,7 @@ use syn::Type;
|
||||
const BASE_PATH_ATTRIBUTE_NAME: &str = "base_path";
|
||||
// Sadly we can't use `path` here, because it conflicts with Rust's builtin attribute.
|
||||
const PATH_ATTRIBUTE_NAME: &str = "theme_path";
|
||||
const ACCESSOR_ATTRIBUTE_NAME: &str = "accessor";
|
||||
|
||||
|
||||
// ==================
|
||||
@ -31,11 +32,13 @@ enum ThemeTypes {
|
||||
Color,
|
||||
String,
|
||||
Number,
|
||||
/// The type is not directly supported by the macro, but can be accessed using a custom
|
||||
/// accessor.
|
||||
Unknown,
|
||||
}
|
||||
|
||||
impl ThemeTypes {
|
||||
/// Return the corresponging [`ThemeType`] for the given [`Type`]. Panics with an error message
|
||||
/// indicating the wrong type if this is not a valid theme type.
|
||||
/// Return the corresponging [`ThemeType`] for the given [`Type`].
|
||||
fn from_ty(ty: &Type) -> Self {
|
||||
match ty {
|
||||
Type::Path(type_path)
|
||||
@ -46,11 +49,7 @@ impl ThemeTypes {
|
||||
Type::Path(type_path)
|
||||
if type_path.clone().into_token_stream().to_string().contains("Rgba") =>
|
||||
Self::Color,
|
||||
_ => panic!(
|
||||
"The type `{:?}` cannot be derived from a theme. Only `ImString`,\
|
||||
`color::Rgba` and `f32` are supported.",
|
||||
ty
|
||||
),
|
||||
_ => Self::Unknown,
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -108,8 +107,7 @@ fn build_frp(
|
||||
}
|
||||
}
|
||||
|
||||
/// Create the field initializers for the struct holding the theme values. They differ between
|
||||
/// types that implement copy or clone.
|
||||
/// Create the field initializers for the struct holding the theme values.
|
||||
fn make_struct_inits(
|
||||
fields: &syn::punctuated::Punctuated<syn::Field, syn::token::Comma>,
|
||||
) -> TokenStream {
|
||||
@ -120,13 +118,9 @@ fn make_struct_inits(
|
||||
.ident
|
||||
.as_ref()
|
||||
.expect("Encountered unnamed struct field. This cannot not happen.");
|
||||
let init = match ThemeTypes::from_ty(&field.ty) {
|
||||
ThemeTypes::Color | ThemeTypes::Number => quote! {
|
||||
#field_name:*#field_name,
|
||||
},
|
||||
ThemeTypes::String => quote! {
|
||||
#field_name:#field_name.clone(),
|
||||
},
|
||||
// Keep in mind that all [`Copy`] types also implement [`Clone`].
|
||||
let init = quote! {
|
||||
#field_name : #field_name.clone(),
|
||||
};
|
||||
combined.extend(init);
|
||||
}
|
||||
@ -202,19 +196,28 @@ fn make_theme_getters(
|
||||
(None, None) => panic!("Neither `base_path` nor `path` attributes were set."),
|
||||
};
|
||||
|
||||
let accessor = match ThemeTypes::from_ty(&f.ty) {
|
||||
ThemeTypes::Color => quote! {
|
||||
get_color
|
||||
},
|
||||
ThemeTypes::String => quote! {
|
||||
get_text
|
||||
},
|
||||
ThemeTypes::Number => quote! {
|
||||
get_number
|
||||
},
|
||||
};
|
||||
let accessor = get_path_from_metadata(&f.attrs, ACCESSOR_ATTRIBUTE_NAME)
|
||||
.map(|accessor| {
|
||||
quote! { #accessor(&network, style, #field_path) }
|
||||
})
|
||||
.unwrap_or_else(|| match ThemeTypes::from_ty(&f.ty) {
|
||||
ThemeTypes::Color => quote! {
|
||||
StyleWatchFrp::get_color(style, #field_path)
|
||||
},
|
||||
ThemeTypes::String => quote! {
|
||||
StyleWatchFrp::get_text(style, #field_path)
|
||||
},
|
||||
ThemeTypes::Number => quote! {
|
||||
StyleWatchFrp::get_number(style, #field_path)
|
||||
},
|
||||
ThemeTypes::Unknown => panic!(
|
||||
"Unknown type for theme value, but no accessor was provided. \
|
||||
Use the `#[accessor]` attribute to provide a custom accessor function"
|
||||
),
|
||||
});
|
||||
|
||||
quote! {
|
||||
let #field_name = style.#accessor(#field_path);
|
||||
let #field_name = #accessor;
|
||||
}
|
||||
})
|
||||
.collect()
|
||||
|
@ -7,6 +7,20 @@
|
||||
//! appended to `base_path` to get the full path to the value in the theme. It can be overridden
|
||||
//! using the `theme_path` attribute on the field.
|
||||
//!
|
||||
//! A custom accessor function can be provided for each field using the `accessor` attribute. This
|
||||
//! function should have a following signature:
|
||||
//!
|
||||
//! ```ignore
|
||||
//! fn accessor<P: Into<ensogl_core::display::style::Path>>(
|
||||
//! network: &frp::Network,
|
||||
//! style: &StyleWatchFrp,
|
||||
//! path: P,
|
||||
//! ) -> frp::Sampler<T>
|
||||
//! ```
|
||||
//! where `T` is the type of the field. This accessor will be used to retrieve the value from the
|
||||
//! stylesheet. If no accessor is provided, a standard getters of the [`StyleWatchFrp`] are used
|
||||
//! instead.
|
||||
//!
|
||||
//! Example usage
|
||||
//!```no_compile
|
||||
//! use ensogl_core::data::color;
|
||||
@ -19,6 +33,8 @@
|
||||
//! some_color: color::Rgba,
|
||||
//! #[theme_path = "ensogl_hardcoded_theme::some_path::label"]
|
||||
//! some_label: String,
|
||||
//! #[accessor = "my_custom_accessor"]
|
||||
//! some_custom_value: CustomType,
|
||||
//! }
|
||||
//! ```
|
||||
|
||||
@ -69,7 +85,7 @@ mod from_theme;
|
||||
|
||||
|
||||
/// Implements the `FromTheme` derive macro. See thr crate docs for more information.
|
||||
#[proc_macro_derive(FromTheme, attributes(base_path, theme_path))]
|
||||
#[proc_macro_derive(FromTheme, attributes(base_path, theme_path, accessor))]
|
||||
pub fn derive_from_thee(input: TokenStream) -> TokenStream {
|
||||
let input = parse_macro_input!(input as DeriveInput);
|
||||
from_theme::expand(input).into()
|
||||
|
@ -175,8 +175,8 @@ impl Default for Theme {
|
||||
define_themes! { [light:0, dark:1]
|
||||
application {
|
||||
// Original RGB values (for reference after fixing color-conversion issues)
|
||||
// light: rgb(249,250,251), old-dark: Lcha(0.13,0.014,0.18,1.0), dark: rgb(32,34,36)
|
||||
background = Rgba(0.976,0.98,0.984,1.0) , Rgba(0.125,0.133,0.141,1.0);
|
||||
// light: rgb(231,235,238), old-dark: Lcha(0.13,0.014,0.18,1.0), dark: rgb(32,34,36)
|
||||
background = Rgb::from_base_255(231.0, 235.0, 238.0) , Rgba(0.125,0.133,0.141,1.0);
|
||||
tooltip {
|
||||
hide_delay_duration_ms = 150.0, 150.0;
|
||||
show_delay_duration_ms = 150.0, 150.0;
|
||||
@ -184,43 +184,70 @@ define_themes! { [light:0, dark:1]
|
||||
component_browser {
|
||||
panels_gap = 3.0, 3.0;
|
||||
documentation {
|
||||
width = 369.0, 369.0;
|
||||
width = 400.0, 400.0;
|
||||
}
|
||||
component_list_panel {
|
||||
background_color = Rgba::new(252.0 / 256.0, 254.0 / 255.0, 1.0, 1.0),Rgba::new(252.0 / 256.0, 254.0 / 255.0, 1.0, 1.0);
|
||||
corners_radius = 15.0, 15.0;
|
||||
background_color = Rgb::from_base_255(236.0, 240.0, 242.0),Rgb::from_base_255(236.0, 240.0, 242.0);
|
||||
corners_radius = 16.0, 16.0;
|
||||
grid {
|
||||
width = 400.0, 400.0;
|
||||
height = 398.0, 398.0;
|
||||
padding = 3.0, 3.0;
|
||||
width = 413.0, 413.0;
|
||||
height = 414.0, 414.0;
|
||||
padding = 4.0, 4.0;
|
||||
column_gap = 3.0, 3.0;
|
||||
entry_height = 30.0, 30.0;
|
||||
|
||||
// The `color` values support color types (like `color::Rgba`)
|
||||
// and floating-point numbers. This is possible due to a custom stylesheet
|
||||
// accessor defined for
|
||||
// [`ide_view_component_list_panel_grid::entry::style::Color`] type.
|
||||
// Floating-point numbers mean the alpha multiplier for the "main" color of the
|
||||
// component group.
|
||||
entry {
|
||||
background.color_intensity = 0.2, 0.2;
|
||||
dimmed.color_intensity = 0.5, 0.5;
|
||||
padding = 17.0, 17.0;
|
||||
background.intensity = 0.08, 0.08;
|
||||
dimmed = Rgb::from_base_255(160.0, 163.0, 165.0), Rgb::from_base_255(160.0, 163.0, 165.0);
|
||||
padding = 16.0, 16.0;
|
||||
text {
|
||||
font = "default", "default";
|
||||
size = 12.0, 12.0;
|
||||
color_intensity = 1.0, 1.0;
|
||||
highlight_bold = 0.04, 0.04;
|
||||
font = "mplus1p", "mplus1p";
|
||||
y_offset = 8.0, 8.0;
|
||||
y_offset_header = 5.0, 5.0;
|
||||
x_offset_header = 0.0, 0.0;
|
||||
size = 11.5, 11.5;
|
||||
color = 1.0, 1.0;
|
||||
highlight_bold = 0.01, 0.01;
|
||||
}
|
||||
icon {
|
||||
size = 16.0, 16.0;
|
||||
text_padding = 8.0, 8.0;
|
||||
strong_color_intensity = 1.0, 1.0;
|
||||
weak_color_intensity = 0.5, 0.5;
|
||||
text_padding = 9.0, 9.0;
|
||||
color = 1.0, 1.0;
|
||||
dull_color_alpha = 0.25, 0.25;
|
||||
}
|
||||
special_icons {
|
||||
join.intersection_alpha = 0.7, 0.7;
|
||||
libraries {
|
||||
dull_alpha = 1.0, 1.0;
|
||||
secondary_alpha = 1.0, 1.0;
|
||||
tertiary_alpha = 1.0, 1.0;
|
||||
}
|
||||
marketplace {
|
||||
dull_alpha = 1.0, 1.0;
|
||||
secondary_alpha = 1.0, 1.0;
|
||||
tertiary_alpha = 1.0, 1.0;
|
||||
}
|
||||
computer_vision {
|
||||
highlight = Rgba(0.872,0.267,0.255,1.0) , Rgba(0.872,0.267,0.255,1.0);
|
||||
}
|
||||
system {
|
||||
background = Rgba(0.306,0.306,0.306,1.0) , Rgba(0.306,0.306,0.306,1.0);
|
||||
content = Rgba(0.988,0.996,1.0,1.0) , Rgba(0.988,0.996,1.0,1.0);
|
||||
}
|
||||
}
|
||||
highlight {
|
||||
corners_radius = 12.0, 12.0;
|
||||
hover.color_intensity = 0.4, 0.4;
|
||||
hover.color = 0.4, 0.4;
|
||||
selection {
|
||||
background.color_intensity = 1.0, 1.0;
|
||||
dimmed.color_intensity = 0.5, 0.5;
|
||||
text.color_intensity = 0.2, 0.2;
|
||||
icon_strong.color_intensity = 0.5, 0.5;
|
||||
icon_weak.color_intensity = 0.2, 0.2;
|
||||
background.intensity = 0.75, 0.75;
|
||||
text.color = Rgba::white(), Rgba::white();
|
||||
icon.color = Lcha(1.0, 0.0, 0.0, 1.0), Lcha(1.0, 0.0, 0.0, 1.0);
|
||||
}
|
||||
}
|
||||
shadow = shadow , shadow;
|
||||
@ -235,66 +262,80 @@ define_themes! { [light:0, dark:1]
|
||||
}
|
||||
|
||||
group_colors {
|
||||
group_0 = Rgba(43.0 / 255.0, 117.0 / 255.0, 239.0 / 255.0, 1.0),Rgba(43.0 / 255.0, 117.0 / 255.0, 239.0 / 255.0, 1.0);
|
||||
group_1 = Rgba(62.0 / 255.0, 139.0 / 255.0, 41.0 / 255.0, 1.0),Rgba(62.0 / 255.0, 139.0 / 255.0, 41.0 / 255.0, 1.0);
|
||||
group_2 = Rgba(192.0 / 255.0, 71.0 / 255.0, 171.0 / 255.0, 1.0),Rgba(192.0 / 255.0, 71.0 / 255.0, 171.0 / 255.0, 1.0);
|
||||
group_3 = Rgba(121.0 / 255.0, 126.0 / 255.0, 37.0 / 255.0, 1.0),Rgba(121.0 / 255.0, 126.0 / 255.0, 37.0 / 255.0, 1.0);
|
||||
group_4 = Rgba(181.0 / 255.0, 97.0 / 255.0, 35.0 / 255.0, 1.0),Rgba(181.0 / 255.0, 97.0 / 255.0, 35.0 / 255.0, 1.0);
|
||||
group_5 = Rgba(61.0 / 255.0, 146.0 / 255.0, 206.0 / 255.0, 1.0),Rgba(61.0 / 255.0, 146.0 / 255.0, 206.0 / 255.0, 1.0);
|
||||
// Yellow
|
||||
group_0 = Rgb::from_base_255(134.0, 135.0, 43.0), Rgb::from_base_255(134.0, 135.0, 43.0);
|
||||
// Green
|
||||
group_1 = Rgb::from_base_255(63.0, 139.0, 41.0), Rgb::from_base_255(63.0, 139.0, 41.0);
|
||||
// Light Blue
|
||||
group_2 = Rgb::from_base_255(54.0, 122.0, 185.0), Rgb::from_base_255(54.0, 122.0, 185.0);
|
||||
// Pink
|
||||
group_3 = Rgb::from_base_255(193.0, 71.0, 171.0), Rgb::from_base_255(193.0, 71.0, 171.0);
|
||||
// Blue
|
||||
group_4 = Rgb::from_base_255(43.0, 117.0, 239.0), Rgb::from_base_255(43.0, 117.0, 239.0);
|
||||
// Orange
|
||||
group_5 = Rgb::from_base_255(181.0, 97.0, 35.0), Rgb::from_base_255(181.0, 97.0, 35.0);
|
||||
local_scope_group = Rgba::new(0.0, 0.42, 0.64, 1.0),Rgba::new(0.0, 0.42, 0.64, 1.0);
|
||||
}
|
||||
}
|
||||
menu_height = 35.0, 35.0;
|
||||
menu_height = 45.0, 45.0;
|
||||
menu_divider_color = Rgb(0.7804, 0.7804, 0.7804), Rgb(0.7804, 0.7804, 0.7804);
|
||||
navigator_divider_color = Rgb(0.7804, 0.7804, 0.7804), Rgb(0.7804, 0.7804, 0.7804);
|
||||
menu_divider_height = 0.5,0.5;
|
||||
navigator_divider_width = 0.5,0.5;
|
||||
menu {
|
||||
breadcrumbs {
|
||||
crop_left = 9.0, 9.0;
|
||||
crop_left = 8.0, 8.0;
|
||||
crop_right = 3.0, 3.0;
|
||||
height = 28.0, 28.0;
|
||||
height = 44.0, 44.0;
|
||||
separator {
|
||||
width = 8.0, 8.0;
|
||||
height = 6.0, 6.0;
|
||||
color = Rgba(0.0, 0.0, 0.0, 0.15), Rgba(0.0, 0.0, 0.0, 0.15);
|
||||
offset_x = 1.0, 1.0;
|
||||
offset_y = -2.0, -2.0;
|
||||
}
|
||||
ellipsis {
|
||||
background_width = 24.0, 24.0;
|
||||
background_height = 10.0, 10.0;
|
||||
background_corners_radius = 16.0, 16.0;
|
||||
background_color = Rgb(0.89, 0.89, 0.9), Rgb(0.89, 0.89, 0.9);
|
||||
circles_color = Rgb(0.74, 0.74, 0.75), Rgb(0.74, 0.74, 0.75);
|
||||
background_width = 28.0, 28.0;
|
||||
background_height = 16.0, 16.0;
|
||||
background_corners_radius = 100.0, 100.0;
|
||||
background_color = Rgba(0.0, 0.0, 0.0, 0.04), Rgba(0.0, 0.0, 0.0, 0.04);
|
||||
circles_color = Rgba(0.0, 0.0, 0.0, 0.17), Rgba(0.0, 0.0, 0.0, 0.17);
|
||||
circles_radius = 2.0, 2.0;
|
||||
circles_gap = 2.0, 2.0;
|
||||
offset_x = 0.0, 0.0;
|
||||
offset_y = -2.0, -2.0;
|
||||
}
|
||||
entry {
|
||||
margin = 1.0, 1.0;
|
||||
hover_color = Rgba(0.0, 0.0, 0.0, 0.0), Rgba(0.0, 0.0, 0.0, 0.0);
|
||||
font = "default", "default";
|
||||
text_padding_left = 7.0, 7.0;
|
||||
text_size = 12.0, 12.0;
|
||||
selected_color = Rgba(0.5, 0.5, 0.51, 1.0), Rgba(0.5, 0.5, 0.51, 1.0);
|
||||
font = "mplus1p", "mplus1p";
|
||||
text_y_offset = 6.0, 6.0;
|
||||
text_padding_left = 0.0, 0.0;
|
||||
text_size = 11.5, 11.5;
|
||||
selected_color = Rgba(0.0, 0.0, 0.0, 0.46), Rgba(0.0, 0.0, 0.0, 0.46);
|
||||
highlight_corners_radius = 15.0, 15.0;
|
||||
greyed_out_color = Rgba(0.79, 0.79, 0.8, 1.0), Rgba(0.79, 0.79, 0.8, 1.0);
|
||||
greyed_out_color = Rgba(0.0, 0.0, 0.0, 0.15), Rgba(0.0, 0.0, 0.0, 0.15);
|
||||
}
|
||||
}
|
||||
}
|
||||
navigator {
|
||||
width = 37.0, 37.0;
|
||||
icon_strong_color = Rgba(0.569,0.584,0.612,1.0), Rgba(0.569,0.584,0.612,1.0);
|
||||
icon_weak_color = Rgba(0.569,0.584,0.612,1.0), Rgba(0.569,0.584,0.612,1.0);
|
||||
top_padding = -3.0, -3.0;
|
||||
bottom_padding = 7.0, 7.0;
|
||||
list_view_width = 39.0, 39.0;
|
||||
list_view {
|
||||
background = Rgba::transparent() , Rgba::transparent();
|
||||
highlight = Rgb(0.96,0.85,0.725) , Rgb(0.96,0.85,0.725); // rgb(245,217,185)
|
||||
highlight {
|
||||
height = 29.0, 29.0;
|
||||
corner_radius = 10.0, 10.0;
|
||||
width = 41.0, 41.0;
|
||||
button_size = 32.0, 32.0;
|
||||
top_padding = 8.0, 8.0;
|
||||
bottom_padding = 4.0, 4.0;
|
||||
hover_color = Rgba::transparent(), Rgba::transparent();
|
||||
highlight {
|
||||
color = Rgb(0.96,0.85,0.725) , Rgb(0.96,0.85,0.725); // rgb(245,217,185)
|
||||
size = 29.0, 29.0;
|
||||
corners_radius = 12.0, 12.0;
|
||||
}
|
||||
buttons {
|
||||
active {
|
||||
local_scope = Rgb::from_base_255(250.0, 149.0, 31.0), Rgb::from_base_255(250.0, 149.0, 31.0);
|
||||
submodules = Rgb::from_base_255(250.0, 149.0, 31.0), Rgb::from_base_255(250.0, 149.0, 31.0);
|
||||
popular = Rgb::from_base_255(250.0, 149.0, 31.0), Rgb::from_base_255(250.0, 149.0, 31.0);
|
||||
}
|
||||
entry {
|
||||
padding = 14.5, 14.5;
|
||||
}
|
||||
padding = 5.0, 5.0;
|
||||
inactive = Rgba(0.0, 0.0, 0.0, 0.15), Rgba(0.0, 0.0, 0.0, 0.15);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -169,7 +169,8 @@ ensogl_core::define_endpoints_2! {
|
||||
set_column_width((Col, f32)),
|
||||
/// Set the entries parameters.
|
||||
set_entries_params(EntryParams),
|
||||
/// Set the entries size. All entries have the same size.
|
||||
/// Set the entry size. All entries have the same height, but the width can be changed
|
||||
/// using [`set_column_width`] input or [`EntryFrp::override_column_width`] output.
|
||||
set_entries_size(Vector2),
|
||||
/// Set the layer for any texts rendered by entries. The layer will be passed to entries'
|
||||
/// constructors. **Performance note**: This will re-instantiate all entries.
|
||||
|
Loading…
Reference in New Issue
Block a user