mirror of
https://github.com/enso-org/enso.git
synced 2024-11-22 22:10:15 +03:00
This commit is contained in:
parent
f691713077
commit
853dd2f455
@ -5,8 +5,6 @@
|
||||
|
||||
use crate::prelude::*;
|
||||
|
||||
use crate::config::ProjectToOpen;
|
||||
|
||||
use double_representation::name::project;
|
||||
use mockall::automock;
|
||||
use parser::Parser;
|
||||
@ -104,7 +102,9 @@ impl StatusNotificationPublisher {
|
||||
/// used internally in code.
|
||||
#[derive(Copy, Clone, Debug)]
|
||||
pub enum Notification {
|
||||
/// User opened a new or existing project.
|
||||
/// User created a new project. The new project is opened in IDE.
|
||||
NewProjectCreated,
|
||||
/// User opened an existing project.
|
||||
ProjectOpened,
|
||||
/// User closed the project.
|
||||
ProjectClosed,
|
||||
@ -118,12 +118,10 @@ pub enum Notification {
|
||||
|
||||
// === Errors ===
|
||||
|
||||
/// Error raised when a project with given name or ID was not found.
|
||||
#[allow(missing_docs)]
|
||||
#[derive(Clone, Debug, Fail)]
|
||||
#[fail(display = "Project '{}' was not found.", project)]
|
||||
pub struct ProjectNotFound {
|
||||
project: ProjectToOpen,
|
||||
}
|
||||
#[fail(display = "Project with name \"{}\" not found.", 0)]
|
||||
struct ProjectNotFound(String);
|
||||
|
||||
|
||||
// === Managing API ===
|
||||
@ -133,16 +131,11 @@ pub struct ProjectNotFound {
|
||||
/// It is a separate trait, because those methods are not supported in some environments (see also
|
||||
/// [`API::manage_projects`]).
|
||||
pub trait ManagingProjectAPI {
|
||||
/// Create a new project and open it in the IDE.
|
||||
/// Create a new unnamed project and open it in the IDE.
|
||||
///
|
||||
/// `name` is an optional project name. It overrides the name of the template if given.
|
||||
/// `template` is an optional project template name. Available template names are defined in
|
||||
/// `lib/scala/pkg/src/main/scala/org/enso/pkg/Template.scala`.
|
||||
fn create_new_project(
|
||||
&self,
|
||||
name: Option<String>,
|
||||
template: Option<project::Template>,
|
||||
) -> BoxFuture<FallibleResult>;
|
||||
fn create_new_project(&self, template: Option<project::Template>) -> BoxFuture<FallibleResult>;
|
||||
|
||||
/// Return a list of existing projects.
|
||||
fn list_projects(&self) -> BoxFuture<FallibleResult<Vec<ProjectMetadata>>>;
|
||||
@ -157,44 +150,18 @@ pub trait ManagingProjectAPI {
|
||||
/// and then for the project opening.
|
||||
fn open_project_by_name(&self, name: String) -> BoxFuture<FallibleResult> {
|
||||
async move {
|
||||
let project_id = self.find_project(&ProjectToOpen::Name(name.into())).await?;
|
||||
self.open_project(project_id).await
|
||||
}
|
||||
.boxed_local()
|
||||
}
|
||||
|
||||
/// Open a project by name or ID. If no project with the given name exists, it will be created.
|
||||
fn open_or_create_project(&self, project_to_open: ProjectToOpen) -> BoxFuture<FallibleResult> {
|
||||
async move {
|
||||
match self.find_project(&project_to_open).await {
|
||||
Ok(project_id) => self.open_project(project_id).await,
|
||||
Err(error) =>
|
||||
if let ProjectToOpen::Name(name) = project_to_open {
|
||||
info!("Attempting to create project with name '{name}'.");
|
||||
self.create_new_project(Some(name.to_string()), None).await
|
||||
} else {
|
||||
Err(error)
|
||||
},
|
||||
let projects = self.list_projects().await?;
|
||||
let mut projects = projects.into_iter();
|
||||
let project = projects.find(|project| project.name.as_ref() == name);
|
||||
let uuid = project.map(|project| project.id);
|
||||
if let Some(uuid) = uuid {
|
||||
self.open_project(uuid).await
|
||||
} else {
|
||||
Err(ProjectNotFound(name).into())
|
||||
}
|
||||
}
|
||||
.boxed_local()
|
||||
}
|
||||
|
||||
/// Find a project by name or ID.
|
||||
fn find_project<'a: 'c, 'b: 'c, 'c>(
|
||||
&'a self,
|
||||
project_to_open: &'b ProjectToOpen,
|
||||
) -> BoxFuture<'c, FallibleResult<Uuid>> {
|
||||
async move {
|
||||
self.list_projects()
|
||||
.await?
|
||||
.into_iter()
|
||||
.find(|project_metadata| project_to_open.matches(project_metadata))
|
||||
.map(|metadata| metadata.id)
|
||||
.ok_or_else(|| ProjectNotFound { project: project_to_open.clone() }.into())
|
||||
}
|
||||
.boxed_local()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -226,11 +193,6 @@ pub trait API: Debug {
|
||||
#[allow(clippy::needless_lifetimes)]
|
||||
fn manage_projects<'a>(&'a self) -> FallibleResult<&'a dyn ManagingProjectAPI>;
|
||||
|
||||
/// Returns whether the Managing Project API is available.
|
||||
fn can_manage_projects(&self) -> bool {
|
||||
self.manage_projects().is_ok()
|
||||
}
|
||||
|
||||
/// Return whether private entries should be visible in the component browser.
|
||||
fn are_component_browser_private_entries_visible(&self) -> bool;
|
||||
|
||||
|
@ -4,10 +4,12 @@
|
||||
|
||||
use crate::prelude::*;
|
||||
|
||||
use crate::config::ProjectToOpen;
|
||||
use crate::controller::ide::ManagingProjectAPI;
|
||||
use crate::controller::ide::Notification;
|
||||
use crate::controller::ide::StatusNotificationPublisher;
|
||||
use crate::controller::ide::API;
|
||||
use crate::ide::initializer;
|
||||
|
||||
use double_representation::name::project;
|
||||
use engine_protocol::project_manager;
|
||||
@ -47,16 +49,53 @@ pub struct Handle {
|
||||
}
|
||||
|
||||
impl Handle {
|
||||
/// Create IDE controller.
|
||||
pub fn new(project_manager: Rc<dyn project_manager::API>) -> FallibleResult<Self> {
|
||||
Ok(Self {
|
||||
current_project: default(),
|
||||
/// Create IDE controller. If `maybe_project_name` is `Some`, a project with provided name will
|
||||
/// be opened. Otherwise controller will be used for project manager operations by Welcome
|
||||
/// Screen.
|
||||
pub async fn new(
|
||||
project_manager: Rc<dyn project_manager::API>,
|
||||
project_to_open: Option<ProjectToOpen>,
|
||||
) -> FallibleResult<Self> {
|
||||
let project = match project_to_open {
|
||||
Some(project_to_open) =>
|
||||
Some(Self::init_project_model(project_manager.clone_ref(), project_to_open).await?),
|
||||
None => None,
|
||||
};
|
||||
Ok(Self::new_with_project_model(project_manager, project))
|
||||
}
|
||||
|
||||
/// Create IDE controller with prepared project model. If `project` is `None`,
|
||||
/// `API::current_project` returns `None` as well.
|
||||
pub fn new_with_project_model(
|
||||
project_manager: Rc<dyn project_manager::API>,
|
||||
project: Option<model::Project>,
|
||||
) -> Self {
|
||||
let current_project = Rc::new(CloneCell::new(project));
|
||||
let status_notifications = default();
|
||||
let parser = Parser::new();
|
||||
let notifications = default();
|
||||
let component_browser_private_entries_visibility_flag = default();
|
||||
Self {
|
||||
current_project,
|
||||
project_manager,
|
||||
status_notifications: default(),
|
||||
parser: default(),
|
||||
notifications: default(),
|
||||
component_browser_private_entries_visibility_flag: default(),
|
||||
})
|
||||
status_notifications,
|
||||
parser,
|
||||
notifications,
|
||||
component_browser_private_entries_visibility_flag,
|
||||
}
|
||||
}
|
||||
|
||||
/// Open project with provided name.
|
||||
async fn init_project_model(
|
||||
project_manager: Rc<dyn project_manager::API>,
|
||||
project_to_open: ProjectToOpen,
|
||||
) -> FallibleResult<model::Project> {
|
||||
// TODO[ao]: Reuse of initializer used in previous code design. It should be soon replaced
|
||||
// anyway, because we will soon resign from the "open or create" approach when opening
|
||||
// IDE. See https://github.com/enso-org/ide/issues/1492 for details.
|
||||
let initializer = initializer::WithProjectManager::new(project_manager, project_to_open);
|
||||
let model = initializer.initialize_project_model().await?;
|
||||
Ok(model)
|
||||
}
|
||||
}
|
||||
|
||||
@ -94,16 +133,14 @@ impl API for Handle {
|
||||
|
||||
impl ManagingProjectAPI for Handle {
|
||||
#[profile(Objective)]
|
||||
fn create_new_project(
|
||||
&self,
|
||||
name: Option<String>,
|
||||
template: Option<project::Template>,
|
||||
) -> BoxFuture<FallibleResult> {
|
||||
fn create_new_project(&self, template: Option<project::Template>) -> BoxFuture<FallibleResult> {
|
||||
async move {
|
||||
use model::project::Synchronized as Project;
|
||||
|
||||
let list = self.project_manager.list_projects(&None).await?;
|
||||
let existing_names: HashSet<_> =
|
||||
list.projects.into_iter().map(|p| p.name.into()).collect();
|
||||
let name = name.unwrap_or_else(|| make_project_name(&template));
|
||||
let name = make_project_name(&template);
|
||||
let name = choose_unique_project_name(&existing_names, &name);
|
||||
let name = ProjectName::new_unchecked(name);
|
||||
let version = &enso_config::ARGS.groups.engine.options.preferred_version.value;
|
||||
@ -114,7 +151,12 @@ impl ManagingProjectAPI for Handle {
|
||||
.project_manager
|
||||
.create_project(&name, &template.map(|t| t.into()), &version, &action)
|
||||
.await?;
|
||||
self.open_project(create_result.project_id).await
|
||||
let new_project_id = create_result.project_id;
|
||||
let project_mgr = self.project_manager.clone_ref();
|
||||
let new_project = Project::new_opened(project_mgr, new_project_id);
|
||||
self.current_project.set(Some(new_project.await?));
|
||||
self.notifications.notify(Notification::NewProjectCreated);
|
||||
Ok(())
|
||||
}
|
||||
.boxed_local()
|
||||
}
|
||||
|
@ -10,8 +10,8 @@ use crate::controller::searcher::component::group;
|
||||
use crate::model::module::NodeEditStatus;
|
||||
use crate::model::module::NodeMetadata;
|
||||
use crate::model::suggestion_database;
|
||||
|
||||
use crate::presenter::searcher;
|
||||
|
||||
use breadcrumbs::Breadcrumbs;
|
||||
use double_representation::graph::GraphInfo;
|
||||
use double_representation::graph::LocationHint;
|
||||
@ -712,6 +712,33 @@ impl Searcher {
|
||||
Mode::NewNode { .. } => self.add_example(&example).map(Some),
|
||||
_ => Err(CannotExecuteWhenEditingNode.into()),
|
||||
},
|
||||
Action::ProjectManagement(action) => {
|
||||
match self.ide.manage_projects() {
|
||||
Ok(_) => {
|
||||
let ide = self.ide.clone_ref();
|
||||
executor::global::spawn(async move {
|
||||
// We checked that manage_projects returns Some just a moment ago, so
|
||||
// unwrapping is safe.
|
||||
let manage_projects = ide.manage_projects().unwrap();
|
||||
let result = match action {
|
||||
action::ProjectManagement::CreateNewProject =>
|
||||
manage_projects.create_new_project(None),
|
||||
action::ProjectManagement::OpenProject { id, .. } =>
|
||||
manage_projects.open_project(*id),
|
||||
};
|
||||
if let Err(err) = result.await {
|
||||
error!("Error when creating new project: {err}");
|
||||
}
|
||||
});
|
||||
Ok(None)
|
||||
}
|
||||
Err(err) => Err(NotSupported {
|
||||
action_label: Action::ProjectManagement(action).to_string(),
|
||||
reason: err,
|
||||
}
|
||||
.into()),
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -949,6 +976,12 @@ impl Searcher {
|
||||
let mut actions = action::ListWithSearchResultBuilder::new();
|
||||
let (libraries_icon, default_icon) =
|
||||
action::hardcoded::ICONS.with(|i| (i.libraries.clone_ref(), i.default.clone_ref()));
|
||||
if should_add_additional_entries && self.ide.manage_projects().is_ok() {
|
||||
let mut root_cat = actions.add_root_category("Projects", default_icon.clone_ref());
|
||||
let category = root_cat.add_category("Projects", default_icon.clone_ref());
|
||||
let create_project = action::ProjectManagement::CreateNewProject;
|
||||
category.add_action(Action::ProjectManagement(create_project));
|
||||
}
|
||||
let mut libraries_root_cat =
|
||||
actions.add_root_category("Libraries", libraries_icon.clone_ref());
|
||||
if should_add_additional_entries {
|
||||
|
@ -67,6 +67,14 @@ impl Suggestion {
|
||||
/// Action of adding example code.
|
||||
pub type Example = Rc<model::suggestion_database::Example>;
|
||||
|
||||
/// A variants of project management actions. See also [`Action`].
|
||||
#[allow(missing_docs)]
|
||||
#[derive(Clone, CloneRef, Debug, Eq, PartialEq)]
|
||||
pub enum ProjectManagement {
|
||||
CreateNewProject,
|
||||
OpenProject { id: Immutable<Uuid>, name: ImString },
|
||||
}
|
||||
|
||||
/// A single action on the Searcher list. See also `controller::searcher::Searcher` docs.
|
||||
#[derive(Clone, CloneRef, Debug, PartialEq)]
|
||||
pub enum Action {
|
||||
@ -77,6 +85,8 @@ pub enum Action {
|
||||
/// Add to the current module a new function with example code, and a new node in
|
||||
/// current scene calling that function.
|
||||
Example(Example),
|
||||
/// The project management operation: creating or opening, projects.
|
||||
ProjectManagement(ProjectManagement),
|
||||
// In the future, other action types will be added (like module/method management, etc.).
|
||||
}
|
||||
|
||||
@ -92,6 +102,10 @@ impl Display for Action {
|
||||
Self::Suggestion(Suggestion::Hardcoded(suggestion)) =>
|
||||
Display::fmt(&suggestion.name, f),
|
||||
Self::Example(example) => write!(f, "Example: {}", example.name),
|
||||
Self::ProjectManagement(ProjectManagement::CreateNewProject) =>
|
||||
write!(f, "New Project"),
|
||||
Self::ProjectManagement(ProjectManagement::OpenProject { name, .. }) =>
|
||||
Display::fmt(name, f),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -2,7 +2,6 @@
|
||||
|
||||
use crate::prelude::*;
|
||||
|
||||
use crate::config::ProjectToOpen;
|
||||
use crate::presenter::Presenter;
|
||||
|
||||
use analytics::AnonymousData;
|
||||
@ -94,11 +93,6 @@ impl Ide {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Open a project by name or ID. If no project with the given name exists, it will be created.
|
||||
pub fn open_or_create_project(&self, project: ProjectToOpen) {
|
||||
self.presenter.open_or_create_project(project)
|
||||
}
|
||||
}
|
||||
|
||||
/// A reduced version of [`Ide`] structure, representing an application which failed to initialize.
|
||||
@ -110,6 +104,7 @@ pub struct FailedIde {
|
||||
pub view: ide_view::root::View,
|
||||
}
|
||||
|
||||
|
||||
/// The Path of the module initially opened after opening project in IDE.
|
||||
pub fn initial_module_path(project: &model::Project) -> model::module::Path {
|
||||
project.main_module_path()
|
||||
|
@ -3,14 +3,17 @@
|
||||
use crate::prelude::*;
|
||||
|
||||
use crate::config;
|
||||
use crate::config::ProjectToOpen;
|
||||
use crate::ide::Ide;
|
||||
use crate::retry::retry_operation;
|
||||
use crate::transport::web::WebSocket;
|
||||
use crate::FailedIde;
|
||||
|
||||
use engine_protocol::project_manager;
|
||||
use engine_protocol::project_manager::ProjectName;
|
||||
use ensogl::application::Application;
|
||||
use std::time::Duration;
|
||||
use uuid::Uuid;
|
||||
|
||||
|
||||
|
||||
@ -32,6 +35,19 @@ const INITIALIZATION_RETRY_TIMES: &[Duration] =
|
||||
|
||||
|
||||
|
||||
// ==============
|
||||
// === Errors ===
|
||||
// ==============
|
||||
|
||||
/// Error raised when project with given name was not found.
|
||||
#[derive(Clone, Debug, Fail)]
|
||||
#[fail(display = "Project '{}' was not found.", name)]
|
||||
pub struct ProjectNotFound {
|
||||
name: ProjectToOpen,
|
||||
}
|
||||
|
||||
|
||||
|
||||
// ===================
|
||||
// === Initializer ===
|
||||
// ===================
|
||||
@ -80,13 +96,7 @@ impl Initializer {
|
||||
|
||||
match self.initialize_ide_controller_with_retries().await {
|
||||
Ok(controller) => {
|
||||
let can_manage_projects = controller.can_manage_projects();
|
||||
let ide = Ide::new(ensogl_app, view, controller);
|
||||
if can_manage_projects {
|
||||
if let Some(project) = &self.config.project_to_open {
|
||||
ide.open_or_create_project(project.clone());
|
||||
}
|
||||
}
|
||||
info!("IDE was successfully initialized.");
|
||||
Ok(ide)
|
||||
}
|
||||
@ -116,8 +126,9 @@ impl Initializer {
|
||||
match &self.config.backend {
|
||||
ProjectManager { endpoint } => {
|
||||
let project_manager = self.setup_project_manager(endpoint).await?;
|
||||
let controller = controller::ide::Desktop::new(project_manager)?;
|
||||
Ok(Rc::new(controller))
|
||||
let project_to_open = self.config.project_to_open.clone();
|
||||
let controller = controller::ide::Desktop::new(project_manager, project_to_open);
|
||||
Ok(Rc::new(controller.await?))
|
||||
}
|
||||
LanguageServer { json_endpoint, binary_endpoint, namespace, project_name } => {
|
||||
let json_endpoint = json_endpoint.clone();
|
||||
@ -157,6 +168,81 @@ impl Initializer {
|
||||
|
||||
|
||||
|
||||
// ==========================
|
||||
// === WithProjectManager ===
|
||||
// ==========================
|
||||
|
||||
/// Ide Initializer with project manager.
|
||||
///
|
||||
/// This structure do the specific initialization part when we are connected to Project Manager,
|
||||
/// like list projects, find the one we want to open, open it, or create new one if it does not
|
||||
/// exist.
|
||||
#[allow(missing_docs)]
|
||||
#[derive(Clone, Derivative)]
|
||||
#[derivative(Debug)]
|
||||
pub struct WithProjectManager {
|
||||
#[derivative(Debug = "ignore")]
|
||||
pub project_manager: Rc<dyn project_manager::API>,
|
||||
pub project_to_open: ProjectToOpen,
|
||||
}
|
||||
|
||||
impl WithProjectManager {
|
||||
/// Constructor.
|
||||
pub fn new(
|
||||
project_manager: Rc<dyn project_manager::API>,
|
||||
project_to_open: ProjectToOpen,
|
||||
) -> Self {
|
||||
Self { project_manager, project_to_open }
|
||||
}
|
||||
|
||||
/// Create and initialize a new Project Model, for a project with name passed in constructor.
|
||||
///
|
||||
/// If the project with given name does not exist yet, it will be created.
|
||||
pub async fn initialize_project_model(self) -> FallibleResult<model::Project> {
|
||||
let project_id = self.get_project_or_create_new().await?;
|
||||
let project_manager = self.project_manager;
|
||||
model::project::Synchronized::new_opened(project_manager, project_id).await
|
||||
}
|
||||
|
||||
/// Creates a new project and returns its id, so the newly connected project can be opened.
|
||||
pub async fn create_project(&self, project_name: &ProjectName) -> FallibleResult<Uuid> {
|
||||
use project_manager::MissingComponentAction::Install;
|
||||
info!("Creating a new project named '{}'.", project_name);
|
||||
let version = &enso_config::ARGS.groups.engine.options.preferred_version.value;
|
||||
let version = (!version.is_empty()).as_some_from(|| version.clone());
|
||||
let response = self.project_manager.create_project(project_name, &None, &version, &Install);
|
||||
Ok(response.await?.project_id)
|
||||
}
|
||||
|
||||
async fn lookup_project(&self) -> FallibleResult<Uuid> {
|
||||
let response = self.project_manager.list_projects(&None).await?;
|
||||
let mut projects = response.projects.iter();
|
||||
projects
|
||||
.find(|project_metadata| self.project_to_open.matches(project_metadata))
|
||||
.map(|md| md.id)
|
||||
.ok_or_else(|| ProjectNotFound { name: self.project_to_open.clone() }.into())
|
||||
}
|
||||
|
||||
/// Look for the project with the name specified when constructing this initializer,
|
||||
/// or, if it does not exist, create it. The id of found/created project is returned.
|
||||
pub async fn get_project_or_create_new(&self) -> FallibleResult<Uuid> {
|
||||
let project = self.lookup_project().await;
|
||||
if let Ok(project_id) = project {
|
||||
Ok(project_id)
|
||||
} else if let ProjectToOpen::Name(name) = &self.project_to_open {
|
||||
info!("Attempting to create {}", name);
|
||||
self.create_project(name).await
|
||||
} else {
|
||||
// This can happen only if we are told to open project by id but it cannot be found.
|
||||
// We cannot fallback to creating a new project in this case, as we cannot create a
|
||||
// project with a given id. Thus, we simply propagate the lookup result.
|
||||
project
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
// =============
|
||||
// === Utils ===
|
||||
// =============
|
||||
@ -201,10 +287,6 @@ pub fn register_views(app: &Application) {
|
||||
mod test {
|
||||
use super::*;
|
||||
|
||||
use crate::config::ProjectToOpen;
|
||||
use crate::controller::ide::ManagingProjectAPI;
|
||||
use crate::engine_protocol::project_manager::ProjectName;
|
||||
|
||||
use json_rpc::expect_call;
|
||||
use wasm_bindgen_test::wasm_bindgen_test;
|
||||
|
||||
@ -228,10 +310,9 @@ mod test {
|
||||
expect_call!(mock_client.list_projects(count) => Ok(project_lists));
|
||||
|
||||
let project_manager = Rc::new(mock_client);
|
||||
let ide_controller = controller::ide::Desktop::new(project_manager).unwrap();
|
||||
let project_to_open = ProjectToOpen::Name(project_name);
|
||||
let project_id =
|
||||
ide_controller.find_project(&project_to_open).await.expect("Couldn't get project.");
|
||||
assert_eq!(project_id, expected_id);
|
||||
let initializer = WithProjectManager { project_manager, project_to_open };
|
||||
let project = initializer.get_project_or_create_new().await;
|
||||
assert_eq!(expected_id, project.expect("Couldn't get project."))
|
||||
}
|
||||
}
|
||||
|
@ -83,10 +83,7 @@ impl Fixture {
|
||||
let project_management =
|
||||
controller.manage_projects().expect("Cannot access Managing Project API");
|
||||
|
||||
project_management
|
||||
.create_new_project(None, None)
|
||||
.await
|
||||
.expect("Failed to create new project");
|
||||
project_management.create_new_project(None).await.expect("Failed to create new project");
|
||||
}
|
||||
|
||||
/// After returning, the IDE is in a state with the project opened and ready to work
|
||||
|
@ -4,16 +4,13 @@
|
||||
|
||||
use crate::prelude::*;
|
||||
|
||||
use crate::config::ProjectToOpen;
|
||||
use crate::controller::ide::StatusNotification;
|
||||
use crate::executor::global::spawn_stream_handler;
|
||||
use crate::presenter;
|
||||
|
||||
use enso_frp as frp;
|
||||
use ensogl::system::js;
|
||||
use ide_view as view;
|
||||
use ide_view::graph_editor::SharedHashMap;
|
||||
use std::time::Duration;
|
||||
|
||||
|
||||
// ==============
|
||||
@ -32,19 +29,6 @@ pub use searcher::component_browser::ComponentBrowserSearcher;
|
||||
|
||||
|
||||
|
||||
// =================
|
||||
// === Constants ===
|
||||
// =================
|
||||
|
||||
/// We don't know how long opening the project will take, but we still want to show a fake
|
||||
/// progress indicator for the user. This constant represents how long the spinner will run for in
|
||||
/// milliseconds.
|
||||
const OPEN_PROJECT_SPINNER_TIME_MS: u64 = 5_000;
|
||||
/// The interval in milliseconds at which we should increase the spinner
|
||||
const OPEN_PROJECT_SPINNER_UPDATE_PERIOD_MS: u64 = 10;
|
||||
|
||||
|
||||
|
||||
// =============
|
||||
// === Model ===
|
||||
// =============
|
||||
@ -110,15 +94,15 @@ impl Model {
|
||||
#[profile(Task)]
|
||||
pub fn open_project(&self, project_name: String) {
|
||||
let controller = self.controller.clone_ref();
|
||||
crate::executor::global::spawn(with_progress_indicator(|| async move {
|
||||
crate::executor::global::spawn(async move {
|
||||
if let Ok(managing_api) = controller.manage_projects() {
|
||||
if let Err(err) = managing_api.open_project_by_name(project_name).await {
|
||||
error!("Cannot open project by name: {err}.");
|
||||
}
|
||||
} else {
|
||||
warn!("Project Manager API not available, cannot open project.");
|
||||
warn!("Project opening failed: no ProjectManagingAPI available.");
|
||||
}
|
||||
}));
|
||||
});
|
||||
}
|
||||
|
||||
/// Create a new project. `template` is an optional name of the project template passed to the
|
||||
@ -129,10 +113,9 @@ impl Model {
|
||||
if let Ok(template) =
|
||||
template.map(double_representation::name::project::Template::from_text).transpose()
|
||||
{
|
||||
crate::executor::global::spawn(with_progress_indicator(|| async move {
|
||||
crate::executor::global::spawn(async move {
|
||||
if let Ok(managing_api) = controller.manage_projects() {
|
||||
if let Err(err) = managing_api.create_new_project(None, template.clone()).await
|
||||
{
|
||||
if let Err(err) = managing_api.create_new_project(template.clone()).await {
|
||||
if let Some(template) = template {
|
||||
error!("Could not create new project from template {template}: {err}.");
|
||||
} else {
|
||||
@ -140,66 +123,13 @@ impl Model {
|
||||
}
|
||||
}
|
||||
} else {
|
||||
warn!("Project Manager API not available, cannot create project.");
|
||||
warn!("Project creation failed: no ProjectManagingAPI available.");
|
||||
}
|
||||
}))
|
||||
})
|
||||
} else if let Some(template) = template {
|
||||
error!("Invalid project template name: {template}");
|
||||
};
|
||||
}
|
||||
|
||||
/// Open a project by name or ID. If no project with the given name exists, it will be created.
|
||||
#[profile(Task)]
|
||||
fn open_or_create_project(&self, project: ProjectToOpen) {
|
||||
let controller = self.controller.clone_ref();
|
||||
crate::executor::global::spawn(with_progress_indicator(|| async move {
|
||||
if let Ok(managing_api) = controller.manage_projects() {
|
||||
if let Err(error) = managing_api.open_or_create_project(project).await {
|
||||
error!("Cannot open or create project. {error}");
|
||||
}
|
||||
} else {
|
||||
warn!("Project Manager API not available, cannot open or create project.");
|
||||
}
|
||||
}));
|
||||
}
|
||||
}
|
||||
|
||||
/// Show a full-screen spinner for the exact duration of the specified function.
|
||||
async fn with_progress_indicator<F, T>(f: F)
|
||||
where
|
||||
F: FnOnce() -> T,
|
||||
T: Future<Output = ()>, {
|
||||
// TODO[ss]: Use a safer variant of getting the JS app. This one gets a variable from JS, casts
|
||||
// it to a type, etc. Somewhere in EnsoGL we might already have some logic for getting the JS
|
||||
// app and throwing an error if it's not defined.
|
||||
let Ok(app) = js::app() else { return error!("Failed to get JavaScript EnsoGL app.") };
|
||||
app.show_progress_indicator(0.0);
|
||||
|
||||
let (finished_tx, finished_rx) = futures::channel::oneshot::channel();
|
||||
let spinner_progress = futures::stream::unfold(0, |time| async move {
|
||||
enso_web::sleep(Duration::from_millis(OPEN_PROJECT_SPINNER_UPDATE_PERIOD_MS)).await;
|
||||
let new_time = time + OPEN_PROJECT_SPINNER_UPDATE_PERIOD_MS;
|
||||
if new_time < OPEN_PROJECT_SPINNER_TIME_MS {
|
||||
let progress = new_time as f32 / OPEN_PROJECT_SPINNER_TIME_MS as f32;
|
||||
Some((progress, new_time))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
})
|
||||
.take_until(finished_rx);
|
||||
executor::global::spawn(spinner_progress.for_each(|progress| async move {
|
||||
let Ok(app) = js::app() else { return error!("Failed to get JavaScript EnsoGL app.") };
|
||||
app.show_progress_indicator(progress);
|
||||
}));
|
||||
|
||||
f().await;
|
||||
|
||||
// This fails when the spinner progressed until the end before the function got completed
|
||||
// and therefore the receiver got dropped, so we'll ignore the result.
|
||||
let _ = finished_tx.send(());
|
||||
|
||||
let Ok(app) = js::app() else { return error!("Failed to get JavaScript EnsoGL app.") };
|
||||
app.hide_progress_indicator();
|
||||
}
|
||||
|
||||
|
||||
@ -235,13 +165,6 @@ impl Presenter {
|
||||
let root_frp = &model.view.frp;
|
||||
root_frp.switch_view_to_project <+ welcome_view_frp.create_project.constant(());
|
||||
root_frp.switch_view_to_project <+ welcome_view_frp.open_project.constant(());
|
||||
|
||||
eval root_frp.selected_project ([model] (project) {
|
||||
if let Some(project) = project {
|
||||
model.close_project();
|
||||
model.open_project(project.name.to_string());
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
Self { model, network }.init()
|
||||
@ -251,8 +174,8 @@ impl Presenter {
|
||||
fn init(self) -> Self {
|
||||
self.setup_status_bar_notification_handler();
|
||||
self.setup_controller_notification_handler();
|
||||
executor::global::spawn(self.clone_ref().set_projects_list_on_welcome_screen());
|
||||
self.model.clone_ref().setup_and_display_new_project();
|
||||
executor::global::spawn(self.clone_ref().set_projects_list_on_welcome_screen());
|
||||
self
|
||||
}
|
||||
|
||||
@ -291,7 +214,8 @@ impl Presenter {
|
||||
let weak = Rc::downgrade(&self.model);
|
||||
spawn_stream_handler(weak, stream, move |notification, model| {
|
||||
match notification {
|
||||
controller::ide::Notification::ProjectOpened =>
|
||||
controller::ide::Notification::NewProjectCreated
|
||||
| controller::ide::Notification::ProjectOpened =>
|
||||
model.setup_and_display_new_project(),
|
||||
controller::ide::Notification::ProjectClosed => {
|
||||
model.close_project();
|
||||
@ -315,11 +239,6 @@ impl Presenter {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Open a project by name or ID. If no project with the given name exists, it will be created.
|
||||
pub fn open_or_create_project(&self, project: ProjectToOpen) {
|
||||
self.model.open_or_create_project(project)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
@ -11,8 +11,8 @@ use crate::presenter::searcher::SearcherPresenter;
|
||||
use crate::presenter::ComponentBrowserSearcher;
|
||||
|
||||
use engine_protocol::language_server::ExecutionEnvironment;
|
||||
use engine_protocol::project_manager::ProjectMetadata;
|
||||
use enso_frp as frp;
|
||||
use ensogl::system::js;
|
||||
use ide_view as view;
|
||||
use ide_view::project::SearcherParams;
|
||||
use ide_view::project::SearcherType;
|
||||
@ -22,6 +22,16 @@ use model::project::VcsStatus;
|
||||
|
||||
|
||||
|
||||
// =================
|
||||
// === Constants ===
|
||||
// =================
|
||||
|
||||
/// We don't know how long the project opening will take, but we still want to show a fake progress
|
||||
/// indicator for the user. This constant represents a progress percentage that will be displayed.
|
||||
const OPEN_PROJECT_SPINNER_PROGRESS: f32 = 0.8;
|
||||
|
||||
|
||||
|
||||
// =============
|
||||
// === Model ===
|
||||
// =============
|
||||
@ -39,7 +49,7 @@ struct Model {
|
||||
graph: presenter::Graph,
|
||||
code: presenter::Code,
|
||||
searcher: RefCell<Option<Box<dyn SearcherPresenter>>>,
|
||||
available_projects: Rc<RefCell<Vec<ProjectMetadata>>>,
|
||||
available_projects: Rc<RefCell<Vec<(ImString, Uuid)>>>,
|
||||
shortcut_transaction: RefCell<Option<Rc<model::undo_redo::Transaction>>>,
|
||||
}
|
||||
|
||||
@ -275,6 +285,8 @@ impl Model {
|
||||
executor::global::spawn(async move {
|
||||
if let Ok(api) = controller.manage_projects() {
|
||||
if let Ok(projects) = api.list_projects().await {
|
||||
let projects = projects.into_iter();
|
||||
let projects = projects.map(|p| (p.name.clone().into(), p.id)).collect_vec();
|
||||
*projects_list.borrow_mut() = projects;
|
||||
project_list_ready.emit(());
|
||||
}
|
||||
@ -282,6 +294,36 @@ impl Model {
|
||||
})
|
||||
}
|
||||
|
||||
/// User clicked a project in the Open Project dialog. Open it.
|
||||
fn open_project(&self, id_in_list: &usize) {
|
||||
let controller = self.ide_controller.clone_ref();
|
||||
let projects_list = self.available_projects.clone_ref();
|
||||
let view = self.view.clone_ref();
|
||||
let status_bar = self.status_bar.clone_ref();
|
||||
let id = *id_in_list;
|
||||
executor::global::spawn(async move {
|
||||
let app = js::app_or_panic();
|
||||
app.show_progress_indicator(OPEN_PROJECT_SPINNER_PROGRESS);
|
||||
view.hide_graph_editor();
|
||||
if let Ok(api) = controller.manage_projects() {
|
||||
api.close_project();
|
||||
let uuid = projects_list.borrow().get(id).map(|(_name, uuid)| *uuid);
|
||||
if let Some(uuid) = uuid {
|
||||
if let Err(error) = api.open_project(uuid).await {
|
||||
error!("Error opening project: {error}.");
|
||||
status_bar.add_event(format!("Error opening project: {error}."));
|
||||
}
|
||||
} else {
|
||||
error!("Project with id {id} not found.");
|
||||
}
|
||||
} else {
|
||||
error!("Project Manager API not available, cannot open project.");
|
||||
}
|
||||
app.hide_progress_indicator();
|
||||
view.show_graph_editor();
|
||||
})
|
||||
}
|
||||
|
||||
fn execution_environment_changed(
|
||||
&self,
|
||||
execution_environment: ide_view::execution_environment_selector::ExecutionEnvironment,
|
||||
@ -355,15 +397,28 @@ impl Project {
|
||||
let view = &model.view.frp;
|
||||
let breadcrumbs = &model.view.graph().model.breadcrumbs;
|
||||
let graph_view = &model.view.graph().frp;
|
||||
let project_list = &model.view.project_list().frp;
|
||||
let project_list = &model.view.project_list();
|
||||
|
||||
frp::extend! { network
|
||||
project_list_ready <- source_();
|
||||
project_list.project_list <+ project_list_ready.map(
|
||||
f_!(model.available_projects.borrow().clone())
|
||||
);
|
||||
|
||||
project_list.grid.reset_entries <+ project_list_ready.map(f_!([model]{
|
||||
let cols = 1;
|
||||
let rows = model.available_projects.borrow().len();
|
||||
(rows, cols)
|
||||
}));
|
||||
entry_model <- project_list.grid.model_for_entry_needed.map(f!([model]((row, col)) {
|
||||
let projects = model.available_projects.borrow();
|
||||
let project = projects.get(*row);
|
||||
project.map(|(name, _)| (*row, *col, name.clone_ref()))
|
||||
})).filter_map(|t| t.clone());
|
||||
project_list.grid.model_for_entry <+ entry_model;
|
||||
|
||||
open_project_list <- view.project_list_shown.on_true();
|
||||
eval_ open_project_list (model.project_list_opened(project_list_ready.clone_ref()));
|
||||
eval_ open_project_list(model.project_list_opened(project_list_ready.clone_ref()));
|
||||
selected_project <- project_list.grid.entry_selected.filter_map(|e| *e);
|
||||
eval selected_project(((row, _col)) model.open_project(row));
|
||||
project_list.grid.select_entry <+ selected_project.constant(None);
|
||||
|
||||
eval view.searcher ([model](params) {
|
||||
if let Some(params) = params {
|
||||
|
@ -18,10 +18,16 @@ use ide_view::graph_editor::NodeId;
|
||||
use ide_view::project::SearcherParams;
|
||||
use ide_view::project::SearcherType;
|
||||
|
||||
|
||||
// ==============
|
||||
// === Export ===
|
||||
// ==============
|
||||
|
||||
pub mod ai;
|
||||
pub mod component_browser;
|
||||
|
||||
|
||||
|
||||
/// Trait for the searcher.
|
||||
pub trait SearcherPresenter: Debug {
|
||||
/// Initiate the operating mode for the searcher based on the given [`SearcherParams`]. If the
|
||||
|
@ -13,8 +13,8 @@ use crate::presenter;
|
||||
use crate::presenter::graph::AstNodeId;
|
||||
use crate::presenter::graph::ViewNodeId;
|
||||
use crate::presenter::searcher::component_browser::provider::ControllerComponentsProviderExt;
|
||||
|
||||
use crate::presenter::searcher::SearcherPresenter;
|
||||
|
||||
use enso_frp as frp;
|
||||
use enso_suggestion_database::documentation_ir::EntryDocumentation;
|
||||
use enso_suggestion_database::documentation_ir::Placeholder;
|
||||
@ -35,6 +35,7 @@ use ide_view::project::SearcherParams;
|
||||
pub mod provider;
|
||||
|
||||
|
||||
|
||||
// ==============
|
||||
// === Errors ===
|
||||
// ==============
|
||||
|
@ -133,6 +133,7 @@ impl ide_view::searcher::DocumentationProvider for Action {
|
||||
Some(doc.unwrap_or_else(|| Self::doc_placeholder_for(&suggestion)))
|
||||
}
|
||||
Action::Example(example) => Some(example.documentation_html.clone()),
|
||||
Action::ProjectManagement(_) => None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,10 +1,11 @@
|
||||
use super::prelude::*;
|
||||
|
||||
use crate::controller::ide;
|
||||
use crate::controller::ide::ManagingProjectAPI;
|
||||
use crate::config::ProjectToOpen;
|
||||
use crate::ide;
|
||||
use crate::transport::test_utils::TestWithMockedTransport;
|
||||
|
||||
use engine_protocol::project_manager;
|
||||
use engine_protocol::project_manager::ProjectName;
|
||||
use json_rpc::test_util::transport::mock::MockTransport;
|
||||
use serde_json::json;
|
||||
use wasm_bindgen_test::wasm_bindgen_test;
|
||||
@ -27,8 +28,11 @@ fn failure_to_open_project_is_reported() {
|
||||
fixture.run_test(async move {
|
||||
let project_manager = Rc::new(project_manager::Client::new(transport));
|
||||
executor::global::spawn(project_manager.runner());
|
||||
let ide_controller = ide::Desktop::new(project_manager).unwrap();
|
||||
let result = ide_controller.create_new_project(None, None).await;
|
||||
let name = ProjectName::new_unchecked(crate::constants::DEFAULT_PROJECT_NAME.to_owned());
|
||||
let project_to_open = ProjectToOpen::Name(name);
|
||||
let initializer =
|
||||
ide::initializer::WithProjectManager::new(project_manager, project_to_open);
|
||||
let result = initializer.initialize_project_model().await;
|
||||
result.expect_err("Error should have been reported.");
|
||||
});
|
||||
fixture.when_stalled_send_response(json!({
|
||||
|
@ -105,6 +105,10 @@ ensogl::define_endpoints! {
|
||||
hide_project_list(),
|
||||
/// Close the searcher without taking any actions
|
||||
close_searcher(),
|
||||
/// Show the graph editor.
|
||||
show_graph_editor(),
|
||||
/// Hide the graph editor.
|
||||
hide_graph_editor(),
|
||||
/// Simulates a style toggle press event.
|
||||
toggle_style(),
|
||||
/// Toggles the visibility of private components in the component browser.
|
||||
@ -296,6 +300,14 @@ impl Model {
|
||||
fn hide_project_list(&self) {
|
||||
self.display_object.remove_child(&*self.project_list);
|
||||
}
|
||||
|
||||
fn show_graph_editor(&self) {
|
||||
self.display_object.add_child(&*self.graph_editor);
|
||||
}
|
||||
|
||||
fn hide_graph_editor(&self) {
|
||||
self.display_object.remove_child(&*self.graph_editor);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -428,6 +440,9 @@ impl View {
|
||||
let documentation = &searcher.model().documentation;
|
||||
|
||||
frp::extend! { network
|
||||
eval_ frp.show_graph_editor(model.show_graph_editor());
|
||||
eval_ frp.hide_graph_editor(model.hide_graph_editor());
|
||||
|
||||
// We block graph navigator if it interferes with other panels (searcher, documentation,
|
||||
// etc.)
|
||||
searcher_active <- searcher.is_hovered || documentation.frp.is_selected;
|
||||
@ -657,7 +672,7 @@ impl View {
|
||||
let project_list = &model.project_list;
|
||||
frp::extend! { network
|
||||
eval_ frp.show_project_list (model.show_project_list());
|
||||
project_chosen <- project_list.frp.selected_project.constant(());
|
||||
project_chosen <- project_list.grid.entry_selected.constant(());
|
||||
mouse_down <- scene.mouse.frp_deprecated.down.constant(());
|
||||
clicked_on_bg <- mouse_down.filter(f_!(scene.mouse.target.get().is_background()));
|
||||
should_be_closed <- any(frp.hide_project_list,project_chosen,clicked_on_bg);
|
||||
|
@ -4,7 +4,6 @@
|
||||
use crate::prelude::*;
|
||||
use ensogl::display::shape::*;
|
||||
|
||||
use engine_protocol::project_manager::ProjectMetadata;
|
||||
use enso_frp as frp;
|
||||
use ensogl::application::frp::API;
|
||||
use ensogl::application::Application;
|
||||
@ -196,23 +195,6 @@ mod background {
|
||||
|
||||
|
||||
|
||||
// ===========
|
||||
// === FRP ===
|
||||
// ===========
|
||||
|
||||
ensogl::define_endpoints! {
|
||||
Input {
|
||||
/// This is a list of projects to choose from.
|
||||
project_list (Vec<ProjectMetadata>),
|
||||
}
|
||||
Output {
|
||||
/// This is the selected project.
|
||||
selected_project (Option<ProjectMetadata>),
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
// ===================
|
||||
// === ProjectList ===
|
||||
// ===================
|
||||
@ -222,19 +204,18 @@ ensogl::define_endpoints! {
|
||||
/// This is a list of projects in a nice frame with a title.
|
||||
#[derive(Clone, CloneRef, Debug)]
|
||||
pub struct ProjectList {
|
||||
network: frp::Network,
|
||||
display_object: display::object::Instance,
|
||||
background: background::View,
|
||||
caption: text::Text,
|
||||
grid: grid_view::scrollable::SelectableGridView<Entry>,
|
||||
#[allow(missing_docs)]
|
||||
pub frp: Frp,
|
||||
pub grid: grid_view::scrollable::SelectableGridView<Entry>,
|
||||
}
|
||||
|
||||
impl ProjectList {
|
||||
/// Create Project List Component.
|
||||
pub fn new(app: &Application) -> Self {
|
||||
let frp = Frp::new();
|
||||
let network = &frp.network;
|
||||
let network = frp::Network::new("ProjectList");
|
||||
let display_object = display::object::Instance::new();
|
||||
let background = background::View::new();
|
||||
let caption = app.new_view::<text::Text>();
|
||||
@ -247,7 +228,7 @@ impl ProjectList {
|
||||
caption.add_to_scene_layer(&app.display.default_scene.layers.panel_text);
|
||||
|
||||
let style_frp = StyleWatchFrp::new(&app.display.default_scene.style_sheet);
|
||||
let style = Style::from_theme(network, &style_frp);
|
||||
let style = Style::from_theme(&network, &style_frp);
|
||||
|
||||
frp::extend! { network
|
||||
init <- source::<()>();
|
||||
@ -285,30 +266,11 @@ impl ProjectList {
|
||||
grid_x <- grid_width.map(|width| -width / 2.0);
|
||||
grid_y <- all_with3(&content_size, &bar_height, &paddings, |s,h,p| s.y / 2.0 - *h - *p);
|
||||
_eval <- all_with(&grid_x, &grid_y, f!((x, y) grid.set_xy(Vector2(*x, *y))));
|
||||
|
||||
grid.reset_entries <+ frp.input.project_list.map(|projects| (projects.len(), 1));
|
||||
grid_model_for_entry <= grid.model_for_entry_needed.map2(
|
||||
&frp.input.project_list,
|
||||
|(row, col), projects| {
|
||||
let project = projects.get(*row)?;
|
||||
Some((*row, *col, project.name.clone().into()))
|
||||
}
|
||||
);
|
||||
grid.model_for_entry <+ grid_model_for_entry;
|
||||
|
||||
frp.source.selected_project <+ grid.entry_selected.map2(
|
||||
&frp.input.project_list,
|
||||
|selected_entry, projects| {
|
||||
let (row, _) = (*selected_entry)?;
|
||||
projects.get(row).cloned()
|
||||
}
|
||||
);
|
||||
grid.select_entry <+ frp.output.selected_project.filter_map(|s| s.as_ref().map(|_| None));
|
||||
}
|
||||
style.init.emit(());
|
||||
init.emit(());
|
||||
|
||||
Self { display_object, background, caption, grid, frp }
|
||||
Self { network, display_object, background, caption, grid }
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -6,7 +6,6 @@
|
||||
|
||||
use ensogl::prelude::*;
|
||||
|
||||
use engine_protocol::project_manager::ProjectMetadata;
|
||||
use enso_frp as frp;
|
||||
use ensogl::application;
|
||||
use ensogl::application::Application;
|
||||
@ -40,12 +39,11 @@ pub struct Model {
|
||||
status_bar: crate::status_bar::View,
|
||||
welcome_view: crate::welcome_screen::View,
|
||||
project_view: Rc<CloneCell<Option<crate::project::View>>>,
|
||||
frp_outputs: FrpOutputsSource,
|
||||
}
|
||||
|
||||
impl Model {
|
||||
/// Constructor.
|
||||
pub fn new(app: &Application, frp: &Frp) -> Self {
|
||||
/// Constuctor.
|
||||
pub fn new(app: &Application) -> Self {
|
||||
let app = app.clone_ref();
|
||||
let display_object = display::object::Instance::new();
|
||||
let state = Rc::new(CloneCell::new(State::WelcomeScreen));
|
||||
@ -54,9 +52,8 @@ impl Model {
|
||||
let welcome_view = app.new_view::<crate::welcome_screen::View>();
|
||||
let project_view = Rc::new(CloneCell::new(None));
|
||||
display_object.add_child(&welcome_view);
|
||||
let frp_outputs = frp.output.source.clone_ref();
|
||||
|
||||
Self { app, display_object, state, status_bar, welcome_view, project_view, frp_outputs }
|
||||
Self { app, display_object, state, status_bar, welcome_view, project_view }
|
||||
}
|
||||
|
||||
/// Switch displayed view from Project View to Welcome Screen. Project View will not be
|
||||
@ -87,7 +84,6 @@ impl Model {
|
||||
if self.project_view.get().is_none() {
|
||||
let view = self.app.new_view::<crate::project::View>();
|
||||
let network = &view.network;
|
||||
let project_list_frp = &view.project_list().frp;
|
||||
let status_bar = &self.status_bar;
|
||||
let display_object = &self.display_object;
|
||||
frp::extend! { network
|
||||
@ -95,8 +91,6 @@ impl Model {
|
||||
fs_vis_hidden <- view.fullscreen_visualization_shown.on_false();
|
||||
eval fs_vis_shown ((_) status_bar.unset_parent());
|
||||
eval fs_vis_hidden ([display_object, status_bar](_) display_object.add_child(&status_bar));
|
||||
|
||||
self.frp_outputs.selected_project <+ project_list_frp.selected_project;
|
||||
}
|
||||
self.project_view.set(Some(view));
|
||||
}
|
||||
@ -117,8 +111,6 @@ ensogl::define_endpoints! {
|
||||
switch_view_to_welcome_screen(),
|
||||
}
|
||||
Output {
|
||||
/// The selected project in the project list
|
||||
selected_project (Option<ProjectMetadata>),
|
||||
}
|
||||
}
|
||||
|
||||
@ -146,8 +138,8 @@ impl Deref for View {
|
||||
impl View {
|
||||
/// Constuctor.
|
||||
pub fn new(app: &Application) -> Self {
|
||||
let model = Model::new(app);
|
||||
let frp = Frp::new();
|
||||
let model = Model::new(app, &frp);
|
||||
let network = &frp.network;
|
||||
let style = StyleWatchFrp::new(&app.display.default_scene.style_sheet);
|
||||
let offset_y = style.get_number(ensogl_hardcoded_theme::application::status_bar::offset_y);
|
||||
|
@ -33,7 +33,7 @@ impl TestOnNewProjectControllersOnly {
|
||||
let initializer = enso_gui::Initializer::new(config);
|
||||
let error_msg = "Couldn't open project.";
|
||||
let ide = initializer.initialize_ide_controller().await.expect(error_msg);
|
||||
ide.manage_projects().unwrap().create_new_project(None, None).await.unwrap();
|
||||
ide.manage_projects().unwrap().create_new_project(None).await.unwrap();
|
||||
let project = ide.current_project().unwrap();
|
||||
Self { _ide: ide, project, _executor: executor }
|
||||
}
|
||||
|
@ -428,9 +428,10 @@ export class App {
|
||||
|
||||
/** Show a spinner. The displayed progress is constant. */
|
||||
showProgressIndicator(progress: number) {
|
||||
if (this.progressIndicator == null) {
|
||||
this.progressIndicator = new wasm.ProgressIndicator(this.config)
|
||||
if (this.progressIndicator) {
|
||||
this.hideProgressIndicator()
|
||||
}
|
||||
this.progressIndicator = new wasm.ProgressIndicator(this.config)
|
||||
this.progressIndicator.set(progress)
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user