-
Notifications
You must be signed in to change notification settings - Fork 326
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
Conversation
1563ca8
to
8c6e04d
Compare
3648923
to
571b66a
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Really nice code, beautiful description and clear video! Thanks for it :)
app/gui/view/component-browser/component-list-panel/breadcrumbs/src/lib.rs
Outdated
Show resolved
Hide resolved
data.label_thin.set_font <+ font; | ||
data.label_bold.set_font <+ font; | ||
|
||
natural_entry_width <- data.label_bold.width.map2(&text_offset, |w, offset| w + offset); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
what is "natural" width?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Natural here refers to the width that the entry would like to take by itself, if no external limits were in place. It is based on text label width, with paddings applied.
I'm not sure what could be a more descriptive name, without being too verbose. Maybe desired_entry_width
, but I'm not sure if that's better/easier to understand.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In such a case, just add a comment there. And yeah, desiredentry_width
sounds better.
// === Keyboard navigation === | ||
model.grid.accept_selected_entry <+ input.toggle_focused_entry; | ||
model.grid.move_selection_up <+ input.focus_previous_entry; | ||
model.grid.move_selection_down <+ input.focus_next_entry; | ||
model.grid.select_entry <+ model.grid.entry_hovered; | ||
|
||
has_focused_entry <- model.grid.entry_selected.map(|entry| entry.is_some()); | ||
model.grid.select_entry <+ input.focus_previous_entry.gate_not(&has_focused_entry) | ||
.map2(&visible_range, |_, range| Some(((range.end - 1).max(range.start), 0))); | ||
model.grid.select_entry <+ input.focus_next_entry.gate_not(&has_focused_entry) | ||
.map2(&visible_range, |_, range| Some((range.start, 0))); | ||
|
||
|
||
// === Initialization === |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please refactor these FRP inits to several init functions.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I tried to do that now, but it turns out to be massively verbose. Because the component::Frp
trait doesn't use self
value for initialization, each init function needs to receive network, private frp Api and model as separate arguments. Then, some sections use streams defined in previous sections (on purpose). Spitting it into multiple methods also means that I need to weave those streams through returns and extra params.
I think leaving it as a single FRP initialization block is much easier to follow and it is definitely less verbose.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think that it's a mistake that component::Frp
trait doesnt use self for initialization. We should fix it. I understand that we might not do it during this task, but on the other hand, this will become a big chunk of coede no one will ever touch again. I'm very afraid of such code chunks, we already have several of them. That's why I'd prefer to hve this refactored. we can do it in another PR, but let's at least agree that before drop-down widgets are finished, this will be refactored, ok?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm fine to change that. I'll need to understand the reasons behind original design of it, but in general I agree that it should change to allow easier code splitting. Let's do that as part of future widget PRs.
f8c255e
to
04392c3
Compare
data.label_thin.set_font <+ font; | ||
data.label_bold.set_font <+ font; | ||
|
||
natural_entry_width <- data.label_bold.width.map2(&text_offset, |w, offset| w + offset); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In such a case, just add a comment there. And yeah, desiredentry_width
sounds better.
// === Keyboard navigation === | ||
model.grid.accept_selected_entry <+ input.toggle_focused_entry; | ||
model.grid.move_selection_up <+ input.focus_previous_entry; | ||
model.grid.move_selection_down <+ input.focus_next_entry; | ||
model.grid.select_entry <+ model.grid.entry_hovered; | ||
|
||
has_focused_entry <- model.grid.entry_selected.map(|entry| entry.is_some()); | ||
model.grid.select_entry <+ input.focus_previous_entry.gate_not(&has_focused_entry) | ||
.map2(&visible_range, |_, range| Some(((range.end - 1).max(range.start), 0))); | ||
model.grid.select_entry <+ input.focus_next_entry.gate_not(&has_focused_entry) | ||
.map2(&visible_range, |_, range| Some((range.start, 0))); | ||
|
||
|
||
// === Initialization === |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think that it's a mistake that component::Frp
trait doesnt use self for initialization. We should fix it. I understand that we might not do it during this task, but on the other hand, this will become a big chunk of coede no one will ever touch again. I'm very afraid of such code chunks, we already have several of them. That's why I'd prefer to hve this refactored. we can do it in another PR, but let's at least agree that before drop-down widgets are finished, this will be refactored, ok?
let garbage = &mut *self.garbage.borrow_mut(); | ||
garbage.before_pixel_update.append(&mut garbage.before_pixel_sync); | ||
} | ||
|
||
/// Pixel value retrieved (the point 3 in [`Collector`] docs). | ||
#[profile(Debug)] | ||
pub fn pixel_updated(&self) { | ||
let mut garbage = self.garbage.borrow_mut(); | ||
let objects_being_moved = std::mem::take(&mut garbage.before_pixel_update); | ||
garbage.before_mouse_events.extend(objects_being_moved); | ||
let garbage = &mut *self.garbage.borrow_mut(); | ||
garbage.before_mouse_events.append(&mut garbage.before_pixel_update); | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
- Why these changes? The previous code was a little bit more verbose but also more explanatory. I'm OK with the shorter version, although I like the earlier one a little bit more. Before making this change, talk with the code author aobut it, with Adam.
- There is a logic change. The old code was removing
garbage.before_pixel_update
, the new code is not. Why? There is no explanation in this PR why such change was introduced and what happened here.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Vec::append
also does remove the items from source vector. This is how it is a move, not a clone. That way it is not a logic change.
The actual significant change here is that the vector is not reallocated every time a garbage collector is run. Instead, the vector capacity remains allocated and is ready to be reused for future garbage.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@farmaazon could you take a look at this part to verify that the changes are correct?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm ok with this change. I just wasn't aware of append
method.
320d927
to
1d3029d
Compare
…les to demo scene
3d84ef1
to
4a670e8
Compare
@@ -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; |
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
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.
Pull Request Description
Implements https://www.pivotaltracker.com/n/projects/2539304/stories/184023380
Dropdown component. Planned to be used in nodes as a single and multiple selection widget, both for static and dynamically loaded values. Initial support is focused on static data, with limited support for dynamic sources. Notably, loading states are not supported yet. Full support for that is planned to be added later with widget lazy-loading.
Important Notes
Implementing the dropdown on top of grid-view have uncovered some assumptions around grid-view layers. It was assumed to always be a part of the component browser. Removing that assumption required a mechanism for propagating camera update information through layer tree. This is now implemented using a
camera_parent
layer field. Ideally each layer should simply have at most a single parent, and camera inheritance would follow that. That refactor turned out to be quite involved, so right now the simpler temporary solution is introduced in order to not delay this PR further.Checklist
Please include the following checklist in your PR:
Scala,
Java,
and
Rust
style guides.
./run ide build
and./run ide watch
.