mirror of
https://github.com/enso-org/enso.git
synced 2024-11-30 05:35:09 +03:00
Initial Project View (https://github.com/enso-org/ide/pull/159)
Original commit: bfe5377ba5
This commit is contained in:
parent
6897884a9e
commit
a9f2de5b00
@ -14,6 +14,7 @@ members = [
|
||||
"lib/ide/ast/macros",
|
||||
"lib/ide/file-manager",
|
||||
"lib/ide/file-manager/mock-server",
|
||||
"lib/ide/",
|
||||
"lib/ide/json-rpc",
|
||||
"lib/ide/parser",
|
||||
"lib/ide/utils",
|
||||
|
@ -73,7 +73,7 @@ async function download_content(cfg) {
|
||||
// ====================
|
||||
|
||||
/// The name of the main scene in the WASM binary.
|
||||
let main_scene_name = 'shapes'
|
||||
let main_scene_name = 'ide'
|
||||
|
||||
/// Prefix name of each scene defined in the WASM binary.
|
||||
let wasm_fn_pfx = "run_example_"
|
||||
|
@ -27,7 +27,7 @@ use wasm_bindgen::prelude::Closure;
|
||||
|
||||
/// Add initialization callback
|
||||
///
|
||||
/// The callback passed as argument will be called once the msdfgen libirary
|
||||
/// The callback passed as argument will be called once the msdfgen library
|
||||
/// will be initialized.
|
||||
pub fn run_once_initialized<F>(callback:F)
|
||||
where F : 'static + FnOnce() {
|
||||
|
@ -242,7 +242,7 @@ impl PhysicsProperties {
|
||||
|
||||
impl PhysicsProperties {
|
||||
/// Safe accessor to modify `KinematicsProperties`.
|
||||
pub fn mod_kinematics<F:FnOnce(&mut KinematicsProperties)>(&mut self, f:F) {
|
||||
pub fn modify_kinematics<F:FnOnce(&mut KinematicsProperties)>(&mut self, f:F) {
|
||||
let mut kinematics = self.kinematics();
|
||||
f(&mut kinematics);
|
||||
self.set_kinematics(kinematics);
|
||||
@ -254,7 +254,7 @@ impl PhysicsProperties {
|
||||
}
|
||||
|
||||
/// Safe accessor to modify `SpringProperties`.
|
||||
pub fn mod_spring<F:FnOnce(&mut SpringProperties)>(&mut self, f:F) {
|
||||
pub fn modify_spring<F:FnOnce(&mut SpringProperties)>(&mut self, f:F) {
|
||||
let mut spring = self.spring();
|
||||
f(&mut spring);
|
||||
self.set_spring(spring);
|
||||
@ -266,7 +266,7 @@ impl PhysicsProperties {
|
||||
}
|
||||
|
||||
/// Safe accessor to modify `DragProperties`.
|
||||
pub fn mod_drag<F:FnOnce(&mut DragProperties)>(&mut self, f:F) {
|
||||
pub fn modify_drag<F:FnOnce(&mut DragProperties)>(&mut self, f:F) {
|
||||
let mut drag = self.drag();
|
||||
f(&mut drag);
|
||||
self.set_drag(drag);
|
||||
@ -278,7 +278,7 @@ impl PhysicsProperties {
|
||||
}
|
||||
|
||||
/// Safe accessor to modify `SimulationThresholds`.
|
||||
pub fn mod_thresholds<F:FnOnce(&mut SimulationThresholds)>(&mut self, f:F) {
|
||||
pub fn modify_thresholds<F:FnOnce(&mut SimulationThresholds)>(&mut self, f:F) {
|
||||
let mut thresholds = self.thresholds();
|
||||
f(&mut thresholds);
|
||||
self.set_thresholds(thresholds);
|
||||
@ -362,7 +362,7 @@ impl PhysicsSimulator {
|
||||
|
||||
let fixed_point = properties.spring().fixed_point;
|
||||
let thresholds = properties.thresholds();
|
||||
properties.mod_kinematics(|kinematics| {
|
||||
properties.modify_kinematics(|kinematics| {
|
||||
let speed = kinematics.velocity.magnitude();
|
||||
let position = kinematics.position();
|
||||
let distance = (position - fixed_point).magnitude();
|
||||
@ -398,7 +398,7 @@ fn simulate(properties:&mut PhysicsProperties, delta_ms:f64) -> Vector3<f32> {
|
||||
let spring = properties.spring();
|
||||
let drag = properties.drag();
|
||||
let mut net_force = zero();
|
||||
properties.mod_kinematics(|mut kinematics| {
|
||||
properties.modify_kinematics(|mut kinematics| {
|
||||
net_force += spring.force(&kinematics);
|
||||
net_force += drag.force(&kinematics);
|
||||
let delta_seconds = delta_ms / 1000.0;
|
||||
|
@ -7,7 +7,9 @@ use crate::data::dirty;
|
||||
use crate::display::layout::types::*;
|
||||
use crate::display::object::DisplayObjectData;
|
||||
use crate::data::dirty::traits::*;
|
||||
use crate::control::callback::{XCallbackRegistry1, CallbackHandle, XCallbackMut1Fn};
|
||||
use crate::control::callback::XCallbackRegistry1;
|
||||
use crate::control::callback::XCallbackMut1Fn;
|
||||
use crate::control::callback::CallbackHandle;
|
||||
|
||||
use nalgebra::Vector2;
|
||||
use nalgebra::Vector3;
|
||||
|
@ -93,18 +93,18 @@ impl Navigator {
|
||||
let y = pan.movement.y * scale;
|
||||
let z = 0.0;
|
||||
|
||||
properties.mod_spring(|spring| { spring.fixed_point += Vector3::new(x, y, z); });
|
||||
properties.modify_spring(|spring| { spring.fixed_point += Vector3::new(x, y, z); });
|
||||
});
|
||||
|
||||
let transform = camera.transform();
|
||||
let resize_callback = camera.add_screen_update_callback(
|
||||
enclose!((mut properties,transform) move |_:&Vector2<f32>| {
|
||||
let position = transform.position();
|
||||
properties.mod_kinematics(|kinematics| {
|
||||
properties.modify_kinematics(|kinematics| {
|
||||
kinematics.set_position(position);
|
||||
kinematics.set_velocity(Vector3::new(0.0, 0.0, 0.0));
|
||||
});
|
||||
properties.mod_spring(|spring| spring.fixed_point = position);
|
||||
properties.modify_spring(|spring| spring.fixed_point = position);
|
||||
})
|
||||
);
|
||||
|
||||
@ -126,7 +126,7 @@ impl Navigator {
|
||||
let min_zoom = camera.clipping().near + min_zoom;
|
||||
position.z = clamp(position.z, min_zoom, max_zoom);
|
||||
|
||||
properties.mod_spring(|spring| spring.fixed_point = position);
|
||||
properties.modify_spring(|spring| spring.fixed_point = position);
|
||||
};
|
||||
(resize_callback, NavigatorEvents::new(dom, panning_callback, zoom_callback, zoom_speed))
|
||||
}
|
||||
|
@ -42,6 +42,7 @@ pub struct TextFieldProperties {
|
||||
/// Text size being a line height in pixels.
|
||||
pub text_size: f32,
|
||||
/// Base color of displayed text.
|
||||
//TODO: base_color should use definitions in core/data/color
|
||||
pub base_color: Vector4<f32>,
|
||||
/// Size of this component.
|
||||
pub size: Vector2<f32>,
|
||||
@ -83,6 +84,16 @@ shared! { TextField
|
||||
self.display_object.set_position(position);
|
||||
}
|
||||
|
||||
/// Get position of this TextField.
|
||||
pub fn position(&self) -> Vector3<f32> {
|
||||
self.display_object.position()
|
||||
}
|
||||
|
||||
/// Get size.
|
||||
pub fn size(&self) -> Vector2<f32> {
|
||||
self.properties.size
|
||||
}
|
||||
|
||||
/// Scroll text by given offset in pixels.
|
||||
pub fn scroll(&mut self, offset:Vector2<f32>) {
|
||||
let position_change = -Vector3::new(offset.x,offset.y,0.0);
|
||||
|
@ -77,7 +77,7 @@ mod tests {
|
||||
let y = 240.0 * random() as f32;
|
||||
let z = 0.0;
|
||||
let position = Vector3::new(x, y, z);
|
||||
properties.mod_spring(|spring| spring.fixed_point = position);
|
||||
properties.modify_spring(|spring| spring.fixed_point = position);
|
||||
target.mod_position(|t| *t = position);
|
||||
});
|
||||
|
||||
|
@ -7,12 +7,12 @@ edition = "2018"
|
||||
[lib]
|
||||
crate-type = ["cdylib"]
|
||||
|
||||
|
||||
[dependencies]
|
||||
basegl = { version = "0.1.0" , path = "../core" }
|
||||
basegl-core-msdf-sys = { version = "0.1.0" , path = "../core/msdf-sys" }
|
||||
basegl-system-web = { version = "0.1.0" , path = "../system/web" }
|
||||
enso-frp = { version = "0.1.0" , path = "../frp" }
|
||||
enso-frp = { version = "0.1.0" , path = "../frp" }
|
||||
ide = { version = "0.1.0" , path = "../ide" }
|
||||
|
||||
enso-prelude = { version = "0.1.0" , path = "../prelude" }
|
||||
wasm-bindgen = { version = "0.2.58" , features = ["nightly"] }
|
||||
|
@ -10,9 +10,9 @@ use basegl::display::object::DisplayObjectOps;
|
||||
use basegl::display::symbol::geometry::Sprite;
|
||||
use basegl::display::symbol::geometry::SpriteSystem;
|
||||
use basegl::display::world::*;
|
||||
use basegl::display::navigation::navigator::Navigator;
|
||||
use basegl::prelude::*;
|
||||
use basegl::animation::animator::fixed_step::FixedStepAnimator;
|
||||
use basegl::display::navigation::navigator::Navigator;
|
||||
|
||||
use nalgebra::Vector2;
|
||||
use nalgebra::Vector3;
|
||||
|
25
gui/lib/gui/src/ide.rs
Normal file
25
gui/lib/gui/src/ide.rs
Normal file
@ -0,0 +1,25 @@
|
||||
//! This module defines the entrypoint function for IDE.
|
||||
|
||||
use wasm_bindgen::prelude::*;
|
||||
|
||||
use basegl::system::web;
|
||||
use ide::run_ide;
|
||||
|
||||
/// IDE startup function.
|
||||
#[wasm_bindgen]
|
||||
#[allow(dead_code)]
|
||||
pub fn run_example_ide() {
|
||||
web::forward_panic_hook_to_console();
|
||||
web::set_stdout();
|
||||
|
||||
// FIXME: This code is temporary. It's used to remove the loader UI.
|
||||
basegl_core_msdf_sys::run_once_initialized(|| {
|
||||
web::get_element_by_id("loader").map(|t| {
|
||||
t.parent_node().map(|p| {
|
||||
p.remove_child(&t).unwrap()
|
||||
})
|
||||
}).ok();
|
||||
|
||||
run_ide()
|
||||
});
|
||||
}
|
@ -25,5 +25,6 @@ pub mod sprite_system;
|
||||
pub mod text_field;
|
||||
pub mod text_typing;
|
||||
pub mod css3d_system;
|
||||
pub mod ide;
|
||||
|
||||
use enso_prelude as prelude;
|
||||
|
46
gui/lib/ide/Cargo.toml
Normal file
46
gui/lib/ide/Cargo.toml
Normal file
@ -0,0 +1,46 @@
|
||||
[package]
|
||||
name = "ide"
|
||||
version = "0.1.0"
|
||||
authors = ["Enso Team <contact@luna-lang.org>"]
|
||||
edition = "2018"
|
||||
|
||||
[lib]
|
||||
crate-type = ["cdylib", "rlib"]
|
||||
|
||||
[dependencies]
|
||||
basegl = { version = "0.1.0" , path = "../core" }
|
||||
enso-prelude = { version = "0.1.0" , path = "../prelude" }
|
||||
file-manager-client = { version = "0.1.0" , path = "file-manager" }
|
||||
json-rpc = { version = "0.1.0" , path = "json-rpc" }
|
||||
utils = { version = "0.1.0" , path = "utils" }
|
||||
basegl-core-msdf-sys = { version = "0.1.0" , path = "../core/msdf-sys" }
|
||||
shapely = { version = "0.1.0" , path = "../shapely/impl" }
|
||||
|
||||
console_error_panic_hook = { version = "0.1.6" }
|
||||
futures = { version = "0.3.1" }
|
||||
nalgebra = { version = "0.19.0" }
|
||||
js-sys = { version = "0.3.35" }
|
||||
serde = { version = "1.0", features = ["derive"] }
|
||||
serde_json = { version = "1.0" }
|
||||
uuid = { version = "0.8", features = ["serde", "v5"] }
|
||||
wasm-bindgen = { version = "0.2.58" }
|
||||
wasm-bindgen-test = { version = "0.3.8" }
|
||||
|
||||
[target.'cfg(not(target_arch = "wasm32"))'.dependencies]
|
||||
websocket = "0.23.0"
|
||||
|
||||
[dependencies.web-sys]
|
||||
version = "0.3.22"
|
||||
features = [
|
||||
'Blob',
|
||||
'console',
|
||||
'CloseEvent',
|
||||
'Document',
|
||||
'Element',
|
||||
"ErrorEvent",
|
||||
"MessageEvent",
|
||||
'HtmlElement',
|
||||
'Node',
|
||||
'WebSocket',
|
||||
'Window',
|
||||
]
|
@ -321,6 +321,7 @@ impl<Notification> Handler<Notification> {
|
||||
match event {
|
||||
TransportEvent::TextMessage(msg) =>
|
||||
self.process_incoming_message(msg),
|
||||
TransportEvent::Opened => {}
|
||||
TransportEvent::Closed => {
|
||||
// Dropping all ongoing calls will cancel their futures.
|
||||
self.clear_ongoing_requests();
|
||||
|
@ -25,6 +25,8 @@ pub trait Transport : Debug {
|
||||
pub enum TransportEvent {
|
||||
/// A text message with has been received.
|
||||
TextMessage(String),
|
||||
/// A socket has been opened.
|
||||
Opened,
|
||||
/// A socket has been closed by the peer.
|
||||
Closed,
|
||||
}
|
||||
|
0
gui/lib/ide/src/entry_point.rs
Normal file
0
gui/lib/ide/src/entry_point.rs
Normal file
34
gui/lib/ide/src/lib.rs
Normal file
34
gui/lib/ide/src/lib.rs
Normal file
@ -0,0 +1,34 @@
|
||||
//! Main library crate for IDE. It includes implementation of
|
||||
//! controllers, view logic and code that wraps them all together.
|
||||
|
||||
#![warn(missing_docs)]
|
||||
#![warn(trivial_casts)]
|
||||
#![warn(trivial_numeric_casts)]
|
||||
#![warn(unused_import_braces)]
|
||||
#![warn(unused_qualifications)]
|
||||
#![warn(unsafe_code)]
|
||||
#![warn(missing_copy_implementations)]
|
||||
#![warn(missing_debug_implementations)]
|
||||
|
||||
#[allow(unused)]
|
||||
pub mod todo;
|
||||
pub mod view;
|
||||
|
||||
#[allow(missing_docs)]
|
||||
/// Common types that should be visible across the whole IDE crate.
|
||||
pub mod prelude {
|
||||
pub use enso_prelude::*;
|
||||
|
||||
pub use futures::Future;
|
||||
pub use futures::FutureExt;
|
||||
pub use futures::Stream;
|
||||
pub use futures::StreamExt;
|
||||
pub use futures::task::LocalSpawnExt;
|
||||
}
|
||||
|
||||
use view::project::ProjectView;
|
||||
|
||||
/// This function is the IDE entry point responsible for setting up all views and controllers.
|
||||
pub fn run_ide() {
|
||||
ProjectView::new().forget();
|
||||
}
|
105
gui/lib/ide/src/todo/executor.rs
Normal file
105
gui/lib/ide/src/todo/executor.rs
Normal file
@ -0,0 +1,105 @@
|
||||
//! TODO [mwu] This module is still provisional work in progress. Currently
|
||||
//! provided only for tests purposes, needs to be completed and
|
||||
//! properly reviewed.
|
||||
//!
|
||||
//! Module providing the executor-related types and functions.
|
||||
|
||||
#![allow(missing_docs)] // TODO [mwu] remove once module is done
|
||||
|
||||
use crate::prelude::*;
|
||||
|
||||
use futures::task::LocalSpawnExt;
|
||||
use futures::task::LocalSpawn;
|
||||
use futures::task::LocalFutureObj;
|
||||
use futures::task::SpawnError;
|
||||
use futures::executor::LocalPool;
|
||||
use futures::executor::LocalSpawner;
|
||||
|
||||
use basegl::control::callback::CallbackHandle;
|
||||
use basegl::control::EventLoop;
|
||||
|
||||
// TODO [mwu] If anything, likely thread local variable should be preferred.
|
||||
static mut CURRENT_SPAWNER: Option<Box<dyn LocalSpawn>> = None;
|
||||
|
||||
// TODO [mwu] consider whether it is the "current" spawner or rather
|
||||
// "the global" spawner. Is it available from outside the async
|
||||
// context? Do we allow using more than one executor in the IDE?
|
||||
#[allow(unsafe_code)]
|
||||
pub fn set_global_spawner(spawner: impl LocalSpawn + 'static) {
|
||||
unsafe {
|
||||
CURRENT_SPAWNER = Some(Box::new(spawner));
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(unsafe_code)]
|
||||
pub fn unset_global_spawner() {
|
||||
unsafe {
|
||||
CURRENT_SPAWNER = None;
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(unsafe_code)]
|
||||
pub fn current_spawner() -> &'static mut dyn LocalSpawn {
|
||||
unsafe {
|
||||
CURRENT_SPAWNER.as_mut().expect("no global executor has been provided")
|
||||
}
|
||||
}
|
||||
|
||||
/// Spawn a task scheduled within a current executor.
|
||||
/// Panics, if called when there is no active asynchronous execution.
|
||||
pub fn spawn_task(f:impl Future<Output=()> + 'static) {
|
||||
current_spawner().spawn_local(f).ok();
|
||||
}
|
||||
|
||||
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct JsExecutor {
|
||||
_executor : Rc<RefCell<LocalPool>>,
|
||||
_event_loop : EventLoop,
|
||||
spawner : LocalSpawner,
|
||||
_cb_handle : CallbackHandle,
|
||||
}
|
||||
|
||||
impl JsExecutor {
|
||||
pub fn new(_event_loop:EventLoop) -> JsExecutor {
|
||||
let _executor = LocalPool::default();
|
||||
let spawner = _executor.spawner();
|
||||
let _executor = Rc::new(RefCell::new(_executor));
|
||||
let _cb_handle = JsExecutor::schedule_execution(_event_loop.clone(), _executor.clone());
|
||||
JsExecutor {_executor,_event_loop,spawner,_cb_handle}
|
||||
}
|
||||
|
||||
pub fn schedule_execution
|
||||
(event_loop:EventLoop, executor:Rc<RefCell<LocalPool>>) -> CallbackHandle {
|
||||
event_loop.add_callback(move |_| {
|
||||
// Safe, because this is the only place borrowing executor and loop
|
||||
// callback shall never be re-entrant.
|
||||
let mut executor = executor.borrow_mut();
|
||||
set_global_spawner(executor.spawner());
|
||||
executor.run_until_stalled();
|
||||
unset_global_spawner();
|
||||
})
|
||||
}
|
||||
|
||||
pub fn spawn
|
||||
(&self, f:impl Future<Output = ()> + 'static)
|
||||
-> Result<(), SpawnError> {
|
||||
self.spawner.spawn_local(f)
|
||||
}
|
||||
|
||||
pub fn add_callback<F:basegl::control::EventLoopCallback>
|
||||
(&mut self, callback:F) -> CallbackHandle {
|
||||
self._event_loop.add_callback(callback)
|
||||
}
|
||||
}
|
||||
|
||||
impl LocalSpawn for JsExecutor {
|
||||
fn spawn_local_obj(&self, future: LocalFutureObj<'static, ()>) -> Result<(), SpawnError> {
|
||||
self.spawner.spawn_local_obj(future)
|
||||
}
|
||||
|
||||
fn status_local(&self) -> Result<(), SpawnError> {
|
||||
self.spawner.status_local()
|
||||
}
|
||||
}
|
3
gui/lib/ide/src/todo/mod.rs
Normal file
3
gui/lib/ide/src/todo/mod.rs
Normal file
@ -0,0 +1,3 @@
|
||||
//! Provisional modules being work in progress.
|
||||
|
||||
pub mod executor;
|
15
gui/lib/ide/src/view.rs
Normal file
15
gui/lib/ide/src/view.rs
Normal file
@ -0,0 +1,15 @@
|
||||
//! A module containing view components.
|
||||
|
||||
#![warn(unsafe_code)]
|
||||
#![warn(missing_copy_implementations)]
|
||||
#![warn(missing_debug_implementations)]
|
||||
#![warn(missing_docs)]
|
||||
#![warn(trivial_casts)]
|
||||
#![warn(trivial_numeric_casts)]
|
||||
#![warn(unused_import_braces)]
|
||||
#![warn(unused_qualifications)]
|
||||
|
||||
pub mod temporary_panel;
|
||||
pub mod project;
|
||||
pub mod layout;
|
||||
pub mod text_editor;
|
178
gui/lib/ide/src/view/layout.rs
Normal file
178
gui/lib/ide/src/view/layout.rs
Normal file
@ -0,0 +1,178 @@
|
||||
//! This module contains implementation of ViewLayout with a single TextEditor temporarily
|
||||
//! occupying half bottom of the screen as the default layout.
|
||||
|
||||
use wasm_bindgen::prelude::*;
|
||||
use basegl::prelude::*;
|
||||
|
||||
use crate::view::temporary_panel::TemporaryPadding;
|
||||
use crate::view::text_editor::TextEditor;
|
||||
use crate::view::temporary_panel::TemporaryPanel;
|
||||
|
||||
use basegl::system::web::*;
|
||||
use basegl::display::world::World;
|
||||
use js_sys::Function;
|
||||
use nalgebra::zero;
|
||||
use nalgebra::Vector2;
|
||||
use std::rc::Rc;
|
||||
use std::cell::RefCell;
|
||||
use wasm_bindgen::JsCast;
|
||||
use web_sys::KeyboardEvent;
|
||||
use web_sys::HtmlElement;
|
||||
|
||||
|
||||
|
||||
// ==================
|
||||
// === LayoutMode ===
|
||||
// ==================
|
||||
//TODO: LayoutMode is a temporary enumeration, it will be replaced by proper Panel impl.
|
||||
|
||||
/// Defines the element's layout mode. It can fully occupy the screen or only half of it.
|
||||
#[derive(Clone,Copy,Debug)]
|
||||
pub enum LayoutMode {
|
||||
#[allow(missing_docs)]
|
||||
Full,
|
||||
#[allow(missing_docs)]
|
||||
Half
|
||||
}
|
||||
|
||||
impl Default for LayoutMode {
|
||||
fn default() -> Self {
|
||||
Self::Half
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
// ========================
|
||||
// === KeyboardListener ===
|
||||
// ========================
|
||||
|
||||
type KeyboardClosure = Closure<dyn FnMut(KeyboardEvent)>;
|
||||
|
||||
#[derive(Debug)]
|
||||
struct KeyboardListener {
|
||||
callback : KeyboardClosure,
|
||||
element : HtmlElement,
|
||||
event_type : String
|
||||
}
|
||||
|
||||
impl KeyboardListener {
|
||||
fn new(element:&HtmlElement, event_type:String, callback:KeyboardClosure) -> Self {
|
||||
let element = element.clone();
|
||||
Self {callback,element,event_type}
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for KeyboardListener {
|
||||
fn drop(&mut self) {
|
||||
let callback : &Function = self.callback.as_ref().unchecked_ref();
|
||||
self.element.remove_event_listener_with_callback(&self.event_type, callback).ok();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
// ==================
|
||||
// === ViewLayout ===
|
||||
// ==================
|
||||
|
||||
shared! { ViewLayout
|
||||
|
||||
/// Initial implementation of ViewLayout with a single TextEditor. Pressing ctrl+f toggles
|
||||
/// fullscreen mode.
|
||||
#[derive(Debug)]
|
||||
pub struct ViewLayoutData {
|
||||
text_editor : TextEditor,
|
||||
key_listener : Option<KeyboardListener>,
|
||||
layout_mode : LayoutMode,
|
||||
size : Vector2<f32>
|
||||
}
|
||||
|
||||
impl {
|
||||
/// Switches LayoutMode between Half and Full.
|
||||
pub fn switch_layout_mode(&mut self) {
|
||||
if let LayoutMode::Half = self.layout_mode {
|
||||
self.set_layout_mode(LayoutMode::Full)
|
||||
} else {
|
||||
self.set_layout_mode(LayoutMode::Half)
|
||||
}
|
||||
}
|
||||
|
||||
/// Sets ViewLayout size.
|
||||
pub fn set_size(&mut self, size:Vector2<f32>) {
|
||||
self.size = size;
|
||||
self.recalculate_layout();
|
||||
}
|
||||
}}
|
||||
|
||||
|
||||
// === Private Methods ===
|
||||
|
||||
impl ViewLayoutData {
|
||||
fn set_layout_mode(&mut self, layout_mode:LayoutMode) {
|
||||
self.layout_mode = layout_mode;
|
||||
self.recalculate_layout();
|
||||
}
|
||||
|
||||
fn recalculate_layout(&mut self) {
|
||||
let size = self.size;
|
||||
let (position,size) = match self.layout_mode {
|
||||
LayoutMode::Full => {
|
||||
let position = Vector2::new(0.0,size.y);
|
||||
(position,size)
|
||||
},
|
||||
LayoutMode::Half => {
|
||||
let position = Vector2::new(0.0,size.y / 2.0);
|
||||
let size = Vector2::new(size.x,size.y / 2.0);
|
||||
(position,size)
|
||||
}
|
||||
};
|
||||
let padding = TemporaryPadding {
|
||||
left : 10.0,
|
||||
top : 0.0,
|
||||
right : 10.0,
|
||||
bottom : 0.0
|
||||
};
|
||||
self.text_editor.set_padding(padding);
|
||||
self.text_editor.set_size(size);
|
||||
self.text_editor.set_position(position);
|
||||
self.text_editor.update();
|
||||
}
|
||||
}
|
||||
|
||||
impl ViewLayout {
|
||||
/// Creates a new ViewLayout with a single TextEditor.
|
||||
pub fn default(world:&World) -> Self {
|
||||
let text_editor = TextEditor::new(&world);
|
||||
let key_listener = None;
|
||||
let layout_mode = default();
|
||||
let size = zero();
|
||||
let data = ViewLayoutData {text_editor,key_listener,layout_mode,size};
|
||||
let rc = Rc::new(RefCell::new(data));
|
||||
Self {rc}.init(world)
|
||||
}
|
||||
|
||||
fn init_keyboard(self) -> Self {
|
||||
let view_layout = self.clone();
|
||||
let closure = move |event:KeyboardEvent| {
|
||||
const F_KEY : u32 = 70;
|
||||
if event.ctrl_key() && event.key_code() == F_KEY {
|
||||
view_layout.switch_layout_mode();
|
||||
event.prevent_default();
|
||||
}
|
||||
};
|
||||
let closure : Box<dyn FnMut(KeyboardEvent)> = Box::new(closure);
|
||||
let callback = Closure::wrap(closure);
|
||||
let body = document().unwrap().body().unwrap();
|
||||
let key_listener = KeyboardListener::new(&body, "keydown".into(), callback);
|
||||
self.rc.borrow_mut().key_listener = Some(key_listener);
|
||||
self
|
||||
}
|
||||
|
||||
fn init(self, world:&World) -> Self {
|
||||
let screen = world.scene().camera().screen();
|
||||
let size = Vector2::new(screen.width,screen.height);
|
||||
self.set_size(size);
|
||||
self.init_keyboard()
|
||||
}
|
||||
}
|
81
gui/lib/ide/src/view/project.rs
Normal file
81
gui/lib/ide/src/view/project.rs
Normal file
@ -0,0 +1,81 @@
|
||||
//! This module contains ProjectView, the main view, responsible for managing TextEditor and
|
||||
//! GraphEditor.
|
||||
|
||||
use crate::prelude::*;
|
||||
|
||||
use super::layout::ViewLayout;
|
||||
|
||||
use basegl::display::world::WorldData;
|
||||
use basegl::display::world::World;
|
||||
use basegl::system::web;
|
||||
use basegl::control::callback::CallbackHandle;
|
||||
|
||||
use nalgebra::Vector2;
|
||||
use shapely::shared;
|
||||
|
||||
|
||||
|
||||
// ===================
|
||||
// === ProjectView ===
|
||||
// ===================
|
||||
|
||||
shared! { ProjectView
|
||||
|
||||
/// ProjectView is the main view of the project, holding instances of TextEditor and
|
||||
/// GraphEditor.
|
||||
#[derive(Debug)]
|
||||
pub struct ProjectViewData {
|
||||
world : World,
|
||||
layout : ViewLayout,
|
||||
resize_callback: Option<CallbackHandle>
|
||||
}
|
||||
|
||||
impl {
|
||||
/// Set view size.
|
||||
pub fn set_size(&mut self, size:Vector2<f32>) {
|
||||
self.layout.set_size(size);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for ProjectViewData {
|
||||
fn default() -> Self {
|
||||
let world = WorldData::new(&web::body());
|
||||
let layout = ViewLayout::default(&world);
|
||||
let resize_callback = None;
|
||||
ProjectViewData{world,layout,resize_callback}
|
||||
}
|
||||
}
|
||||
|
||||
impl ProjectView {
|
||||
/// Create new ProjectView.
|
||||
pub fn new() -> Self {
|
||||
let data = default();
|
||||
Self{rc:data}.init()
|
||||
}
|
||||
|
||||
fn init(self) -> Self {
|
||||
let scene = self.with_borrowed(|data| data.world.scene());
|
||||
let weak = self.downgrade();
|
||||
let resize_callback = scene.camera().add_screen_update_callback(
|
||||
move |size:&Vector2<f32>| {
|
||||
if let Some(this) = weak.upgrade() {
|
||||
this.set_size(*size)
|
||||
}
|
||||
}
|
||||
);
|
||||
self.with_borrowed(move |data| data.resize_callback = Some(resize_callback));
|
||||
self
|
||||
}
|
||||
|
||||
/// Forgets ProjectView, so it won't get dropped when it goes out of scope.
|
||||
pub fn forget(self) {
|
||||
std::mem::forget(self)
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for ProjectView {
|
||||
fn default() -> Self {
|
||||
Self::new()
|
||||
}
|
||||
}
|
52
gui/lib/ide/src/view/temporary_panel.rs
Normal file
52
gui/lib/ide/src/view/temporary_panel.rs
Normal file
@ -0,0 +1,52 @@
|
||||
//! This module contains a temporary definition of Panel. There is a plan to implement proper
|
||||
//! panels in the future.
|
||||
|
||||
use nalgebra::Vector2;
|
||||
|
||||
|
||||
|
||||
// ===============
|
||||
// === Padding ===
|
||||
// ===============
|
||||
|
||||
/// A struct containing the padding values.
|
||||
/// This code is temporary and should be used with cautious.
|
||||
#[derive(Clone,Copy,Debug,Default)]
|
||||
pub struct TemporaryPadding {
|
||||
#[allow(missing_docs)]
|
||||
pub left : f32,
|
||||
#[allow(missing_docs)]
|
||||
pub top : f32,
|
||||
#[allow(missing_docs)]
|
||||
pub right : f32,
|
||||
#[allow(missing_docs)]
|
||||
pub bottom : f32
|
||||
}
|
||||
|
||||
|
||||
|
||||
// =============
|
||||
// === Panel ===
|
||||
// =============
|
||||
|
||||
/// A trait defining a panel interface.
|
||||
/// This code is temporary and should be used with cautious.
|
||||
pub trait TemporaryPanel {
|
||||
/// Sets padding.
|
||||
fn set_padding(&mut self, padding: TemporaryPadding);
|
||||
|
||||
/// Gets padding.
|
||||
fn padding(&self) -> TemporaryPadding;
|
||||
|
||||
/// Sets size.
|
||||
fn set_size(&mut self, size:Vector2<f32>);
|
||||
|
||||
/// Gets size.
|
||||
fn size(&self) -> Vector2<f32>;
|
||||
|
||||
/// Sets position.
|
||||
fn set_position(&mut self, position:Vector2<f32>);
|
||||
|
||||
/// Gets position.
|
||||
fn position(&self) -> Vector2<f32>;
|
||||
}
|
98
gui/lib/ide/src/view/text_editor.rs
Normal file
98
gui/lib/ide/src/view/text_editor.rs
Normal file
@ -0,0 +1,98 @@
|
||||
//! This module contains TextEditor, an UiComponent to edit Enso Modules or Text Files.
|
||||
|
||||
use crate::prelude::*;
|
||||
|
||||
use super::temporary_panel::TemporaryPanel;
|
||||
use super::temporary_panel::TemporaryPadding;
|
||||
|
||||
use basegl::display::object::DisplayObjectOps;
|
||||
use basegl::display::shape::text::glyph::font::FontRegistry;
|
||||
use basegl::display::shape::text::text_field::TextField;
|
||||
use basegl::display::shape::text::text_field::TextFieldProperties;
|
||||
use basegl::display::world::*;
|
||||
|
||||
use nalgebra::Vector2;
|
||||
use nalgebra::zero;
|
||||
|
||||
|
||||
// ==================
|
||||
// === TextEditor ===
|
||||
// ==================
|
||||
|
||||
/// TextEditor allows us to edit text files or Enso Modules. Extensible code highlighting is
|
||||
/// planned to be implemented for it.
|
||||
#[derive(Clone,Debug)]
|
||||
pub struct TextEditor {
|
||||
text_field : TextField,
|
||||
padding : TemporaryPadding,
|
||||
position : Vector2<f32>,
|
||||
size : Vector2<f32>
|
||||
}
|
||||
|
||||
impl TextEditor {
|
||||
/// Creates a new TextEditor.
|
||||
pub fn new(world:&World) -> Self {
|
||||
let scene = world.scene();
|
||||
let camera = scene.camera();
|
||||
let screen = camera.screen();
|
||||
let mut fonts = FontRegistry::new();
|
||||
let font = fonts.get_or_load_embedded_font("DejaVuSansMono").unwrap();
|
||||
let padding = default();
|
||||
let position = zero();
|
||||
let size = Vector2::new(screen.width, screen.height);
|
||||
let black = Vector4::new(0.0,0.0,0.0,1.0);
|
||||
let base_color = black;
|
||||
let text_size = 16.0;
|
||||
let properties = TextFieldProperties {font,text_size,base_color,size};
|
||||
let text_field = TextField::new(&world,properties);
|
||||
world.add_child(&text_field);
|
||||
|
||||
Self {text_field,padding,position,size}.initialize()
|
||||
}
|
||||
|
||||
fn initialize(self) -> Self {
|
||||
self.update();
|
||||
self
|
||||
}
|
||||
|
||||
/// Updates the underlying display object.
|
||||
pub fn update(&self) {
|
||||
let padding = self.padding;
|
||||
let position = self.position;
|
||||
let position = Vector3::new(position.x + padding.left, position.y + padding.bottom, 0.0);
|
||||
self.text_field.set_position(position);
|
||||
// TODO: set text field size once the property change will be supported.
|
||||
// let padding = Vector2::new(padding.left + padding.right, padding.top + padding.bottom);
|
||||
// self.text_field.set_size(self.dimensions - padding);
|
||||
self.text_field.update();
|
||||
}
|
||||
}
|
||||
|
||||
impl TemporaryPanel for TextEditor {
|
||||
fn set_padding(&mut self, padding: TemporaryPadding) {
|
||||
self.padding = padding;
|
||||
}
|
||||
|
||||
fn padding(&self) -> TemporaryPadding {
|
||||
self.padding
|
||||
}
|
||||
|
||||
fn set_size(&mut self, size:Vector2<f32>) {
|
||||
self.size = size;
|
||||
self.update();
|
||||
}
|
||||
|
||||
fn size(&self) -> Vector2<f32> {
|
||||
self.text_field.size()
|
||||
}
|
||||
|
||||
fn set_position(&mut self, position:Vector2<f32>) {
|
||||
self.position = position;
|
||||
self.update();
|
||||
}
|
||||
|
||||
fn position(&self) -> Vector2<f32> {
|
||||
let position = self.text_field.position();
|
||||
Vector2::new(position.x, position.y)
|
||||
}
|
||||
}
|
@ -212,14 +212,6 @@ macro_rules! shared_struct {
|
||||
$(#[$($meta)*])*
|
||||
pub struct [<Weak $name>] <$($params)*> { weak: Weak<RefCell<$name_mut<$($params)*>>> }
|
||||
|
||||
impl<$($params)*> $name <$($params)*> {
|
||||
/// Downgrade the reference to weak ref.
|
||||
pub fn downgrade(&self) -> [<Weak $name>] <$($params)*> {
|
||||
let weak = Rc::downgrade(&self.rc);
|
||||
[<Weak $name>] {weak}
|
||||
}
|
||||
}
|
||||
|
||||
impl<$($params)*> Clone for [<Weak $name>] <$($params)*> {
|
||||
fn clone(&self) -> Self {
|
||||
let weak = self.weak.clone();
|
||||
@ -236,6 +228,26 @@ macro_rules! shared_struct {
|
||||
self.weak.upgrade().map(|rc| $name {rc})
|
||||
}
|
||||
}
|
||||
|
||||
impl<$($params)*> $name <$($params)*> {
|
||||
/// Downgrade the reference to weak ref.
|
||||
pub fn downgrade(&self) -> [<Weak $name>] <$($params)*> {
|
||||
let weak = Rc::downgrade(&self.rc);
|
||||
[<Weak $name>] {weak}
|
||||
}
|
||||
|
||||
/// Call operation with borrowed data. Should be use in implementation of wrapper
|
||||
/// only.
|
||||
fn with_borrowed<F,R>(&self, operation:F) -> R
|
||||
where F : FnOnce(&mut $name_mut<$($params)*>) -> R {
|
||||
operation(&mut self.rc.borrow_mut())
|
||||
}
|
||||
|
||||
/// Check if the shared pointer points to the same struct as `other`.
|
||||
pub fn identity_equals(&self, other:&Self) -> bool {
|
||||
Rc::ptr_eq(&self.rc,&other.rc)
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user