-
Notifications
You must be signed in to change notification settings - Fork 323
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
Implement creating nodes by dropping a connection #3231
Conversation
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.
Some implementation-related explanations.
app/gui/view/src/project.rs
Outdated
|
||
/// The information needed to setup Searcher Controller. | ||
#[derive(Clone, CloneRef, Copy, Debug, PartialEq)] | ||
pub enum SearcherInput { |
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 can't think of a better name and/or location for this enum. It is used both for created nodes and for edited nodes (all cases when the searcher is opened), and it can't be placed in controllers, as it must be used in the View's FRP.
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 the placement is ok, if we change the meaning of this structure a little: not "The information needed to set up the Searcher Controller." but rather "An enum describing how the searcher was opened".
And adjust the name to that: this is not actually the searcher input, because the edge is not a part of edge input (and is a part of one variant of this enum). WayOfOpeningSearcher
is better, or something more compact if you find.
@@ -2426,14 +2426,49 @@ fn new_graph_editor(app: &Application) -> GraphEditor { | |||
} | |||
|
|||
|
|||
// === Add Node === | |||
|
|||
frp::extend! { network |
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 FRP piece is just moved to make has_detached_edge
being available for Node Editing
section.
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.
Thank you for this comment, very helpful during the PR review ❤️
graph: &controller::Graph, | ||
) -> Option<Self> { | ||
let id = *nodes.first()?; | ||
pub fn new(id: double_representation::node::Id, graph: &controller::Graph) -> Option<Self> { |
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.
Passing selected_nodes
everywhere just to take the first of it doesn't make sense to me. So now it receives just one node, either selected one or the source node of the connection (depending on Mode)
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 there was an idea long ago, that the new node in searcher shall take not only "this"/"self" argument of selected node, but also arguments from other selected nodes. But for now, we can leave your fix, according to the YAGNI rule.
adding_new_node (bool), | ||
searcher_input (Option<NodeId>), |
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.
searcher_opened
wasn't used before at all, so I decided to replace usages of searcher_input
with it.
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.
Few questions / requests:
-
Please show all corner cases - press-drag-drop is one way of creating the connection. What with click (press and release) , move mouse, click again? What with connections from input ports? What when you are disconnecting existing connections and dropping them? Please show ALL possible corner cases on the video.
-
The design doc describes the desired behavior of this feature, including alignment of the newly created node. Is the Magnet Alignment algorithm used here as described in the doc? If no, is it planned as next PR? I did not find any information about it in the PR description. Please remember, that you should always carefully read the design doc (or at least a part of it) when implementing a feature, and there the alignment algorithm is described.
Sure, I will record additional footage to cover that. How do you distinguish between click-move-click and press-drag-drop on a video? I actually used click-move-click in the posted recording. Also, what is
This is a PR for the task #180873545, which includes only creating new nodes. The alignment will be covered in the task #180887079, though it might be modified after the Design Doc will be finally ready. Should I describe future plans for things that are not included in the PR? |
Please do! It's important to cover in the video all possible corner cases (as described in our process doc) 🙂
I was sure that the mouse cursor gets bigger when you stop pressing it, but I was wrong - it is small even if you don't press it (and that's correct behavior!).
Wow, it was so smooth and fast that I was sure it was press-drag-release! TBH I. was never able to use this method so smoothly! 😄
You can disconnect node in such a way that the connection is still connected to input port, but not to the output port.
Regarding corner cases, double check with the task designer if you covered all the corner cases before requesting the review, please. This will drastically shorten the review process – otherwise, the PR needs to be rejected before the code is even checked, the explanation needs to be described, you have to fix it, then it needs to be re-reviewed – this just should not be needed. There are sections about it in the design doc (I know it was being slightly modified for the last few days, but these things were already described there), like this one:
Amazing, thanks!
No need for that. And I dont think anything will change. The design doc is pretty stable since Monday – we had a few calls about look and feel of some advanced cases, but no deep changes were done there during the last 2 days. I will update some old screenshots and we are ready to merge it already. So you should be able to get all the needed info from the current stage.
If you are implementing a feature that is tightly connected to another one, it is cool if you mention that "this doesn't cover the node alignment, which will be implemented in another PR" – this makes everyone sure that this is by design and not accidental. Of course you don't have to – in such a case, me or someone else would just sometimes ask additional questions about it :) |
Cool, I was a bit confused that you separated nodes' disconnection and dropping connection from input ports in your first reply, so I was afraid that I missed this opportunity. It works as the design doc states: dropping the source endpoint does nothing, dropping the target one creates a new node. I attached an additional screen recording and expanded PR description, hope it's better now.
Yeah, it was an oversight from my side, I was expecting that Task Reviewer will verify if the task description matches implemented behavior and he will be in a full context of the task. I should've simplified his work anyway. |
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.
Minor comments. I'll still need to go through the test scenario.
app/gui/src/presenter/project.rs
Outdated
@@ -9,6 +9,7 @@ use crate::executor::global::spawn_stream_handler; | |||
use crate::presenter::graph::ViewNodeId; | |||
use enso_frp as frp; | |||
use ide_view as view; | |||
use view::project::WayOfOpeningSearcher; |
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 fix import ordering.
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.
Ordering is actually matching our style guide, I added a few new lines here and there
app/gui/view/graph-editor/src/lib.rs
Outdated
// === Node Editing === | ||
|
||
frp::extend! { network | ||
// Clicking on background either drops dragged edge or aborts node editing. | ||
let background_selected = touch.background.selected.clone_ref(); | ||
edge_being_dragged <- has_detached_edge.sample(&background_selected); |
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 this name does not quite tell what the FRP node is. It suggests something like "a bool property telling if I'm during edge dragging". Instead, it should be something like "was_edge_dragged_when_background_selected". Or "was_edge_dragged" is sufficient, as we use this name only locally.
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.
Renamed. As it is used only locally, it's okay to use quite a long name I think, so I stick to it.
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.
Regressions found during tests:
- I cannot select an entry with tab key (it should put the suggestion in the searcher input and continue editing). Instead, the new searcher is opened. I think that because the output used to block shortcut was renamed or removed, I missed it during my first review.
- For similar reason, quitting searcher with Escape does not work as well.
also fix review suggestions
The new video looks amazing! I did not check the code yet, however, let's first fix the regressions found by Adam + could you also add a functionality that pressing enter while dragging the connection removes it? I just realised that right now it is impossible to drop a connection when dragging it without opening the searcher, which is bad. |
@wdanilo I fixed the regression and implemented this. I'd appreciate FRP code review if you have a free time. |
app/gui/CHANGELOG.md
Outdated
@@ -2,6 +2,10 @@ | |||
|
|||
#### Visual Environment | |||
|
|||
- [New nodes can be created by dragging and dropping a connection on the | |||
scene.][3231] | |||
- [Node connections can be dropped by pressing the Enter key while dragging |
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.
For me, it would be much more natural to press Esc
instead.
Also, update the shortcuts list in the docs
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.
Of course this should be escape, I've written enter completely by accident. Thanks for catching it!
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.
Fixed it :)
app/gui/src/presenter/searcher.rs
Outdated
@@ -201,4 +202,23 @@ impl Searcher { | |||
let entry = controller.actions().list().and_then(|l| l.get_cloned(entry)); | |||
entry.map_or(false, |e| matches!(e.action, Example(_))) | |||
} | |||
|
|||
/// Select source node for node creation. It would be either: | |||
/// 1. The source node of the dragged connection, dropping which leads to the node creation. |
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 don't think this sentence is logically correct or just iff it is correct English sentence. What does mean "dropping which leads to the node creation"? Could you rephrase it, please?
app/gui/src/presenter/searcher.rs
Outdated
let id = edge_source.map(|source| source.node_id); | ||
id.map(|id| graph_presenter.ast_node_of_view(id)).flatten() |
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.
Make var names a little bit better. I understand what edge
is, I understand edge_source
, but then we have id
- this is ID of what? the var name should explain that better imo.
app/gui/src/presenter/searcher.rs
Outdated
fn source_node( | ||
view: &view::project::View, | ||
graph_presenter: &presenter::Graph, | ||
way_of_opening_searcher: &WayOfOpeningSearcher, | ||
) -> Option<Uuid> { |
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 function name does not reflect what it does. The name source_node
suggests that it returns a source node, but it does not. It also does not return the node's id. It returns the AST node id, is that correct? This should be well explained in the doc.
@@ -2426,14 +2426,49 @@ fn new_graph_editor(app: &Application) -> GraphEditor { | |||
} | |||
|
|||
|
|||
// === Add Node === | |||
|
|||
frp::extend! { network |
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.
Thank you for this comment, very helpful during the PR review ❤️
app/gui/view/graph-editor/src/lib.rs
Outdated
// === Node Editing === | ||
|
||
frp::extend! { network | ||
// Clicking on background either drops dragged edge or aborts node editing. | ||
let background_selected = touch.background.selected.clone_ref(); |
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 do you need clone of it if you are only using it as reference and then dropping it?
drop_edges <- any (drop_on_bg_up,click_to_drop_edge); | ||
|
||
edge_dropped_to_create_node <= drop_edges.map(f_!(model.edges_with_detached_targets())); | ||
out.source.on_edge_drop_to_create_node <+ edge_dropped_to_create_node; |
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.
Do you need to connect it here to out....
? We try to connect to out minimal amounts of things, and there was already a logic that was opening the searcher – are you not able to plug into this existing logic here? Maybe not, but it's a little bit hard for me to understand what this code does because its hard to understand the var names now.
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.
The searcher is opened when the node enters being_edited
state - but the Project View is creating new nodes and is starting editing them, not the Graph View. So there is no way to open searcher from the Graph View at the moment, I need an additional output to signal Project View.
Perhaps there is a way to refactor the code in such a way that Graph View would be responsible for node creation, but it feels like quite a big change.
edge_dropped_to_create_node <= drop_edges.map(f_!(model.edges_with_detached_targets())); | ||
out.source.on_edge_drop_to_create_node <+ edge_dropped_to_create_node; | ||
|
||
remove_all_detached_edges <- any (drop_edges, inputs.drop_dragged_edge); |
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.
Is this an event name? I mean, is this a name that means "this happens on this event"? If so, please rename it to a name starting with "on_" - this is a new naming convention that we are using in FRP, but it was not used yet in all places in this FRP
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 identifier are you referring to?
app/gui/view/src/project.rs
Outdated
#[derive(Clone, CloneRef, Copy, Debug, PartialEq)] | ||
pub enum WayOfOpeningSearcher { | ||
/// New node was created by opening the searcher or the node is being edited. | ||
NodeEdited(NodeId), |
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.
Node Edited sounds like the node was edited. I would never understand that as node being edited. So this name is misleading,
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.
Would NodeEditing
be better?
app/gui/view/src/project.rs
Outdated
|
||
/// An enum describing how the searcher was opened. | ||
#[derive(Clone, CloneRef, Copy, Debug, PartialEq)] | ||
pub enum WayOfOpeningSearcher { |
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'd rename it to ComponentBrowserOpenReason
. The "way" word is broader than "reason" IMO
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.
Btw, we should lowly rename "searcher" to component browser in the future. I think that new apis can already use the new name.
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 Cause
is even better.
app/gui/view/src/project.rs
Outdated
} | ||
|
||
impl WayOfOpeningSearcher { | ||
/// NodeId of the created/edited node. |
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.
If you are referring to a name from documentation, please use Rust syntax for referencing existing names (I dont remember it right now, but its something with [
and ]
)
Pull Request Description
Creating nodes by pressing Tab works as before, without changes.
Creating nodes by dragging edges and dropping them on the scene:
Creating.nodes.by.dragging.edges.mp4
It doesn't matter if you drag the connection by pressing LMB then releasing it or if you continuously hold LMB while dragging.
Searcher filters suggestions based on the type of node from which the connection was dragged.
Disconnecting existing connections works depending on which endpoint you drag (which half of the connection you start to drag): dropping the target endpoint creates a new node, dropping the source endpoint does not.
Screen.Recording.2022-01-24.at.12.39.23.mov
This PR does not cover nodes alignment after creation - they are created always directly below the cursor (you can't really mess up with other nodes though, as dropping connection on top of other node won't work or will just connect these nodes).
Changes in this PR:
controller::searcher::Mode::NewNode
node holds an optional node id of the "source node". The source node is either the node selected while creating a new node or the node from which the connection was dragged out.controller::searcher::ThisNode
constructor changed so now it accepts a single node id argument instead of a list of nodes. Previously it accepted a list of selected nodes, but it doesn't make sense now, as we should make a decision on what node to use as a "source node" earlier.WayOfOpeningSearcher
enum added.searcher_input
output of the Project View is removed, its usages replaced withsearcher_opened
output (it now contains WayOfOpeningSearcher rather than NodeId.Important Notes
Checklist
Please include the following checklist in your PR: