From 0d84a601dde48af9701b6be891fb754ec3aa26ee Mon Sep 17 00:00:00 2001 From: Michael Mauderer Date: Tue, 25 Apr 2023 22:28:15 +0200 Subject: [PATCH] Ability to change the execution environment between design and live. (#6341) Integrate the UI for electing the Execution Environment with the Language Server and unify existing uses. Implements #5930 + actual integration instead of just mocking it. https://user-images.githubusercontent.com/1428930/232919438-6e1e295a-34fe-4756-86a4-5f5d8f718fa0.mp4 # Important Notes The console output is only emitted as part of the `INFO` level. A better check would be to look at the messages sent to the backend in the developer console. --- CHANGELOG.md | 3 + Cargo.lock | 14 +- .../engine-protocol/src/language_server.rs | 7 +- .../src/language_server/types.rs | 66 ++++++ app/gui/docs/product/shortcuts.md | 1 + app/gui/src/controller/graph/executed.rs | 10 + app/gui/src/model/execution_context.rs | 8 + app/gui/src/model/execution_context/plain.rs | 14 ++ .../model/execution_context/synchronized.rs | 23 +- app/gui/src/presenter/graph.rs | 2 +- app/gui/src/presenter/project.rs | 31 ++- app/gui/view/Cargo.toml | 2 +- app/gui/view/examples/Cargo.toml | 2 +- .../Cargo.toml | 4 +- .../src/lib.rs | 16 +- app/gui/view/examples/interface/Cargo.toml | 2 +- app/gui/view/examples/interface/src/lib.rs | 3 +- app/gui/view/examples/src/lib.rs | 2 +- .../Cargo.toml | 2 +- .../src/lib.rs | 68 +++--- .../src/play_button.rs | 182 ++++++++++++++++ app/gui/view/graph-editor/Cargo.toml | 2 +- .../view/graph-editor/src/component/node.rs | 2 +- .../src/component/node/action_bar.rs | 3 +- .../graph-editor/src/execution_environment.rs | 73 +++++++ app/gui/view/graph-editor/src/lib.rs | 200 ++++-------------- app/gui/view/graph-editor/src/shortcuts.rs | 68 ++++++ app/gui/view/src/lib.rs | 2 +- .../ensogl/app/theme/hardcoded/src/lib.rs | 4 +- .../component/drop-down-menu/src/lib.rs | 41 ++-- 30 files changed, 605 insertions(+), 252 deletions(-) rename app/gui/view/examples/{execution-mode-dropdown => execution-environment-dropdown}/Cargo.toml (79%) rename app/gui/view/examples/{execution-mode-dropdown => execution-environment-dropdown}/src/lib.rs (71%) rename app/gui/view/{execution-mode-selector => execution-environment-selector}/Cargo.toml (93%) rename app/gui/view/{execution-mode-selector => execution-environment-selector}/src/lib.rs (78%) create mode 100644 app/gui/view/execution-environment-selector/src/play_button.rs create mode 100644 app/gui/view/graph-editor/src/execution_environment.rs create mode 100644 app/gui/view/graph-editor/src/shortcuts.rs diff --git a/CHANGELOG.md b/CHANGELOG.md index 6a3b3d90e2d0..185520cdc6e3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -132,6 +132,8 @@ quickly understand each button's function. - [File associations are created on Windows and macOS][6077]. This allows opening Enso files by double-clicking them in the file explorer. +- [The IDE UI element for selecting the execution mode of the project is now + sending messages to the backend.][6341]. #### EnsoGL (rendering engine) @@ -194,6 +196,7 @@ [5895]: https://github.com/enso-org/enso/pull/6130 [6035]: https://github.com/enso-org/enso/pull/6035 [6097]: https://github.com/enso-org/enso/pull/6097 +[6097]: https://github.com/enso-org/enso/pull/6341 #### Enso Standard Library diff --git a/Cargo.lock b/Cargo.lock index 8a2cc5970d00..d1b1ef04b395 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1614,7 +1614,7 @@ dependencies = [ ] [[package]] -name = "debug-scene-execution-mode-dropdown" +name = "debug-scene-execution-environment-dropdown" version = "0.1.0" dependencies = [ "ensogl", @@ -1622,7 +1622,7 @@ dependencies = [ "ensogl-hardcoded-theme", "ensogl-list-view", "ensogl-text-msdf", - "ide-view-execution-mode-selector", + "ide-view-execution-environment-selector", ] [[package]] @@ -1647,7 +1647,7 @@ dependencies = [ "ensogl-hardcoded-theme", "ensogl-text-msdf", "ide-view", - "ide-view-execution-mode-selector", + "ide-view-execution-environment-selector", "parser", "span-tree", "uuid 0.8.2", @@ -2109,7 +2109,7 @@ version = "0.1.0" dependencies = [ "debug-scene-component-list-panel-view", "debug-scene-documentation", - "debug-scene-execution-mode-dropdown", + "debug-scene-execution-environment-dropdown", "debug-scene-icons", "debug-scene-interface", "debug-scene-text-grid-visualization", @@ -4298,7 +4298,7 @@ dependencies = [ "ensogl-text-msdf", "ide-view-component-browser", "ide-view-documentation", - "ide-view-execution-mode-selector", + "ide-view-execution-environment-selector", "ide-view-graph-editor", "js-sys", "multi-map", @@ -4413,7 +4413,7 @@ dependencies = [ ] [[package]] -name = "ide-view-execution-mode-selector" +name = "ide-view-execution-environment-selector" version = "0.1.0" dependencies = [ "enso-frp", @@ -4446,7 +4446,7 @@ dependencies = [ "ensogl-hardcoded-theme", "ensogl-text-msdf", "failure", - "ide-view-execution-mode-selector", + "ide-view-execution-environment-selector", "indexmap", "js-sys", "nalgebra", diff --git a/app/gui/controller/engine-protocol/src/language_server.rs b/app/gui/controller/engine-protocol/src/language_server.rs index 5132b45cb46d..5ebe1784818c 100644 --- a/app/gui/controller/engine-protocol/src/language_server.rs +++ b/app/gui/controller/engine-protocol/src/language_server.rs @@ -157,7 +157,7 @@ trait API { /// Restart the program execution. #[MethodInput=RecomputeInput, rpc_name="executionContext/recompute"] - fn recompute(&self, context_id: ContextId, invalidated_expressions: InvalidatedExpressions) -> (); + fn recompute(&self, context_id: ContextId, invalidated_expressions: InvalidatedExpressions, mode: Option) -> (); /// Obtain the full suggestions database. #[MethodInput=GetSuggestionsDatabaseInput, rpc_name="search/getSuggestionsDatabase"] @@ -205,6 +205,11 @@ trait API { /// VCS snapshot if no `commit_id` is provided. #[MethodInput=VcsRestoreInput, rpc_name="vcs/restore"] fn restore_vcs(&self, root: Path, commit_id: Option) -> response::RestoreVcs; + + /// Set the execution environment of the context for future evaluations. + #[MethodInput=SetModeInput, rpc_name="executionContext/setExecutionEnvironment"] + fn set_execution_environment(&self, context_id: ContextId, execution_environment: ExecutionEnvironment) -> (); + }} diff --git a/app/gui/controller/engine-protocol/src/language_server/types.rs b/app/gui/controller/engine-protocol/src/language_server/types.rs index 5a580451cdd9..aa890c766af8 100644 --- a/app/gui/controller/engine-protocol/src/language_server/types.rs +++ b/app/gui/controller/engine-protocol/src/language_server/types.rs @@ -1155,6 +1155,72 @@ pub struct LibraryComponentGroup { } + +// ============================= +// === Execution Environment === +// ============================= + +/// The execution environment which controls the global execution of functions with side effects. +/// +/// For more information, see +/// https://github.com/enso-org/design/blob/main/epics/basic-libraries/write-action-control/design.md. +#[derive(Hash, Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, Display)] +pub enum ExecutionEnvironment { + /// Allows editing the graph, but the `Output` context is disabled, so it prevents accidental + /// changes. + Design, + /// Unrestricted, live editing of data. + Live, +} + +impl Default for ExecutionEnvironment { + fn default() -> Self { + ExecutionEnvironment::Design + } +} + +impl ExecutionEnvironment { + /// List all available execution environments. + pub fn list_all() -> Vec { + vec![ExecutionEnvironment::Design, ExecutionEnvironment::Live] + } + + /// List all available execution environments as ImStrings. Useful for UI. + pub fn list_all_as_imstrings() -> Vec { + Self::list_all().iter().map(|env| (*env).into()).collect() + } +} + +impl From for ImString { + fn from(env: ExecutionEnvironment) -> Self { + ImString::new(env.to_string()) + } +} + +impl TryFrom<&str> for ExecutionEnvironment { + type Error = (); + + fn try_from(value: &str) -> core::result::Result { + match value.to_lowercase().as_str() { + "design" => Ok(ExecutionEnvironment::Design), + "live" => Ok(ExecutionEnvironment::Live), + _ => Err(()), + } + } +} + +impl ExecutionEnvironment { + /// Returns whether the output context is enabled for this execution environment. + pub fn output_context_enabled(&self) -> bool { + match self { + Self::Design => false, + Self::Live => true, + } + } +} + + + // ====================== // === Test Utilities === // ====================== diff --git a/app/gui/docs/product/shortcuts.md b/app/gui/docs/product/shortcuts.md index 1d7a7edd64e4..d109fb96c52c 100644 --- a/app/gui/docs/product/shortcuts.md +++ b/app/gui/docs/product/shortcuts.md @@ -50,6 +50,7 @@ broken and require further investigation. | escape | Cancel current action. For example, drop currently dragged connection. | | cmd+shift+t | Terminate the program execution | | cmd+shift+r | Re-execute the program | +| cmd+shift+e | Toggle the execution environment between Live and Design. | #### Navigation diff --git a/app/gui/src/controller/graph/executed.rs b/app/gui/src/controller/graph/executed.rs index 96f286d77d38..580015b91b8f 100644 --- a/app/gui/src/controller/graph/executed.rs +++ b/app/gui/src/controller/graph/executed.rs @@ -16,6 +16,7 @@ use crate::model::execution_context::VisualizationId; use crate::model::execution_context::VisualizationUpdateData; use double_representation::name::QualifiedName; +use engine_protocol::language_server::ExecutionEnvironment; use engine_protocol::language_server::MethodPointer; use span_tree::generate::context::CalledMethodInfo; use span_tree::generate::context::Context; @@ -355,6 +356,15 @@ impl Handle { self.graph.borrow().disconnect(connection, self) } } + + /// Set the execution environment. + pub async fn set_execution_environment( + &self, + execution_environment: ExecutionEnvironment, + ) -> FallibleResult { + self.execution_ctx.set_execution_environment(execution_environment).await?; + Ok(()) + } } diff --git a/app/gui/src/model/execution_context.rs b/app/gui/src/model/execution_context.rs index 7a7682c538c4..7f3d4b3d2f4c 100644 --- a/app/gui/src/model/execution_context.rs +++ b/app/gui/src/model/execution_context.rs @@ -6,6 +6,7 @@ use double_representation::identifier::Identifier; use double_representation::name::project; use double_representation::name::QualifiedName; use engine_protocol::language_server; +use engine_protocol::language_server::ExecutionEnvironment; use engine_protocol::language_server::ExpressionUpdate; use engine_protocol::language_server::ExpressionUpdatePayload; use engine_protocol::language_server::MethodPointer; @@ -503,6 +504,13 @@ pub trait API: Debug { /// Adjust method pointers after the project rename action. fn rename_method_pointers(&self, old_project_name: String, new_project_name: String); + + /// Set the execution environment of the context. + #[allow(clippy::needless_lifetimes)] + fn set_execution_environment<'a>( + &'a self, + execution_environment: ExecutionEnvironment, + ) -> BoxFuture<'a, FallibleResult>; } // Note: Needless lifetimes diff --git a/app/gui/src/model/execution_context/plain.rs b/app/gui/src/model/execution_context/plain.rs index cf5123f2a3b7..057ed4a000af 100644 --- a/app/gui/src/model/execution_context/plain.rs +++ b/app/gui/src/model/execution_context/plain.rs @@ -11,6 +11,7 @@ use crate::model::execution_context::Visualization; use crate::model::execution_context::VisualizationId; use crate::model::execution_context::VisualizationUpdateData; +use engine_protocol::language_server::ExecutionEnvironment; use engine_protocol::language_server::MethodPointer; use engine_protocol::language_server::VisualisationConfiguration; use futures::future::LocalBoxFuture; @@ -61,6 +62,8 @@ pub struct ExecutionContext { pub is_ready: crate::sync::Synchronized, /// Component groups defined in libraries imported into the execution context. pub component_groups: RefCell>>, + /// Execution environment of the context. + pub execution_environment: Cell, } impl ExecutionContext { @@ -72,6 +75,7 @@ impl ExecutionContext { let computed_value_info_registry = default(); let is_ready = default(); let component_groups = default(); + let execution_environment = default(); Self { entry_point, stack, @@ -79,6 +83,7 @@ impl ExecutionContext { computed_value_info_registry, is_ready, component_groups, + execution_environment, } } @@ -273,6 +278,15 @@ impl model::execution_context::API for ExecutionContext { local_call.definition = update_method_pointer(&mut local_call.definition) }); } + + fn set_execution_environment( + &self, + environment: ExecutionEnvironment, + ) -> BoxFuture { + info!("Setting execution environment to {environment:?}."); + self.execution_environment.set(environment); + futures::future::ready(Ok(())).boxed_local() + } } diff --git a/app/gui/src/model/execution_context/synchronized.rs b/app/gui/src/model/execution_context/synchronized.rs index d20e79634a77..f055bd71ab28 100644 --- a/app/gui/src/model/execution_context/synchronized.rs +++ b/app/gui/src/model/execution_context/synchronized.rs @@ -11,6 +11,7 @@ use crate::model::execution_context::VisualizationId; use crate::model::execution_context::VisualizationUpdateData; use engine_protocol::language_server; +use engine_protocol::language_server::ExecutionEnvironment; @@ -298,7 +299,11 @@ impl model::execution_context::API for ExecutionContext { async move { self.language_server .client - .recompute(&self.id, &language_server::InvalidatedExpressions::All) + .recompute( + &self.id, + &language_server::InvalidatedExpressions::All, + &Some(self.model.execution_environment.get()), + ) .await?; Ok(()) } @@ -308,6 +313,22 @@ impl model::execution_context::API for ExecutionContext { fn rename_method_pointers(&self, old_project_name: String, new_project_name: String) { self.model.rename_method_pointers(old_project_name, new_project_name); } + + fn set_execution_environment( + &self, + execution_environment: ExecutionEnvironment, + ) -> BoxFuture { + self.model.execution_environment.set(execution_environment); + async move { + info!("Setting execution environment to {execution_environment:?}."); + self.language_server + .client + .set_execution_environment(&self.id, &execution_environment) + .await?; + Ok(()) + } + .boxed_local() + } } impl Drop for ExecutionContext { diff --git a/app/gui/src/presenter/graph.rs b/app/gui/src/presenter/graph.rs index f8cad3e1e11c..99d7a4c1d99e 100644 --- a/app/gui/src/presenter/graph.rs +++ b/app/gui/src/presenter/graph.rs @@ -12,6 +12,7 @@ use crate::presenter::graph::state::State; use double_representation::context_switch::Context; use double_representation::context_switch::ContextSwitch; use double_representation::context_switch::ContextSwitchExpression; +use engine_protocol::language_server::ExecutionEnvironment; use engine_protocol::language_server::SuggestionId; use enso_frp as frp; use futures::future::LocalBoxFuture; @@ -19,7 +20,6 @@ use ide_view as view; use ide_view::graph_editor::component::node as node_view; use ide_view::graph_editor::component::visualization as visualization_view; use ide_view::graph_editor::EdgeEndpoint; -use view::graph_editor::ExecutionEnvironment; use view::graph_editor::WidgetUpdates; diff --git a/app/gui/src/presenter/project.rs b/app/gui/src/presenter/project.rs index b9490454c7a4..7f8ffc5f6689 100644 --- a/app/gui/src/presenter/project.rs +++ b/app/gui/src/presenter/project.rs @@ -7,6 +7,7 @@ use crate::executor::global::spawn_stream_handler; use crate::presenter; use crate::presenter::graph::ViewNodeId; +use engine_protocol::language_server::ExecutionEnvironment; use enso_frp as frp; use ensogl::system::js; use ide_view as view; @@ -287,6 +288,24 @@ impl Model { view.show_graph_editor(); }) } + + fn execution_environment_changed( + &self, + execution_environment: &ide_view::execution_environment_selector::ExecutionEnvironment, + ) { + if let Ok(execution_environment) = execution_environment.as_str().try_into() { + let graph_controller = self.graph_controller.clone_ref(); + executor::global::spawn(async move { + if let Err(err) = + graph_controller.set_execution_environment(execution_environment).await + { + error!("Error setting execution environment: {err}"); + } + }); + } else { + error!("Invalid execution environment: {execution_environment:?}"); + } + } } @@ -385,22 +404,22 @@ impl Project { eval_ view.execution_context_restart(model.execution_context_restart()); view.set_read_only <+ view.toggle_read_only.map(f_!(model.toggle_read_only())); + eval graph_view.execution_environment((env) model.execution_environment_changed(env)); } let graph_controller = self.model.graph_controller.clone_ref(); self.init_analytics() - .init_execution_modes() + .init_execution_environments() .setup_notification_handler() .attach_frp_to_values_computed_notifications(graph_controller, values_computed) } - /// Initialises execution modes. Currently a dummy implementqation to be replaced during - /// implementation of #5930. - fn init_execution_modes(self) -> Self { + /// Initialises execution environment. + fn init_execution_environments(self) -> Self { let graph = &self.model.view.graph(); - let entries = Rc::new(vec!["design".to_string(), "live".to_string()]); - graph.set_available_execution_modes(entries); + let entries = Rc::new(ExecutionEnvironment::list_all_as_imstrings()); + graph.set_available_execution_environments(entries); self } diff --git a/app/gui/view/Cargo.toml b/app/gui/view/Cargo.toml index c9d3c103bd11..73e0956319f5 100644 --- a/app/gui/view/Cargo.toml +++ b/app/gui/view/Cargo.toml @@ -23,7 +23,7 @@ ensogl-text = { path = "../../../lib/rust/ensogl/component/text" } ensogl-text-msdf = { path = "../../../lib/rust/ensogl/component/text/src/font/msdf" } ensogl-hardcoded-theme = { path = "../../../lib/rust/ensogl/app/theme/hardcoded" } ide-view-component-browser = { path = "component-browser" } -ide-view-execution-mode-selector = { path = "execution-mode-selector" } +ide-view-execution-environment-selector = { path = "execution-environment-selector" } ide-view-documentation = { path = "documentation" } ide-view-graph-editor = { path = "graph-editor" } span-tree = { path = "../language/span-tree" } diff --git a/app/gui/view/examples/Cargo.toml b/app/gui/view/examples/Cargo.toml index 2e6e9eb0e78f..0eae83f4486c 100644 --- a/app/gui/view/examples/Cargo.toml +++ b/app/gui/view/examples/Cargo.toml @@ -14,7 +14,7 @@ debug-scene-icons = { path = "icons" } debug-scene-interface = { path = "interface" } debug-scene-text-grid-visualization = { path = "text-grid-visualization" } debug-scene-visualization = { path = "visualization" } -debug-scene-execution-mode-dropdown = { path = "execution-mode-dropdown" } +debug-scene-execution-environment-dropdown = { path = "execution-environment-dropdown" } # Stop wasm-pack from running wasm-opt, because we run it from our build scripts in order to customize options. [package.metadata.wasm-pack.profile.release] diff --git a/app/gui/view/examples/execution-mode-dropdown/Cargo.toml b/app/gui/view/examples/execution-environment-dropdown/Cargo.toml similarity index 79% rename from app/gui/view/examples/execution-mode-dropdown/Cargo.toml rename to app/gui/view/examples/execution-environment-dropdown/Cargo.toml index f4e5658753e2..914bf583983d 100644 --- a/app/gui/view/examples/execution-mode-dropdown/Cargo.toml +++ b/app/gui/view/examples/execution-environment-dropdown/Cargo.toml @@ -1,5 +1,5 @@ [package] -name = "debug-scene-execution-mode-dropdown" +name = "debug-scene-execution-environment-dropdown" version = "0.1.0" authors = ["Enso Team "] edition = "2021" @@ -13,4 +13,4 @@ ensogl-drop-down-menu = { path = "../../../../../lib/rust/ensogl/component/drop- ensogl-list-view = { path = "../../../../../lib/rust/ensogl/component/list-view" } ensogl-hardcoded-theme = { path = "../../../../../lib/rust/ensogl/app/theme/hardcoded" } ensogl-text-msdf = { path = "../../../../../lib/rust/ensogl/component/text/src/font/msdf" } -ide-view-execution-mode-selector = { path = "../../execution-mode-selector" } +ide-view-execution-environment-selector = { path = "../../execution-environment-selector" } diff --git a/app/gui/view/examples/execution-mode-dropdown/src/lib.rs b/app/gui/view/examples/execution-environment-dropdown/src/lib.rs similarity index 71% rename from app/gui/view/examples/execution-mode-dropdown/src/lib.rs rename to app/gui/view/examples/execution-environment-dropdown/src/lib.rs index e455ceb2ec1c..c7731b3e871c 100644 --- a/app/gui/view/examples/execution-mode-dropdown/src/lib.rs +++ b/app/gui/view/examples/execution-environment-dropdown/src/lib.rs @@ -19,7 +19,8 @@ use ensogl::prelude::*; use ensogl::animation; use ensogl::application::Application; use ensogl_text_msdf::run_once_initialized; -use ide_view_execution_mode_selector as execution_mode_selector; +use execution_environment_selector::make_dummy_execution_environments; +use ide_view_execution_environment_selector as execution_environment_selector; @@ -27,24 +28,23 @@ use ide_view_execution_mode_selector as execution_mode_selector; // === Initialisation === // ====================== -fn make_entries() -> execution_mode_selector::ExecutionModes { - Rc::new(vec!["design".to_string(), "live".to_string()]) -} fn init(app: &Application) { let app = app.clone_ref(); let world = &app.display; let _scene = &world.default_scene; - let execution_mode_selector = execution_mode_selector::ExecutionModeSelector::new(&app); - world.add_child(&execution_mode_selector); - execution_mode_selector.set_available_execution_modes(make_entries()); + let execution_environment_selector = + execution_environment_selector::ExecutionEnvironmentSelector::new(&app); + world.add_child(&execution_environment_selector); + execution_environment_selector + .set_available_execution_environments(make_dummy_execution_environments()); world .on .before_frame .add(move |_time_info: animation::TimeInfo| { - let _keep_alive = &execution_mode_selector; + let _keep_alive = &execution_environment_selector; }) .forget(); } diff --git a/app/gui/view/examples/interface/Cargo.toml b/app/gui/view/examples/interface/Cargo.toml index 3f40e579dd98..bb5d53a12b24 100644 --- a/app/gui/view/examples/interface/Cargo.toml +++ b/app/gui/view/examples/interface/Cargo.toml @@ -14,7 +14,7 @@ ensogl = { path = "../../../../../lib/rust/ensogl" } ensogl-hardcoded-theme = { path = "../../../../../lib/rust/ensogl/app/theme/hardcoded" } ensogl-text-msdf = { path = "../../../../../lib/rust/ensogl/component/text/src/font/msdf" } ide-view = { path = "../.." } -ide-view-execution-mode-selector = { path = "../../execution-mode-selector" } +ide-view-execution-environment-selector = { path = "../../execution-environment-selector" } parser = { path = "../../../language/parser" } span-tree = { path = "../../../language/span-tree" } uuid = { version = "0.8", features = ["v4", "wasm-bindgen"] } diff --git a/app/gui/view/examples/interface/src/lib.rs b/app/gui/view/examples/interface/src/lib.rs index ea20b7e01206..ccaa4d31ef24 100644 --- a/app/gui/view/examples/interface/src/lib.rs +++ b/app/gui/view/examples/interface/src/lib.rs @@ -25,6 +25,7 @@ use ensogl::gui::text; use ensogl::system::web; use ensogl_hardcoded_theme as theme; use ensogl_text_msdf::run_once_initialized; +use ide_view::execution_environment_selector::make_dummy_execution_environments; use ide_view::graph_editor; use ide_view::graph_editor::component::node::vcs; use ide_view::graph_editor::component::node::Expression; @@ -256,7 +257,7 @@ fn init(app: &Application) { // === Execution Modes === - graph_editor.set_available_execution_modes(vec!["design".to_string(), "live".to_string()]); + graph_editor.set_available_execution_environments(make_dummy_execution_environments()); // === Rendering === diff --git a/app/gui/view/examples/src/lib.rs b/app/gui/view/examples/src/lib.rs index 8fd4ae8c680d..3b18d962ff33 100644 --- a/app/gui/view/examples/src/lib.rs +++ b/app/gui/view/examples/src/lib.rs @@ -24,7 +24,7 @@ pub use debug_scene_component_list_panel_view as new_component_list_panel_view; pub use debug_scene_documentation as documentation; -pub use debug_scene_execution_mode_dropdown as execution_mode_dropdown; +pub use debug_scene_execution_environment_dropdown as execution_environment_dropdown; pub use debug_scene_icons as icons; pub use debug_scene_interface as interface; pub use debug_scene_text_grid_visualization as text_grid_visualization; diff --git a/app/gui/view/execution-mode-selector/Cargo.toml b/app/gui/view/execution-environment-selector/Cargo.toml similarity index 93% rename from app/gui/view/execution-mode-selector/Cargo.toml rename to app/gui/view/execution-environment-selector/Cargo.toml index a67a694a9dd1..216223954cc4 100644 --- a/app/gui/view/execution-mode-selector/Cargo.toml +++ b/app/gui/view/execution-environment-selector/Cargo.toml @@ -1,5 +1,5 @@ [package] -name = "ide-view-execution-mode-selector" +name = "ide-view-execution-environment-selector" version = "0.1.0" authors = ["Enso Team "] edition = "2021" diff --git a/app/gui/view/execution-mode-selector/src/lib.rs b/app/gui/view/execution-environment-selector/src/lib.rs similarity index 78% rename from app/gui/view/execution-mode-selector/src/lib.rs rename to app/gui/view/execution-environment-selector/src/lib.rs index 9a179df48590..a43eac6ca09c 100644 --- a/app/gui/view/execution-mode-selector/src/lib.rs +++ b/app/gui/view/execution-environment-selector/src/lib.rs @@ -33,7 +33,7 @@ use ensogl::display::camera::Camera2d; use ensogl::display::shape::StyleWatchFrp; use ensogl_derive_theme::FromTheme; use ensogl_gui_component::component; -use ensogl_hardcoded_theme::graph_editor::execution_mode_selector as theme; +use ensogl_hardcoded_theme::graph_editor::execution_environment_selector as theme; @@ -41,7 +41,7 @@ use ensogl_hardcoded_theme::graph_editor::execution_mode_selector as theme; // === Style === // ============== -/// Theme specification for the execution mode selector. +/// Theme specification for the execution environment selector. #[derive(Debug, Clone, Copy, Default, FromTheme)] #[base_path = "theme"] pub struct Style { @@ -73,17 +73,23 @@ impl Style { // === FRP === // =========== -/// An identifier of a execution mode. -pub type ExecutionMode = String; -/// A list of execution modes. -pub type ExecutionModes = Rc>; +/// An identifier of a execution environment. +pub type ExecutionEnvironment = ImString; +/// A list of execution environments. +pub type ExecutionEnvironments = Rc>; + +/// Provide a dummy list of execution environments. Used for testing and demo scenes. +pub fn make_dummy_execution_environments() -> ExecutionEnvironments { + Rc::new(vec!["Design".to_string().into(), "Live".to_string().into()]) +} ensogl::define_endpoints_2! { Input { - set_available_execution_modes (ExecutionModes), + set_available_execution_environments (ExecutionEnvironments), + set_execution_environment (ExecutionEnvironment), } Output { - selected_execution_mode (ExecutionMode), + selected_execution_environment (ExecutionEnvironment), play_press(), size (Vector2), } @@ -95,13 +101,13 @@ ensogl::define_endpoints_2! { // === Model === // ============= -/// The model of the execution mode selector. +/// The model of the execution environment selector. #[derive(Debug, Clone, CloneRef)] pub struct Model { - /// Main root object for the execution mode selector exposed for external positioning. + /// Main root object for the execution environment selector exposed for external positioning. display_object: display::object::Instance, - /// Inner root that will be used for positioning the execution mode selector relative to the - /// window + /// Inner root that will be used for positioning the execution environment selector relative to + /// the window inner_root: display::object::Instance, background: display::shape::compound::rectangle::Rectangle, divider: display::shape::compound::rectangle::Rectangle, @@ -113,6 +119,7 @@ impl Model { fn update_dropdown_style(&self, style: &Style) { self.dropdown.set_menu_offset_y(style.menu_offset); self.dropdown.set_x(style.overall_width() / 2.0 - style.divider_offset); + self.dropdown.set_width(style.dropdown_width); self.dropdown.set_label_color(Rgba::white()); self.dropdown.set_icon_size(Vector2::new(1.0, 1.0)); self.dropdown.set_menu_alignment(ensogl_drop_down_menu::Alignment::Right); @@ -148,7 +155,7 @@ impl Model { self.inner_root.set_y(y.round()); } - fn set_entries(&self, entries: Rc>) { + fn set_entries(&self, entries: Rc>) { let provider = ensogl_list_view::entry::AnyModelProvider::from(entries.clone_ref()); self.dropdown.set_entries(provider); self.dropdown.set_selected(0); @@ -173,13 +180,13 @@ impl display::Object for Model { -// ============================= -// === ExecutionModeDropdown === -// ============================= +// ==================================== +// === ExecutionEnvironmentDropdown === +// ==================================== impl component::Model for Model { fn label() -> &'static str { - "ExecutionModeDropdown" + "ExecutionEnvironmentDropdown" } fn new(app: &Application) -> Self { @@ -244,23 +251,20 @@ impl component::Frp for Frp { // == Inputs == - eval input.set_available_execution_modes ((entries) model.set_entries(entries.clone())); + eval input.set_available_execution_environments ((entries) model.set_entries(entries.clone())); + + update_selected_entry <- input.set_execution_environment.map2(&input.set_available_execution_environments, |entry, entries| { + entries.iter().position(|mode| mode == entry) + }); + dropdown.frp.set_selected <+ update_selected_entry; selected_id <- dropdown.frp.chosen_entry.unwrap(); - selection <- all(input.set_available_execution_modes, selected_id); + selection <- all(input.set_available_execution_environments, selected_id); selected_entry <- selection.map(|(entries, entry_id)| entries[*entry_id].clone()); - output.selected_execution_mode <+ selected_entry; + output.selected_execution_environment <+ selected_entry; eval selected_entry ([model] (execution_mode) { - // TODO(#5930): Revisit when connecting with externally set execution mode - let play_button_visibility = match execution_mode.as_str() { - "design" => true, - "live" => false, - _ => { - error!("Play button: invalid execution mode"); - false - } - }; + let play_button_visibility = matches!(execution_mode.to_lowercase().as_str(), "design"); model.set_play_button_visibility(play_button_visibility); }); play_button.reset <+ selected_entry.constant(()); @@ -276,6 +280,6 @@ impl component::Frp for Frp { } } -/// ExecutionModeSelector is a component that allows the user to select the execution mode of the -/// graph. -pub type ExecutionModeSelector = component::ComponentView; +/// ExecutionEnvironmentSelector is a component that allows the user to select the execution +/// environment of the graph. +pub type ExecutionEnvironmentSelector = component::ComponentView; diff --git a/app/gui/view/execution-environment-selector/src/play_button.rs b/app/gui/view/execution-environment-selector/src/play_button.rs new file mode 100644 index 000000000000..85d3ae0b5eb4 --- /dev/null +++ b/app/gui/view/execution-environment-selector/src/play_button.rs @@ -0,0 +1,182 @@ +use enso_prelude::*; +use ensogl::prelude::*; + +use enso_frp as frp; +use ensogl::application::Application; +use ensogl::control::io::mouse; +use ensogl::display; +use ensogl::display::shape::StyleWatchFrp; +use ensogl_derive_theme::FromTheme; +use ensogl_gui_component::component; +use ensogl_hardcoded_theme::graph_editor::execution_environment_selector::play_button as theme; + + + +// ============= +// === Style === +// ============== + +#[derive(Debug, Clone, Copy, Default, FromTheme)] +#[base_path = "theme"] +pub struct Style { + triangle_size: f32, + offset: f32, + padding_x: f32, + padding_y: f32, +} + + + +// ============== +// === Shapes === +// ============== + +mod play_icon { + use super::*; + + use std::f32::consts::PI; + + ensogl::shape! { + above = [display::shape::compound::rectangle::shape]; + (style: Style) { + let triangle_size = style.get_number(theme::triangle_size); + let color = style.get_color(theme::color); + let triangle = Triangle(triangle_size, triangle_size).rotate((PI / 2.0).radians()); + let triangle = triangle.fill(color); + let bg_size = Var::canvas_size(); + let bg = Rect(bg_size).fill(INVISIBLE_HOVER_COLOR); + (bg + triangle).into() + } + } +} + +mod spinner_icon { + use super::*; + + use std::f32::consts::FRAC_PI_3; + + ensogl::shape! { + above = [display::shape::compound::rectangle::shape]; + (style: Style) { + let color = style.get_color(theme::spinner::color); + let speed = style.get_number(theme::spinner::speed); + let width = Var::::from("input_size.x"); + let time = Var::::from("input_time"); + let unit = &width / 16.0; + let arc = RoundedArc(&unit * 5.0, (4.0 * FRAC_PI_3).radians(), &unit * 2.0); + let rotated_arc = arc.rotate(time * speed); + rotated_arc.fill(color).into() + } + } +} + + + +// =========== +// === FRP === +// =========== + +ensogl::define_endpoints_2! { + Input { + reset (), + } + Output { + pressed (), + } +} + + + +// ============= +// === Model === +// ============= + +#[derive(Debug, Clone, CloneRef)] +pub struct Model { + display_object: display::object::Instance, + play_icon: play_icon::View, + spinner_icon: spinner_icon::View, +} + +impl Model { + fn update_style(&self, style: &Style) { + let triangle_size = Vector2::new(style.triangle_size, style.triangle_size); + let padding = Vector2::new(style.padding_x, style.padding_y); + let size = triangle_size + 2.0 * padding; + self.play_icon.set_size(size); + self.spinner_icon.set_size(size); + self.play_icon.set_x(-size.x / 2.0 - style.offset); + self.spinner_icon.set_x(-size.x / 2.0 - style.offset); + } + + fn set_playing(&self, playing: bool) { + if playing { + self.display_object.remove_child(&self.play_icon); + self.display_object.add_child(&self.spinner_icon); + } else { + self.display_object.remove_child(&self.spinner_icon); + self.display_object.add_child(&self.play_icon); + } + } +} + +impl display::Object for Model { + fn display_object(&self) -> &display::object::Instance { + &self.display_object + } +} + + + +// =================== +// === Play Button === +// =================== + +impl component::Model for Model { + fn label() -> &'static str { + "PlayButton" + } + + fn new(_app: &Application) -> Self { + let display_object = display::object::Instance::new(); + let play_icon = play_icon::View::new(); + let spinner_icon = spinner_icon::View::new(); + + display_object.add_child(&play_icon); + + Self { display_object, play_icon, spinner_icon } + } +} + +impl component::Frp for Frp { + fn init( + network: &enso_frp::Network, + frp: &::Private, + _app: &Application, + model: &Model, + style_watch: &StyleWatchFrp, + ) { + let play_icon = &model.play_icon; + let input = &frp.input; + let output = &frp.output; + + let style = Style::from_theme(network, style_watch); + + frp::extend! { network + eval style.update ((style) model.update_style(style)); + + eval_ input.reset (model.set_playing(false)); + + let play_icon_mouse_down = play_icon.on_event::(); + output.pressed <+ play_icon_mouse_down.constant(()); + + eval_ output.pressed (model.set_playing(true)); + } + style.init.emit(()); + } +} + +/// A button to execute the workflow in a fully enabled way within the current execution +/// environment. The button should be visible in any execution environment where one or more +/// contexts are disabled. +pub type PlayButton = component::ComponentView; diff --git a/app/gui/view/graph-editor/Cargo.toml b/app/gui/view/graph-editor/Cargo.toml index 3e13bca0b2cc..5566d5961139 100644 --- a/app/gui/view/graph-editor/Cargo.toml +++ b/app/gui/view/graph-editor/Cargo.toml @@ -24,7 +24,7 @@ ensogl-drop-manager = { path = "../../../../lib/rust/ensogl/component/drop-manag ensogl-hardcoded-theme = { path = "../../../../lib/rust/ensogl/app/theme/hardcoded" } ensogl-text-msdf = { path = "../../../../lib/rust/ensogl/component/text/src/font/msdf" } failure = { workspace = true } -ide-view-execution-mode-selector = { path = "../execution-mode-selector" } +ide-view-execution-environment-selector = { path = "../execution-environment-selector" } indexmap = "1.9.2" js-sys = { workspace = true } nalgebra = { workspace = true } diff --git a/app/gui/view/graph-editor/src/component/node.rs b/app/gui/view/graph-editor/src/component/node.rs index a1e096ff3ebb..41294e6ac264 100644 --- a/app/gui/view/graph-editor/src/component/node.rs +++ b/app/gui/view/graph-editor/src/component/node.rs @@ -11,11 +11,11 @@ use crate::component::visualization; use crate::selection::BoundingBox; use crate::tooltip; use crate::view; -use crate::ExecutionEnvironment; use crate::Type; use crate::WidgetUpdates; use super::edge; +use engine_protocol::language_server::ExecutionEnvironment; use enso_frp as frp; use enso_frp; use ensogl::animation::delayed::DelayedAnimation; diff --git a/app/gui/view/graph-editor/src/component/node/action_bar.rs b/app/gui/view/graph-editor/src/component/node/action_bar.rs index b0ac9343b380..30662563950a 100644 --- a/app/gui/view/graph-editor/src/component/node/action_bar.rs +++ b/app/gui/view/graph-editor/src/component/node/action_bar.rs @@ -3,8 +3,7 @@ use crate::prelude::*; use ensogl::display::shape::*; -use crate::ExecutionEnvironment; - +use engine_protocol::language_server::ExecutionEnvironment; use enso_config::ARGS; use enso_frp as frp; use ensogl::application::tooltip; diff --git a/app/gui/view/graph-editor/src/execution_environment.rs b/app/gui/view/graph-editor/src/execution_environment.rs new file mode 100644 index 000000000000..43bb57bb0a08 --- /dev/null +++ b/app/gui/view/graph-editor/src/execution_environment.rs @@ -0,0 +1,73 @@ +//! This module contains the logic for the execution environment selector. + +use super::*; + +use crate::Frp; + +use ide_view_execution_environment_selector::ExecutionEnvironment; + + + +// ============================= +// === Execution Environment === +// ============================= + +fn get_next_execution_environment( + current: &ExecutionEnvironment, + available: &[ExecutionEnvironment], +) -> Option { + let index = available.iter().position(|mode| mode == current)?; + let next_index = (index + 1) % available.len(); + Some(available[next_index].clone()) +} + +/// Initialise the FRP logic for the execution environment selector. +pub fn init_frp(frp: &Frp, model: &GraphEditorModelWithNetwork) { + let out = &frp.private.output; + let network = frp.network(); + let inputs = &frp.private.input; + let selector = &model.execution_environment_selector; + + frp::extend! { network + + + // === Execution Environment Changes === + + selector.set_available_execution_environments <+ frp.set_available_execution_environments; + selected_environment <- frp.set_execution_environment.map(|env| (*env).into()); + environment_state + <- all(out.execution_environment,frp.set_available_execution_environments); + + environment_toggled <- environment_state.sample(&frp.toggle_execution_environment); + toggled_execution_environment <- environment_toggled.map(|(mode,available)| + get_next_execution_environment(mode,available)).unwrap(); + + external_update <- any(selected_environment,toggled_execution_environment); + selector.set_execution_environment <+ external_update; + + execution_environment_update + <- any(selector.selected_execution_environment,external_update); + out.execution_environment <+ execution_environment_update; + out.execution_environment_play_button_pressed <+ selector.play_press; + + + // === Layout === + + init <- source::<()>(); + size_update <- all(init,selector.size,inputs.space_for_window_buttons); + eval size_update ([model]((_,size,gap_size)) { + let y_offset = MACOS_TRAFFIC_LIGHTS_VERTICAL_CENTER; + let traffic_light_width = traffic_lights_gap_width(); + + let execution_environment_selector_x = gap_size.x + traffic_light_width; + model.execution_environment_selector.set_x(execution_environment_selector_x); + let breadcrumb_gap_width = + execution_environment_selector_x + size.x + TOP_BAR_ITEM_MARGIN; + model.breadcrumbs.gap_width(breadcrumb_gap_width); + + model.execution_environment_selector.set_y(y_offset + size.y / 2.0); + model.breadcrumbs.set_y(y_offset + component::breadcrumbs::HEIGHT / 2.0); + }); + } + init.emit(()); +} diff --git a/app/gui/view/graph-editor/src/lib.rs b/app/gui/view/graph-editor/src/lib.rs index be5015aa7e8c..fd99f9058edc 100644 --- a/app/gui/view/graph-editor/src/lib.rs +++ b/app/gui/view/graph-editor/src/lib.rs @@ -34,6 +34,7 @@ pub mod component; pub mod automation; pub mod builtin; pub mod data; +pub mod execution_environment; pub mod new_node_position; #[warn(missing_docs)] pub mod profiling; @@ -42,6 +43,7 @@ pub mod view; #[warn(missing_docs)] mod selection; +mod shortcuts; use crate::application::command::FrpNetworkProvider; use crate::component::node; @@ -50,12 +52,12 @@ use crate::component::visualization; use crate::component::visualization::instance::PreprocessorConfiguration; use crate::data::enso; pub use crate::node::profiling::Status as NodeProfilingStatus; +use engine_protocol::language_server::ExecutionEnvironment; use application::tooltip; use enso_config::ARGS; use enso_frp as frp; use ensogl::application; -use ensogl::application::shortcut; use ensogl::application::Application; use ensogl::data::color; use ensogl::display; @@ -75,7 +77,8 @@ use ensogl_component::text; use ensogl_component::text::buffer::selection::Selection; use ensogl_component::tooltip::Tooltip; use ensogl_hardcoded_theme as theme; -use ide_view_execution_mode_selector as execution_mode_selector; +use ide_view_execution_environment_selector as execution_environment_selector; +use ide_view_execution_environment_selector::ExecutionEnvironmentSelector; // =============== @@ -585,9 +588,11 @@ ensogl::define_endpoints_2! { // === Execution Environment === - set_execution_environment(ExecutionEnvironment), // TODO(#5930): Temporary shortcut for testing different execution environments toggle_execution_environment(), + /// Set the execution environmenta available to the graph. + set_available_execution_environments (Rc>), + set_execution_environment (ExecutionEnvironment), // === Debug === @@ -656,10 +661,6 @@ ensogl::define_endpoints_2! { /// Drop an edge that is being dragged. drop_dragged_edge (), - - /// Set the execution modes available to the graph. - set_available_execution_modes (Rc>), - } Output { @@ -765,10 +766,10 @@ ensogl::define_endpoints_2! { default_y_gap_between_nodes (f32), min_x_spacing_for_new_nodes (f32), - /// The selected execution mode. - execution_mode (execution_mode_selector::ExecutionMode), - /// A press of the execution mode selector play button. - execution_mode_play_button_pressed (), + /// The selected environment mode. + execution_environment (execution_environment_selector::ExecutionEnvironment), + /// A press of the execution environment selector play button. + execution_environment_play_button_pressed (), } } @@ -1782,26 +1783,26 @@ impl GraphEditorModelWithNetwork { #[derive(Debug, Clone, CloneRef)] #[allow(missing_docs)] // FIXME[everyone] Public-facing API should be documented. pub struct GraphEditorModel { - pub display_object: display::object::Instance, - pub app: Application, - pub breadcrumbs: component::Breadcrumbs, - pub cursor: cursor::Cursor, - pub nodes: Nodes, - pub edges: Edges, - pub vis_registry: visualization::Registry, - pub drop_manager: ensogl_drop_manager::Manager, - pub navigator: Navigator, - pub add_node_button: Rc, - tooltip: Tooltip, - touch_state: TouchState, - visualisations: Visualisations, - frp: api::Private, - frp_public: api::Public, - profiling_statuses: profiling::Statuses, - profiling_button: component::profiling::Button, - styles_frp: StyleWatchFrp, - selection_controller: selection::Controller, - execution_mode_selector: execution_mode_selector::ExecutionModeSelector, + pub display_object: display::object::Instance, + pub app: Application, + pub breadcrumbs: component::Breadcrumbs, + pub cursor: cursor::Cursor, + pub nodes: Nodes, + pub edges: Edges, + pub vis_registry: visualization::Registry, + pub drop_manager: ensogl_drop_manager::Manager, + pub navigator: Navigator, + pub add_node_button: Rc, + tooltip: Tooltip, + touch_state: TouchState, + visualisations: Visualisations, + frp: api::Private, + frp_public: api::Public, + profiling_statuses: profiling::Statuses, + profiling_button: component::profiling::Button, + styles_frp: StyleWatchFrp, + selection_controller: selection::Controller, + execution_environment_selector: ExecutionEnvironmentSelector, } @@ -1819,7 +1820,8 @@ impl GraphEditorModel { let visualisations = default(); let touch_state = TouchState::new(network, &scene.mouse.frp_deprecated); let breadcrumbs = component::Breadcrumbs::new(app.clone_ref()); - let execution_mode_selector = execution_mode_selector::ExecutionModeSelector::new(app); + let execution_environment_selector = + execution_environment_selector::ExecutionEnvironmentSelector::new(app); let app = app.clone_ref(); let navigator = Navigator::new(scene, &scene.camera()); @@ -1858,7 +1860,7 @@ impl GraphEditorModel { frp_public: frp.public.clone_ref(), styles_frp, selection_controller, - execution_mode_selector, + execution_environment_selector, } .init() } @@ -1866,7 +1868,7 @@ impl GraphEditorModel { fn init(self) -> Self { let x_offset = MACOS_TRAFFIC_LIGHTS_SIDE_OFFSET; - self.add_child(&self.execution_mode_selector); + self.add_child(&self.execution_environment_selector); self.add_child(&self.breadcrumbs); self.breadcrumbs.set_x(x_offset); @@ -2678,67 +2680,8 @@ impl application::View for GraphEditor { } fn default_shortcuts() -> Vec { - use shortcut::ActionType::*; - [ - (Press, "!node_editing & !read_only", "tab", "start_node_creation"), - (Press, "!node_editing & !read_only", "enter", "start_node_creation"), - // === Drag === - (Press, "", "left-mouse-button", "node_press"), - (Release, "", "left-mouse-button", "node_release"), - (Press, "!node_editing & !read_only", "backspace", "remove_selected_nodes"), - (Press, "!node_editing & !read_only", "delete", "remove_selected_nodes"), - (Press, "has_detached_edge", "escape", "drop_dragged_edge"), - (Press, "!read_only", "cmd g", "collapse_selected_nodes"), - // === Visualization === - (Press, "!node_editing", "space", "press_visualization_visibility"), - (DoublePress, "!node_editing", "space", "double_press_visualization_visibility"), - (Release, "!node_editing", "space", "release_visualization_visibility"), - (Press, "", "cmd i", "reload_visualization_registry"), - (Press, "is_fs_visualization_displayed", "space", "close_fullscreen_visualization"), - (Press, "", "cmd", "enable_quick_visualization_preview"), - (Release, "", "cmd", "disable_quick_visualization_preview"), - // === Selection === - (Press, "", "shift", "enable_node_multi_select"), - (Press, "", "shift left-mouse-button", "enable_node_multi_select"), - (Release, "", "shift", "disable_node_multi_select"), - (Release, "", "shift left-mouse-button", "disable_node_multi_select"), - (Press, "", "shift ctrl", "toggle_node_merge_select"), - (Release, "", "shift ctrl", "toggle_node_merge_select"), - (Press, "", "shift alt", "toggle_node_subtract_select"), - (Release, "", "shift alt", "toggle_node_subtract_select"), - (Press, "", "shift ctrl alt", "toggle_node_inverse_select"), - (Release, "", "shift ctrl alt", "toggle_node_inverse_select"), - // === Navigation === - ( - Press, - "!is_fs_visualization_displayed", - "ctrl space", - "cycle_visualization_for_selected_node", - ), - (DoublePress, "!read_only", "left-mouse-button", "enter_hovered_node"), - (DoublePress, "!read_only", "left-mouse-button", "start_node_creation_from_port"), - (Press, "!read_only", "right-mouse-button", "start_node_creation_from_port"), - (Press, "!node_editing & !read_only", "cmd enter", "enter_selected_node"), - (Press, "!read_only", "alt enter", "exit_node"), - // === Node Editing === - (Press, "!read_only", "cmd", "edit_mode_on"), - (Release, "!read_only", "cmd", "edit_mode_off"), - (Press, "!read_only", "cmd left-mouse-button", "edit_mode_on"), - (Release, "!read_only", "cmd left-mouse-button", "edit_mode_off"), - (Press, "node_editing & !read_only", "cmd enter", "stop_editing"), - // === Profiling Mode === - (Press, "", "cmd p", "toggle_profiling_mode"), - // === Debug === - (Press, "debug_mode", "ctrl d", "debug_set_test_visualization_data_for_selected_node"), - (Press, "debug_mode", "ctrl shift enter", "debug_push_breadcrumb"), - (Press, "debug_mode", "ctrl shift up", "debug_pop_breadcrumb"), - (Press, "debug_mode", "ctrl n", "add_node_at_cursor"), - // TODO(#5930): Temporary shortcut for testing different execution environments - (Press, "", "cmd shift c", "toggle_execution_environment"), - ] - .iter() - .map(|(a, b, c, d)| Self::self_shortcut_when(*a, *c, *d, *b)) - .collect() + use crate::shortcuts::SHORTCUTS; + SHORTCUTS.iter().map(|(a, b, c, d)| Self::self_shortcut_when(*a, *c, *d, *b)).collect() } } @@ -3906,30 +3849,7 @@ fn new_graph_editor(app: &Application) -> GraphEditor { // === Execution Mode Selection === // ================================ - let execution_mode_selector = &model.execution_mode_selector; - frp::extend! { network - - execution_mode_selector.set_available_execution_modes <+ frp.set_available_execution_modes; - out.execution_mode <+ execution_mode_selector.selected_execution_mode; - out.execution_mode_play_button_pressed <+ execution_mode_selector.play_press; - - // === Layout === - init <- source::<()>(); - size_update <- all(init,execution_mode_selector.size,inputs.space_for_window_buttons); - eval size_update ([model]((_,size,gap_size)) { - let y_offset = MACOS_TRAFFIC_LIGHTS_VERTICAL_CENTER; - let traffic_light_width = traffic_lights_gap_width(); - - let execution_mode_selector_x = gap_size.x + traffic_light_width; - model.execution_mode_selector.set_x(execution_mode_selector_x); - let breadcrumb_gap_width = execution_mode_selector_x + size.x + TOP_BAR_ITEM_MARGIN; - model.breadcrumbs.gap_width(breadcrumb_gap_width); - - model.execution_mode_selector.set_y(y_offset + size.y / 2.0); - model.breadcrumbs.set_y(y_offset + component::breadcrumbs::HEIGHT / 2.0); - }); - } - init.emit(()); + execution_environment::init_frp(&frp, &model); // ================== @@ -3961,48 +3881,6 @@ impl display::Object for GraphEditor { } - -// ============================= -// === Execution Environment === -// ============================= - -// TODO(#5930): Move me once we synchronise the execution environment with the language server. -/// The execution environment which controls the global execution of functions with side effects. -/// -/// For more information, see -/// https://github.com/enso-org/design/blob/main/epics/basic-libraries/write-action-control/design.md. -#[derive(Debug, Clone, CloneRef, Copy, Default)] -pub enum ExecutionEnvironment { - /// Allows editing the graph, but the `Output` context is disabled, so it prevents accidental - /// changes. - #[default] - Design, - /// Unrestricted, live editing of data. - Live, -} - -impl ExecutionEnvironment { - /// Returns whether the output context is enabled for this execution environment. - pub fn output_context_enabled(&self) -> bool { - match self { - Self::Design => false, - Self::Live => true, - } - } -} - -impl Display for ExecutionEnvironment { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - let name = match self { - Self::Design => "design", - Self::Live => "live", - }; - write!(f, "{name}") - } -} - - - // ============= // === Tests === // ============= diff --git a/app/gui/view/graph-editor/src/shortcuts.rs b/app/gui/view/graph-editor/src/shortcuts.rs new file mode 100644 index 000000000000..73ab6b14c048 --- /dev/null +++ b/app/gui/view/graph-editor/src/shortcuts.rs @@ -0,0 +1,68 @@ +//! Shortcuts used in the graph editor. + +use ensogl::application::shortcut::ActionType::*; + + + +// ======================================= +// === Shortcuts for the graph editor. === +// ======================================= + +/// The list of all shortcuts used in the graph editor. +pub const SHORTCUTS: &[(ensogl::application::shortcut::ActionType, &str, &str, &str)] = &[ + (Press, "!node_editing & !read_only", "tab", "start_node_creation"), + (Press, "!node_editing & !read_only", "enter", "start_node_creation"), + // === Drag === + (Press, "", "left-mouse-button", "node_press"), + (Release, "", "left-mouse-button", "node_release"), + (Press, "!node_editing & !read_only", "backspace", "remove_selected_nodes"), + (Press, "!node_editing & !read_only", "delete", "remove_selected_nodes"), + (Press, "has_detached_edge", "escape", "drop_dragged_edge"), + (Press, "!read_only", "cmd g", "collapse_selected_nodes"), + // === Visualization === + (Press, "!node_editing", "space", "press_visualization_visibility"), + (DoublePress, "!node_editing", "space", "double_press_visualization_visibility"), + (Release, "!node_editing", "space", "release_visualization_visibility"), + (Press, "", "cmd i", "reload_visualization_registry"), + (Press, "is_fs_visualization_displayed", "space", "close_fullscreen_visualization"), + (Press, "", "cmd", "enable_quick_visualization_preview"), + (Release, "", "cmd", "disable_quick_visualization_preview"), + // === Selection === + (Press, "", "shift", "enable_node_multi_select"), + (Press, "", "shift left-mouse-button", "enable_node_multi_select"), + (Release, "", "shift", "disable_node_multi_select"), + (Release, "", "shift left-mouse-button", "disable_node_multi_select"), + (Press, "", "shift ctrl", "toggle_node_merge_select"), + (Release, "", "shift ctrl", "toggle_node_merge_select"), + (Press, "", "shift alt", "toggle_node_subtract_select"), + (Release, "", "shift alt", "toggle_node_subtract_select"), + (Press, "", "shift ctrl alt", "toggle_node_inverse_select"), + (Release, "", "shift ctrl alt", "toggle_node_inverse_select"), + // === Navigation === + ( + Press, + "!is_fs_visualization_displayed", + "ctrl space", + "cycle_visualization_for_selected_node", + ), + (DoublePress, "!read_only", "left-mouse-button", "enter_hovered_node"), + (DoublePress, "!read_only", "left-mouse-button", "start_node_creation_from_port"), + (Press, "!read_only", "right-mouse-button", "start_node_creation_from_port"), + (Press, "!node_editing & !read_only", "cmd enter", "enter_selected_node"), + (Press, "!read_only", "alt enter", "exit_node"), + // === Node Editing === + (Press, "!read_only", "cmd", "edit_mode_on"), + (Release, "!read_only", "cmd", "edit_mode_off"), + (Press, "!read_only", "cmd left-mouse-button", "edit_mode_on"), + (Release, "!read_only", "cmd left-mouse-button", "edit_mode_off"), + (Press, "node_editing & !read_only", "cmd enter", "stop_editing"), + // === Profiling Mode === + (Press, "", "cmd p", "toggle_profiling_mode"), + // === Debug === + (Press, "debug_mode", "ctrl d", "debug_set_test_visualization_data_for_selected_node"), + (Press, "debug_mode", "ctrl shift enter", "debug_push_breadcrumb"), + (Press, "debug_mode", "ctrl shift up", "debug_pop_breadcrumb"), + (Press, "debug_mode", "ctrl n", "add_node_at_cursor"), + // === Execution Mode === + (Press, "", "shift ctrl e", "toggle_execution_environment"), +]; diff --git a/app/gui/view/src/lib.rs b/app/gui/view/src/lib.rs index f8f56740f9e3..ab816ae54bc7 100644 --- a/app/gui/view/src/lib.rs +++ b/app/gui/view/src/lib.rs @@ -41,7 +41,7 @@ pub mod window_control_buttons; pub use ide_view_component_browser as component_browser; pub use ide_view_documentation as documentation; -pub use ide_view_execution_mode_selector as execution_mode_selector; +pub use ide_view_execution_environment_selector as execution_environment_selector; pub use ide_view_graph_editor as graph_editor; pub use welcome_screen; diff --git a/lib/rust/ensogl/app/theme/hardcoded/src/lib.rs b/lib/rust/ensogl/app/theme/hardcoded/src/lib.rs index d06e694346ee..332f77a97d7c 100644 --- a/lib/rust/ensogl/app/theme/hardcoded/src/lib.rs +++ b/lib/rust/ensogl/app/theme/hardcoded/src/lib.rs @@ -642,12 +642,12 @@ define_themes! { [light:0, dark:1] color = Rgba(0.0, 0.451, 0.859, 1.0), Rgba(0.0, 0.451, 0.859, 1.0); } } - execution_mode_selector { + execution_environment_selector { background = Rgb::from_base_255(100.0, 181.0, 38.0), Rgb::from_base_255(100.0, 181.0, 38.0); divider = Rgba::black_with_alpha(0.12), Rgba::black_with_alpha(0.12); divider_offset = 32.5, 32.5; divider_padding = 10.0, 10.0; - dropdown_width = 95.0, 95.0; + dropdown_width = 75.0, 75.0; height = 24.0, 24.0; menu_offset = 20.0, 20.0; play_button { diff --git a/lib/rust/ensogl/component/drop-down-menu/src/lib.rs b/lib/rust/ensogl/component/drop-down-menu/src/lib.rs index d97bbb9fcd83..76335a7a0c74 100644 --- a/lib/rust/ensogl/component/drop-down-menu/src/lib.rs +++ b/lib/rust/ensogl/component/drop-down-menu/src/lib.rs @@ -40,10 +40,8 @@ use ensogl_text as text; /// Invisible dummy color to catch hover events. const HOVER_COLOR: color::Rgba = color::Rgba::new(1.0, 0.0, 0.0, 0.000_001); -/// The width of the visualisation selection menu. -const MENU_WIDTH: f32 = 180.0; - - +/// The default width of the selection menu. +const DEFAULT_MENU_WIDTH: f32 = 180.0; // ============== // === Shapes === @@ -122,6 +120,7 @@ ensogl_core::define_endpoints! { set_menu_offset_y (f32), set_menu_alignment (Alignment), set_label_alignment (Alignment), + set_width (f32), } Output { menu_visible (bool), @@ -270,9 +269,11 @@ impl DropDownMenu { // === Layouting === let menu_height = DEPRECATED_Animation::::new(network); + let menu_width = frp.set_width.clone_ref(); - eval menu_height.value ([model](height) { - model.selection_menu.frp.resize.emit(Vector2::new(MENU_WIDTH,*height)); + resize_menu <- all(menu_width,menu_height.value); + eval resize_menu ([model]((width,height)) { + model.selection_menu.frp.resize.emit(Vector2::new(*width,*height)); if *height <= 0.0 { model.hide_selection_menu(); } else if *height > 0.0 { @@ -298,29 +299,29 @@ impl DropDownMenu { model.selection_menu.set_x(x_offset); }); - label_position <- all(model.label.frp.width,frp.input.set_icon_size,model.label.frp.height, - frp.input.set_label_alignment); - eval label_position ([model]((text_width,icon_size,text_height,alignment)) { + label_position <- all5(&model.label.frp.width,&frp.input.set_icon_size,&model.label.frp + .height,&frp.input.set_label_alignment,&menu_width); + eval label_position ([model]((text_width,icon_size,text_height,alignment,menu_width)) { let base_offset = match alignment { - Alignment::Left => -MENU_WIDTH/2.0+icon_size.x/2.0, - Alignment::Right => -text_width-icon_size.x/2.0, + Alignment::Left => -menu_width + icon_size.x / 2.0, + Alignment::Right => -text_width-icon_size.x / 2.0, }; model.label.set_x(base_offset); // Adjust for text offset, so this appears more centered. model.label.set_y(0.5 * text_height); }); - overlay_size <- all( - model.label.frp.width, - model.label.frp.height, - frp.input.set_icon_size, - frp.input.set_icon_padding); - eval overlay_size ([model]((text_width,text_height,icon_size,icon_padding)) { + overlay_size <- all4( + &model.label.frp.height, + &frp.input.set_icon_size, + &frp.input.set_icon_padding, + &menu_width); + eval overlay_size ([model]((text_height,icon_size,icon_padding,menu_width)) { let height = icon_size.y.max(*text_height); - let width = text_width + icon_size.x + icon_padding.x; + let width = *menu_width; let size = Vector2::new(width,height); model.click_overlay.set_size(size); - model.click_overlay.set_x(-width/2.0 + icon_size.x/2.0 - icon_padding.x); + model.click_overlay.set_x(-width / 2.0 + icon_size.x / 2.0 - icon_padding.x); }); @@ -417,7 +418,7 @@ impl DropDownMenu { let styles = StyleWatch::new(&app.display.default_scene.style_sheet); let text_color = styles.get_color(theme::widget::list_view::text); model.label.set_property_default(text_color); - + frp.set_width.emit(DEFAULT_MENU_WIDTH); self }