Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Grid-view based dropdown component #3985

Merged
merged 36 commits into from
Dec 22, 2022
Merged
Show file tree
Hide file tree
Changes from 30 commits
Commits
Show all changes
36 commits
Select commit Hold shift + click to select a range
c0475c3
dropdown with correct scroll layers behaviour
Frizi Nov 24, 2022
f8d0e2a
internal dropdown entry cache and initial multiselect
Frizi Nov 28, 2022
d3590a4
remove bad import
Frizi Nov 28, 2022
93d33ba
New dropdown FRP api and static list support
Frizi Dec 7, 2022
d69e822
adjust dropdown style to better match design
Frizi Dec 9, 2022
eb4ff64
fix deselection of dropdown values and add more cross-connected examp…
Frizi Dec 9, 2022
596c8e8
add tag_values to suggestion entry and propagate it to node widgets
Frizi Dec 12, 2022
796ad19
implement batch node and cleanup FRP network of dropdown
Frizi Dec 13, 2022
dc6bf70
isolate standalone dropdown component changeset
Frizi Dec 14, 2022
4e009ca
automatic dropdown width change
Frizi Dec 14, 2022
b165a0e
fix format issues
Frizi Dec 15, 2022
67792fa
fix clippy issues
Frizi Dec 15, 2022
855f214
always calculate dropdown width based on bold text labels
Frizi Dec 15, 2022
77d7eae
remove leftover debug code
Frizi Dec 15, 2022
a866c6d
add changelog entry
Frizi Dec 15, 2022
b55e9b2
handle keyboard input on empty selection
Frizi Dec 15, 2022
29293d2
adjust wasm size limit
Frizi Dec 15, 2022
6112ca8
adjust wasm size limit
Frizi Dec 15, 2022
7738433
add missing docs and restrict unnecessarily public apis
Frizi Dec 15, 2022
0a55998
remove unused type and fix cache size check
Frizi Dec 15, 2022
4cf4b92
remove unused "mask" hardcoded layer
Frizi Dec 15, 2022
b1e33fd
fix grid view masked layer visibility
Frizi Dec 15, 2022
7b99768
remove magic numbers and fix spacing
Frizi Dec 19, 2022
8934290
only allow single layer parent and refactor camera inheritance
Frizi Dec 19, 2022
1e76c5e
fix set_size after rebase
Frizi Dec 19, 2022
9f917b7
limit code line width in dropdown frp init
Frizi Dec 19, 2022
d961721
fix dropdown min and max size after rebase, add more cases to demo scene
Frizi Dec 20, 2022
389fc98
update according to review comments
Frizi Dec 20, 2022
cef13a0
keep using for_each_sublayer, remove `Group` from docs.
Frizi Dec 20, 2022
4a670e8
adjust wasm size limit
Frizi Dec 20, 2022
427a89c
correctly attach mask layer in complex-shape-system demo scene
Frizi Dec 21, 2022
ecd1fc6
move LayerFlags definition before Layer
Frizi Dec 21, 2022
a2ba44f
add scissor test back to complex-shape-system example
Frizi Dec 21, 2022
5c56247
fix rendering of shape instance children in focus_management scene
Frizi Dec 21, 2022
8178808
Update sprite parents in `ShapeInstance::swap`
Frizi Dec 21, 2022
7ace2dc
Merge branch 'develop' into wip/frizi/dropdown-component-184023380
mergify[bot] Dec 22, 2022
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -112,8 +112,11 @@
steps towards migrating the Cloud Dashboard from the existing React (web-only)
implementation towards a shared structure that can be used in both the Desktop
and Web versions of the IDE.
- [Added a new component: Dropdown][3985]. A list of selectable labeled entries,
suitable for single and multi-select scenarios.

[3857]: https://github.com/enso-org/enso/pull/3857
[3985]: https://github.com/enso-org/enso/pull/3985

#### Enso Standard Library

Expand Down
28 changes: 28 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Original file line number Diff line number Diff line change
Expand Up @@ -128,7 +128,7 @@ struct Layers {
impl Layers {
/// Constructor.
pub fn new(base_layer: &Layer) -> Self {
let mask = Layer::new_with_cam("mask", &base_layer.camera());
let mask = base_layer.create_mask_sublayer("mask");
let main = base_layer.create_sublayer("main");
let text = main.create_sublayer("text");
main.set_mask(&mask);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -265,7 +265,7 @@ impl component::Model for Model {
let enterable_elements = default();
let colors = default();
let requested_section_info = default();
let base_layer = &app.display.default_scene.layers.node_searcher_text;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why this name change? Did the logic of this layer change?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This looked like a mistake. Using text layer for whole grid is surprising and different from what all demo scenes do. Individual sublayers created here are guaranteeing correct draw order anyway, including the dedicated text sublayer created in the grid-view.

With this change, node_searcher_text is only used by the old searcher, so it could be removed with it.

let base_layer = &app.display.default_scene.layers.node_searcher;
let grid_layer = base_layer.create_sublayer("grid_layer");
let selection_layer = base_layer.create_sublayer("selection_layer");
display_object.add_child(&grid);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -340,7 +340,7 @@ impl Model {

port_shape.set_x(unit * index as f32);
if DEBUG {
port_shape.set_y(DEBUG_PORT_OFFSET)
port_shape.set_y(DEBUG_PORT_OFFSET);
}

if is_header {
Expand Down
4 changes: 2 additions & 2 deletions app/gui/view/graph-editor/src/component/node/input/port.rs
Original file line number Diff line number Diff line change
Expand Up @@ -102,8 +102,8 @@ impl Shape {
let viz = viz::View::new();

let width_padded = size.x + 2.0 * PADDING_X;
hover.set_size(Vector2::new(width_padded, hover_height));
viz.set_size(Vector2::new(width_padded, size.y));
Frizi marked this conversation as resolved.
Show resolved Hide resolved
hover.set_size((width_padded, hover_height));
viz.set_size((width_padded, size.y));
hover.set_x(size.x / 2.0);
viz.set_x(size.x / 2.0);
viz.color.set(color::Rgba::transparent().into());
Expand Down
2 changes: 1 addition & 1 deletion build-config.yaml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# Options intended to be common for all developers.

wasm-size-limit: 15.20 MiB
wasm-size-limit: 15.34 MiB

required-versions:
cargo-watch: ^8.1.1
Expand Down
1 change: 1 addition & 0 deletions lib/rust/ensogl/component/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ edition = "2021"
[dependencies]
ensogl-button = { path = "button" }
ensogl-drop-down-menu = { path = "drop-down-menu" }
ensogl-drop-down = { path = "drop-down" }
ensogl-drop-manager = { path = "drop-manager" }
ensogl-file-browser = { path = "file-browser" }
ensogl-flame-graph = { path = "flame-graph" }
Expand Down
13 changes: 13 additions & 0 deletions lib/rust/ensogl/component/drop-down/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
[package]
name = "ensogl-drop-down"
version = "0.1.0"
authors = ["Enso Team <[email protected]>"]
edition = "2021"

[dependencies]
enso-frp = { path = "../../../frp" }
ensogl-core = { path = "../../core" }
ensogl-hardcoded-theme = { path = "../../app/theme/hardcoded" }
ensogl-grid-view = { path = "../grid-view" }
ensogl-text = { path = "../text" }
ensogl-gui-component = { path = "../gui" }
208 changes: 208 additions & 0 deletions lib/rust/ensogl/component/drop-down/src/entry.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,208 @@
//! A module defining the drop-down specific grid-view [`Entry`].

use ensogl_core::display::shape::*;
use ensogl_grid_view::prelude::*;

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::entry;
use ensogl_grid_view::entry::EntryFrp;
use ensogl_text as text;



// ===================
// === EntryParams ===
// ===================

/// The parameters of [`Dropdown`]` grid entries.
#[allow(missing_docs)]
#[derive(Clone, Debug)]
pub struct EntryParams {
pub focus_color: color::Lcha,
pub font: ImString,
pub text_offset: f32,
pub text_size: text::Size,
pub text_color: color::Lcha,
pub selected_text_color: color::Lcha,
pub corners_radius: f32,
pub min_width: f32,
pub max_width: f32,
}

impl Default for EntryParams {
fn default() -> Self {
Self {
focus_color: color::Lcha::from(color::Rgba(1.0, 1.0, 1.0, 0.2)),
font: text::font::DEFAULT_FONT.into(),
text_offset: 7.0,
text_size: text::Size(12.0),
text_color: color::Lcha::from(color::Rgba(1.0, 1.0, 1.0, 0.7)),
selected_text_color: color::Lcha::from(color::Rgba(1.0, 1.0, 1.0, 1.0)),
corners_radius: 0.0,
min_width: 40.0,
max_width: 160.0,
}
}
}



// ==================
// === EntryModel ===
// ==================

/// The model of [`SimpleGridView`]`s entries.
#[allow(missing_docs)]
#[derive(Clone, CloneRef, Debug, Default)]
pub struct EntryModel {
pub text: ImString,
pub selected: Immutable<bool>,
}

impl EntryModel {
/// Create a new entry model with given text contents.
pub fn new(text: ImString, selected: bool) -> Self {
Self { text, selected: Immutable(selected) }
}
}



// =============
// === Entry ===
// =============

// === EntryData ===

/// An internal structure of [`Entry`], which may be passed to FRP network.
#[allow(missing_docs)]
#[derive(Clone, Debug)]
pub struct EntryData {
display_object: display::object::Instance,
label_thin: text::Text,
label_bold: text::Text,
}

impl EntryData {
fn new(app: &Application, text_layer: Option<&Layer>) -> Self {
let display_object = display::object::Instance::new();
let label_thin = app.new_view::<ensogl_text::Text>();
let label_bold = app.new_view::<ensogl_text::Text>();
label_thin.set_long_text_truncation_mode(true);
label_bold.set_long_text_truncation_mode(true);
label_bold.set_property_default(text::Weight::Bold);
display_object.add_child(&label_thin);
if let Some(layer) = text_layer {
label_thin.add_to_scene_layer(layer);
label_bold.add_to_scene_layer(layer);
}
Self { display_object, label_thin, label_bold }
}

fn update_selected(&self, selected: bool) {
if selected {
self.display_object.remove_child(&self.label_thin);
self.display_object.add_child(&self.label_bold);
} else {
self.display_object.remove_child(&self.label_bold);
self.display_object.add_child(&self.label_thin);
}
}

fn update_layout(&self, contour: entry::Contour, text_size: text::Size, text_offset: f32) {
let label_pos = Vector2(text_offset - contour.size.x / 2.0, text_size.value / 2.0);
self.label_thin.set_xy(label_pos);
self.label_bold.set_xy(label_pos);
}
}


// === Entry ===

/// A [`SimpleGridView`] entry - a label with background.
#[derive(Clone, CloneRef, Debug)]
pub struct Entry {
frp: EntryFrp<Self>,
data: Rc<EntryData>,
}

impl ensogl_grid_view::Entry for Entry {
type Model = EntryModel;
type Params = EntryParams;

fn new(app: &Application, text_layer: Option<&Layer>) -> Self {
let data = Rc::new(EntryData::new(app, text_layer));
let frp = EntryFrp::<Self>::new();
let input = &frp.private().input;
let out = &frp.private().output;
let network = frp.network();

out.hover_highlight_color.emit(color::Lcha::transparent());

enso_frp::extend! { network
size <- input.set_size.on_change();
font <- input.set_params.map(|p| p.font.clone_ref()).on_change();
text_offset <- input.set_params.map(|p| p.text_offset).on_change();
focus_color <- input.set_params.map(|p| p.focus_color).on_change();
text_color <- input.set_params.map(|p| p.text_color).on_change();
text_size <- input.set_params.map(|p| p.text_size).on_change();
corners_radius <- input.set_params.map(|p| p.corners_radius).on_change();
selected_text_color <- input.set_params.map(|p| p.selected_text_color).on_change();
max_width <- input.set_params.map(|p| p.max_width).on_change();

contour <- all_with(&size, &corners_radius, |&size, &corners_radius|
entry::Contour { size, corners_radius }
);
layout <- all(contour, text_size, text_offset);
eval layout ((&(c, ts, to)) data.update_layout(c, ts, to));
selected <- input.set_model.map(|m| *m.selected);
eval selected ((&s) data.update_selected(s));

text_size <- text_size.ref_into_some();
data.label_thin.set_property_default <+ text_size;
data.label_bold.set_property_default <+ text_size;
data.label_thin.set_property_default <+ text_color.ref_into_some();
data.label_bold.set_property_default <+ selected_text_color.ref_into_some();
data.label_thin.set_font <+ font;
data.label_bold.set_font <+ font;

desired_entry_width <- data.label_bold.width.map2(&text_offset, |w, offset| w + offset);
limited_entry_width <- desired_entry_width.map2(&input.set_params, |width, params| {
// Using min/max to avoid a panic in clamp when min_width > max_width. In those
// cases, the max value is returned instead.
#[allow(clippy::manual_clamp)]
width.max(params.min_width).min(params.max_width)
});
trace limited_entry_width;
out.minimum_column_width <+ limited_entry_width;

view_width <- max_width.map2(&text_offset, |width, offset| Some(width - offset));
data.label_thin.set_view_width <+ view_width;
data.label_bold.set_view_width <+ view_width;

content <- input.set_model.map(|m| m.text.clone_ref());;
data.label_thin.set_content <+ content;
data.label_bold.set_content <+ content;

out.contour <+ contour;
out.highlight_contour <+ contour;
out.selection_highlight_color <+ focus_color;
}
Self { frp, data }
}

fn frp(&self) -> &EntryFrp<Self> {
&self.frp
}
}

impl display::Object for Entry {
fn display_object(&self) -> &display::object::Instance {
&self.data.display_object
}
}
Loading