Skip to content

Commit

Permalink
Fullscreen Visualization (enso-org/ide#1355)
Browse files Browse the repository at this point in the history
Original commit: enso-org/ide@ebf5c4f
  • Loading branch information
farmaazon authored Mar 30, 2021
1 parent 7faf29e commit 3934cca
Show file tree
Hide file tree
Showing 20 changed files with 550 additions and 240 deletions.
5 changes: 5 additions & 0 deletions ide/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@
will be lost. In this build we added notification in statusbar to signalize
that the connection was lost and IDE must be restarted. In future IDE will try
to automatically reconnect.
- [Visualization can be extended to the whole screen][1355] by selecting the
node and pressing space twice. To quit this view, press space again.
- [Database Visualizations][1335]. Visualizations for the Database library have
been added. The Table visualization now automatically executes the underlying
query to display its results in a table. In addition, the SQL Query
Expand Down Expand Up @@ -95,6 +97,7 @@ you can find their release notes
[1064]: https://github.com/enso-org/ide/pull/1064
[1316]: https://github.com/enso-org/ide/pull/1316
[1318]: https://github.com/enso-org/ide/pull/1318
[1355]: https://github.com/enso-org/ide/pull/1355
[1332]: https://github.com/enso-org/ide/pull/1332
[1341]: https://github.com/enso-org/ide/pull/1341
[1328]: https://github.com/enso-org/ide/pull/1328
Expand All @@ -104,6 +107,8 @@ you can find their release notes
[1385]: https://github.com/enso-org/ide/pull/1385
[1393]: https://github.com/enso-org/ide/pull/1393
[1392]: https://github.com/enso-org/ide/pull/1392
[1335]: https://github.com/enso-org/ide/pull/1335
[1358]: https://github.com/enso-org/ide/pull/1358
[1377]: https://github.com/enso-org/ide/pull/1377

<br/>
Expand Down
3 changes: 3 additions & 0 deletions ide/src/js/lib/content/src/style.css
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,9 @@
.visualization {
z-index: 2;
border-radius: 14px;
/* visualizations may be put into the fullscreen-vis layer which has pointer-events set to none, so we need to
override it */
pointer-events: auto;
}

#crash-banner {
Expand Down
186 changes: 162 additions & 24 deletions ide/src/rust/ensogl/lib/core/src/display/object/class.rs
Original file line number Diff line number Diff line change
Expand Up @@ -243,6 +243,8 @@ impl<Host> Model<Host> {
/// it. It is mainly used for a special 'root' element if such exists.
pub fn force_set_visibility(&self, visibility:bool) {
self.visible.set(visibility);
// TODO[ao] this function should make the next update call on_show or on_hide
// https://github.com/enso-org/ide/issues/1406
}

/// Removes child by a given index. Does nothing if the index was incorrect.
Expand Down Expand Up @@ -342,22 +344,26 @@ impl<Host> Model<Host> {

/// Hide all removed children and show this display object if it was attached to a new parent.
fn update_visibility(&self, host:&Host, parent_scene_layers:&[LayerId]) {
self.take_removed_children_and_hide_orphans(host);
self.take_removed_children_and_update_their_visibility(host);
let parent_changed = self.dirty.parent.check();
if parent_changed && !self.is_orphan() {
self.set_vis_true(host,parent_scene_layers)
}
}

fn take_removed_children_and_hide_orphans(&self, host:&Host) {
fn take_removed_children_and_update_their_visibility(&self, host:&Host) {
if self.dirty.removed_children.check_all() {
debug!(self.logger, "Updating removed children.", || {
for child in self.dirty.removed_children.take().into_iter() {
if let Some(child) = child.upgrade() {
if child.is_orphan() {
if !child.has_visible_parent() {
child.set_vis_false(host);
child.take_removed_children_and_hide_orphans(host);
}
// Even if the child is visible at this point, it does not mean that it
// should be visible after the entire update. Therefore, we must ensure that
// "removed children" lists in its subtree will be managed.
// See also test `visibility_test3`.
child.take_removed_children_and_update_their_visibility(host);
}
}
})
Expand All @@ -366,6 +372,7 @@ impl<Host> Model<Host> {

fn set_vis_false(&self, host:&Host) {
if self.visible.get() {
info!(self.logger,"Hiding.");
self.visible.set(false);
self.callbacks.on_hide(host);
self.children.borrow().iter().for_each(|child| {
Expand Down Expand Up @@ -606,7 +613,7 @@ impl<Host> Instance<Host> {
}

/// Add this object to the provided scene layer and remove it from all other layers. Do not use
// /// this method explicitly. Use layers' methods instead.
/// this method explicitly. Use layers' methods instead.
pub(crate) fn add_to_scene_layer_exclusive(&self, layer:LayerId) {
self.dirty.scene_layer.set();
*self.scene_layers.borrow_mut() = vec![layer];
Expand Down Expand Up @@ -681,6 +688,11 @@ impl<Host> Instance<Host> {
fn parent_index(&self) -> Option<usize> {
self.parent_bind.borrow().as_ref().map(|t| t.index)
}

fn has_visible_parent(&self) -> bool {
let parent = self.parent_bind.borrow().as_ref().and_then(|b| b.parent.upgrade());
parent.map_or(false, |parent| parent.is_visible())
}
}


Expand Down Expand Up @@ -1189,55 +1201,181 @@ mod tests {
assert_eq!(node1.children_count(),0);
}

/// A utility to test display object instances' visibility.
#[derive(Clone,CloneRef,Debug)]
struct TestedNode {
node : Instance<()>,
show_counter : Rc<Cell<usize>>,
hide_counter : Rc<Cell<usize>>,
}

impl Deref for TestedNode {
type Target = Instance<()>;
fn deref(&self) -> &Self::Target { &self.node }
}

impl Object<()> for TestedNode {
fn display_object(&self) -> &Instance<()> { &self.node }
}

impl TestedNode {
fn new(label:impl Into<ImString>) -> Self {
let node = Instance::<()>::new(Logger::new(label));
let show_counter = Rc::<Cell<usize>>::default();
let hide_counter = Rc::<Cell<usize>>::default();
node.set_on_show(f__!(show_counter.set(show_counter.get() + 1)));
node.set_on_hide(f_! (hide_counter.set(hide_counter.get() + 1)));
Self {node,show_counter,hide_counter}
}

fn reset_counters(&self) {
self.show_counter.set(0);
self.hide_counter.set(0);
}

fn check_if_was_shown(&self) {
assert!(self.node.is_visible());
assert_eq!(self.show_counter.get(), 1);
assert_eq!(self.hide_counter.get(), 0);
self.reset_counters();
}

fn check_if_was_hidden(&self) {
assert!(!self.node.is_visible());
assert_eq!(self.show_counter.get(), 0);
assert_eq!(self.hide_counter.get(), 1);
self.reset_counters();
}

fn check_if_visibility_did_not_changed(&self, expected_visibility:bool) {
assert_eq!(self.node.is_visible(), expected_visibility);
assert_eq!(self.show_counter.get(), 0);
assert_eq!(self.hide_counter.get(), 0);
}

fn check_if_still_shown(&self) { self.check_if_visibility_did_not_changed(true) }
fn check_if_still_hidden(&self) { self.check_if_visibility_did_not_changed(false) }
}

#[test]
fn visibility_test() {
let node1 = Instance::<()>::new(Logger::new("node1"));
let node2 = Instance::<()>::new(Logger::new("node2"));
let node3 = Instance::<()>::new(Logger::new("node3"));
let node1 = TestedNode::new("node1");
let node2 = TestedNode::new("node2");
let node3 = TestedNode::new("node3");
node1.force_set_visibility(true);
assert_eq!(node3.is_visible(),false);
node3.check_if_still_hidden();
node3.update(&());
assert_eq!(node3.is_visible(),false);
node3.check_if_still_hidden();

node1.add_child(&node2);
node2.add_child(&node3);
node1.update(&());
assert_eq!(node3.is_visible(),true);
node3.check_if_was_shown();

node3.unset_parent();
assert_eq!(node3.is_visible(),true);
node3.check_if_still_shown();

node1.update(&());
assert_eq!(node3.is_visible(),false);
node3.check_if_was_hidden();

node1.add_child(&node3);
node1.update(&());
assert_eq!(node3.is_visible(),true);
node3.check_if_was_shown();

node2.add_child(&node3);
node1.update(&());
assert_eq!(node3.is_visible(),true);
node3.check_if_still_shown();

node3.unset_parent();
node1.update(&());
assert_eq!(node3.is_visible(),false);
node3.check_if_was_hidden();

node2.add_child(&node3);
node1.update(&());
assert_eq!(node3.is_visible(),true);
node3.check_if_was_shown();
}

#[test]
fn visibility_test2() {
let node1 = Instance::<()>::new(Logger::new("node1"));
let node2 = Instance::<()>::new(Logger::new("node2"));
assert_eq!(node1.is_visible(),false);
let node1 = TestedNode::new("node1");
let node2 = TestedNode::new("node2");
node1.check_if_still_hidden();
node1.update(&());
node1.check_if_still_hidden();
node1.force_set_visibility(true);
node1.update(&());
node1.check_if_still_shown();

node1.add_child(&node2);
node1.update(&());
node1.check_if_still_shown();
node2.check_if_was_shown();
}

#[test]
fn visibility_test3() {
let node1 = TestedNode::new("node1");
let node2 = TestedNode::new("node2");
let node3 = TestedNode::new("node3");
node1.force_set_visibility(true);
node1.add_child(&node2);
node2.add_child(&node3);
node1.update(&());
node2.check_if_was_shown();
node3.check_if_was_shown();

node3.unset_parent();
node3.add_child(&node2);
node1.update(&());
assert_eq!(node1.is_visible(),false);
node2.check_if_was_hidden();
node3.check_if_was_hidden();
}

#[test]
fn visibility_test4() {
let node1 = TestedNode::new("node1");
let node2 = TestedNode::new("node2");
let node3 = TestedNode::new("node3");
let node4 = TestedNode::new("node4");
node1.force_set_visibility(true);
node1.add_child(&node2);
node2.add_child(&node3);
node1.update(&());
assert_eq!(node1.is_visible(),true);
node2.check_if_was_shown();
node3.check_if_was_shown();
node4.check_if_still_hidden();

node2.unset_parent();
node1.add_child(&node2);
node1.update(&());
assert_eq!(node1.is_visible(),true);
assert_eq!(node2.is_visible(),true);
node2.check_if_still_shown();
node3.check_if_still_shown();
node4.check_if_still_hidden();

node1.add_child(&node4);
node4.add_child(&node3);
node1.update(&());
node2.check_if_still_shown();
// TODO[ao]: This assertion fails, see https://github.com/enso-org/ide/issues/1405
// node3.check_if_still_shown();
node3.reset_counters();
node4.check_if_was_shown();

node4.unset_parent();
node2.unset_parent();
node1.update(&());
node2.check_if_was_hidden();
node3.check_if_was_hidden();
node4.check_if_was_hidden();

node2.add_child(&node3);
node1.update(&());
node2.check_if_still_hidden();
node3.check_if_still_hidden();
node4.check_if_still_hidden();
}


#[test]
fn deep_hierarchy_test() {

Expand Down
30 changes: 21 additions & 9 deletions ide/src/rust/ensogl/lib/core/src/display/scene.rs
Original file line number Diff line number Diff line change
Expand Up @@ -458,9 +458,12 @@ impl Dom {
#[derive(Clone,CloneRef,Debug)]
pub struct DomLayers {
/// Back DOM scene layer.
pub back: DomScene,
pub back : DomScene,
/// Back DOM scene layer with fullscreen visualization. Kept separately from `back`, because the
/// fullscreen visualizations should not share camera with main view.
pub fullscreen_vis : DomScene,
/// Front DOM scene layer.
pub front: DomScene,
pub front : DomScene,
/// The WebGL scene layer.
pub canvas : web_sys::HtmlCanvasElement,

Expand All @@ -469,25 +472,29 @@ pub struct DomLayers {
impl DomLayers {
/// Constructor.
pub fn new(logger:&Logger, dom:&web_sys::HtmlDivElement) -> Self {
let canvas = web::create_canvas();
let front = DomScene::new(logger);
let back = DomScene::new(logger);
let canvas = web::create_canvas();
let front = DomScene::new(logger);
let fullscreen_vis = DomScene::new(logger);
let back = DomScene::new(logger);
canvas.set_style_or_warn("height" , "100vh" , &logger);
canvas.set_style_or_warn("width" , "100vw" , &logger);
canvas.set_style_or_warn("display" , "block" , &logger);
// Position must not be "static" to have z-index working.
canvas.set_style_or_warn("position" , "absolute", &logger);
canvas.set_style_or_warn("z-index" , "1" , &logger);
canvas.set_style_or_warn("z-index" , "2" , &logger);
canvas.set_style_or_warn("pointer-events", "none" , &logger);
front.dom.set_class_name("front");
front.dom.set_style_or_warn("z-index", "1", &logger);
back.dom.set_class_name("back");
back.dom.set_style_or_warn("pointer-events", "auto", &logger);
back.dom.set_style_or_warn("z-index" , "0" , &logger);
fullscreen_vis.dom.set_class_name("fullscreen_vis");
fullscreen_vis.dom.set_style_or_warn("z-index" , "1" , &logger);
dom.append_or_panic(&canvas);
dom.append_or_panic(&front.dom);
dom.append_or_panic(&back.dom);
Self {front,canvas,back}
dom.append_or_panic(&fullscreen_vis.dom);
Self {front,canvas,back,fullscreen_vis}
}
}

Expand Down Expand Up @@ -854,14 +861,19 @@ impl SceneData {
fn update_camera(&self, scene:&Scene) {
// Updating camera for DOM layers. Please note that DOM layers cannot use multi-camera
// setups now, so we are using here the main camera only.
let camera = self.camera();
let changed = camera.update(scene);
let camera = self.camera();
let fullscreen_vis_camera = self.layers.viz_fullscreen.camera();
let changed = camera.update(scene);
if changed {
self.frp.camera_changed_source.emit(());
self.symbols.set_camera(&camera);
self.dom.layers.front.update_view_projection(&camera);
self.dom.layers.back.update_view_projection(&camera);
}
let fs_vis_camera_changed = fullscreen_vis_camera.update(scene);
if fs_vis_camera_changed {
self.dom.layers.fullscreen_vis.update_view_projection(&fullscreen_vis_camera);
}

// Updating all other cameras (the main camera was already updated, so it will be skipped).
for layer in &*self.layers.all() {
Expand Down
3 changes: 3 additions & 0 deletions ide/src/rust/ensogl/lib/core/src/display/scene/dom.rs
Original file line number Diff line number Diff line change
Expand Up @@ -179,6 +179,9 @@ impl DomScene {
pub fn manage(&self, object:&DomSymbol) {
let dom = object.dom();
let data = &self.data;
if object.is_visible() {
self.view_projection_dom.append_or_panic(&dom);
}
object.display_object().set_on_hide(f_!(dom.remove()));
object.display_object().set_on_show(f__!([data,dom] {
data.view_projection_dom.append_or_panic(&dom)
Expand Down
Loading

0 comments on commit 3934cca

Please sign in to comment.