mirror of
https://github.com/enso-org/enso.git
synced 2024-12-23 03:51:43 +03:00
Update code style to use rust fmt (#3131)
This commit is contained in:
parent
ab0f50a7a3
commit
c822256e6c
2
.github/workflows/gui.yml
vendored
2
.github/workflows/gui.yml
vendored
@ -122,6 +122,8 @@ jobs:
|
||||
run: npm install --save-dev --save-exact prettier
|
||||
- name: Install Clippy
|
||||
run: rustup component add clippy
|
||||
- name: Install Clippy
|
||||
run: rustup component add rustfmt
|
||||
- name: Lint Markdown sources
|
||||
run: npx prettier --check '*.md'
|
||||
- name: Lint JavaScript sources
|
||||
|
@ -240,8 +240,8 @@ commands.test.rust = async function(argv) {
|
||||
|
||||
commands.lint = command(`Lint the codebase`)
|
||||
commands.lint.rust = async function() {
|
||||
// We run clippy-preview due to https://github.com/rust-lang/rust-clippy/issues/4612
|
||||
await run_cargo('cargo',['clippy','--','-D','warnings'])
|
||||
await run_cargo('cargo',['fmt','--','--check'])
|
||||
}
|
||||
|
||||
|
||||
|
@ -128,6 +128,11 @@ let installClippy = {
|
||||
run: 'rustup component add clippy',
|
||||
}
|
||||
|
||||
let installFmt = {
|
||||
name: "Install Clippy",
|
||||
run: "rustup component add rustfmt"
|
||||
}
|
||||
|
||||
// Install fixed version to avoid upgrading to a breaking version.
|
||||
// Should be removed once this has a better solution as described here:
|
||||
// https://github.com/enso-org/ide/issues/1772
|
||||
@ -465,6 +470,7 @@ let workflow = {
|
||||
installRust,
|
||||
installPrettier,
|
||||
installClippy,
|
||||
installFmt,
|
||||
lintMarkdown,
|
||||
lintJavaScript,
|
||||
lintRust,
|
||||
|
@ -1,43 +1,20 @@
|
||||
---
|
||||
layout: style-guide
|
||||
title: Rust Style Guide
|
||||
category: style-guide
|
||||
tags: [style-guide,contributing]
|
||||
layout: style-guide title: Rust Style Guide category: style-guide tags: [style-guide,contributing]
|
||||
---
|
||||
|
||||
# Rust Style Guide
|
||||
|
||||
## Motivation - why not rely on a formatting tool for code style?
|
||||
|
||||
The code style is way more than just formatting. In many cases formatting can be
|
||||
automated. According to rustfmt docs: "formatting code is a mostly mechanical
|
||||
task which takes both time and mental effort. By using an automatic formatting
|
||||
tool, a programmer is relieved of this task and can concentrate on more
|
||||
important things.". While in many cases it is true, if the code author does not
|
||||
take extra effort to make his code pretty by refactoring long lines to
|
||||
variables, moving code to specific modules, or sections, the formatting tool
|
||||
will result in code that is hard to read and hard to write. Thus, it is
|
||||
important to take time to write code in such way that we can be proud of its
|
||||
quality.
|
||||
|
||||
Because `rustfmt` does not support many of our requirements, we have created a
|
||||
guide to describe how to format Rust code in this project. Please read it
|
||||
carefully. We hope that in the future, some of the things described below will
|
||||
be possible while using `rustfmt` (and we encourage you to contribute there!),
|
||||
however, even if it happens, many parts of this guide will still be valid and
|
||||
will need to be handled manually.
|
||||
|
||||
|
||||
We are using `rustfmt` for the basic formatting of our rust code, which is also checked on CI. We
|
||||
have some additional guidelines for imports, naming, sections within files and naming which are
|
||||
documented here.
|
||||
|
||||
## Styling rules
|
||||
|
||||
### Code width
|
||||
Each line in a source file should have max 80 chars of text (including
|
||||
comments).
|
||||
|
||||
### Imports
|
||||
Imports should be divided into 4 groups separated by blank lines. Items in the
|
||||
groups should be sorted alphabetically.
|
||||
|
||||
Imports should be divided into 4 groups separated by blank lines. Items in the groups should be
|
||||
sorted alphabetically.
|
||||
|
||||
```rust
|
||||
// Group 1: sub-module definitions.
|
||||
// Group 2: prelude-like imports.
|
||||
@ -46,6 +23,7 @@ groups should be sorted alphabetically.
|
||||
```
|
||||
|
||||
For example:
|
||||
|
||||
```rust
|
||||
pub mod display_object;
|
||||
|
||||
@ -60,17 +38,14 @@ use nalgebra::Matrix4;
|
||||
use nalgebra::Vector3;
|
||||
```
|
||||
|
||||
|
||||
### Sections
|
||||
|
||||
Source files should be divided into sections. Section headers should be placed
|
||||
before each new "concept" defined in a file. By "concept" we normally mean a
|
||||
structure with related implementations. In case related implementations use some
|
||||
helper structs with very small implementations, these helper structs may be
|
||||
defined in the same section. Moreover, the code in each section should be
|
||||
divided into sub-sections, grouping related definitions. At least one section
|
||||
should be defined in a file (if there is at least one struct definition as
|
||||
well). For example:
|
||||
Source files should be divided into sections. Section headers should be placed before each new "
|
||||
concept" defined in a file. By "concept" we normally mean a structure with related implementations.
|
||||
In case related implementations use some helper structs with very small implementations, these
|
||||
helper structs may be defined in the same section. Moreover, the code in each section should be
|
||||
divided into sub-sections, grouping related definitions. At least one section should be defined in a
|
||||
file (if there is at least one struct definition as well). For example:
|
||||
|
||||
```rust
|
||||
// =================
|
||||
@ -86,7 +61,6 @@ impl Default for AxisOrder {
|
||||
}
|
||||
|
||||
|
||||
|
||||
// =================
|
||||
// === Transform ===
|
||||
// =================
|
||||
@ -107,7 +81,6 @@ impl Default for TransformOrder {
|
||||
}
|
||||
|
||||
|
||||
|
||||
// =============================
|
||||
// === HierarchicalTransform ===
|
||||
// =============================
|
||||
@ -166,28 +139,15 @@ impl<OnChange:Callback0> HierarchicalTransform<OnChange> {
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
### Vertical spacing
|
||||
We use the following amount of vertical spacing:
|
||||
- 3 blank lines after imports.
|
||||
- 3 blank lines before each section.
|
||||
- 2 blank lines before each sub-section.
|
||||
- 1 blank line after each section / sub-section.
|
||||
- 1 blank line before functions / structures / impls.
|
||||
- 1 blank line at the end of the file.
|
||||
|
||||
Please note that vertical spacing overlaps. For example, if there is a section
|
||||
after imports, the total number of blank lines is 3, not 6.
|
||||
|
||||
|
||||
### Multiline Expressions
|
||||
Most (preferably all) expressions should be single line. Multiline expressions
|
||||
are hard to read and introduce noise in the code. Often, it is also an indicator
|
||||
of code that is not properly refactored. Try to refactor parts of multiline
|
||||
expressions to well-named variables, and divide them to several single-line
|
||||
expressions.
|
||||
|
||||
Most (preferably all) expressions should be single line. Multiline expressions are hard to read and
|
||||
introduce noise in the code. Often, it is also an indicator of code that is not properly refactored.
|
||||
Try to refactor parts of multiline expressions to well-named variables, and divide them to several
|
||||
single-line expressions.
|
||||
|
||||
Example of poorly formatted code:
|
||||
|
||||
```rust
|
||||
pub fn new() -> Self {
|
||||
let shape_dirty = ShapeDirty::new(logger.sub("shape_dirty"),
|
||||
@ -197,6 +157,7 @@ pub fn new() -> Self {
|
||||
Self { dirty_flag, dirty_flag }
|
||||
}
|
||||
```
|
||||
|
||||
Example of properly formatted code:
|
||||
|
||||
```rust
|
||||
@ -209,134 +170,11 @@ pub fn new() -> Self {
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
### Vertical alignment
|
||||
|
||||
The following elements should be aligned vertically in subsequent lines:
|
||||
- assignment operators (`=`),
|
||||
- type operators (`:`),
|
||||
- match arrows (`=>`),
|
||||
- similar parameters or types.
|
||||
|
||||
Examples:
|
||||
```rust
|
||||
impl Printer for GlobalVarStorage {
|
||||
fn print(&self, builder:&mut Builder) {
|
||||
match self {
|
||||
Self::ConstStorage => build!(builder,"const"),
|
||||
Self::UniformStorage => build!(builder,"uniform"),
|
||||
Self::InStorage (qual) => build!(builder,"in" ,qual),
|
||||
Self::OutStorage (qual) => build!(builder,"out",qual),
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
### Spaces
|
||||
- Type operator is not spaced: `fn test(foo:String, bar:Int) { ... }`.
|
||||
- Commas between complex expressions (including arg list) are spaced.
|
||||
- Commas between simple elements are not spaced: `Result<Self,Error>`.
|
||||
- Arguments to functions are not spaced: `build(builder,"out",qual)`.
|
||||
- Operators are always spaced: `let foo = a + b * c;`.
|
||||
|
||||
### Function definitions
|
||||
The following examples show proper function styles:
|
||||
|
||||
```rust
|
||||
pub fn new<Dom:Str>(dom:Dom, logger:Logger) -> Result<Self,Error> {
|
||||
...
|
||||
}
|
||||
```
|
||||
|
||||
```rust
|
||||
pub fn new<Dom:Str>(dom:Dom, logger:Logger) -> Result<Self,Error> {
|
||||
...
|
||||
}
|
||||
```
|
||||
|
||||
```rust
|
||||
pub fn new<Dom:Str>
|
||||
(dom:Dom, logger:Logger, on_dirty:OnDirty) -> Result<Self,Error> {
|
||||
...
|
||||
}
|
||||
```
|
||||
|
||||
```rust
|
||||
pub fn new<Dom:Str>
|
||||
(dom:Dom, logger:Logger, on_dirty:OnDirty, on_remove:OnRemove)
|
||||
-> Result<Self,Error> {
|
||||
...
|
||||
}
|
||||
```
|
||||
|
||||
```rust
|
||||
pub fn new<Dom:Str>
|
||||
( dom : Dom
|
||||
, logger : Logger
|
||||
, on_dirty : OnDirty
|
||||
, on_remove : OnRemove
|
||||
, on_replace : OnReplace
|
||||
) -> Result<Self,Error> {
|
||||
...
|
||||
}
|
||||
```
|
||||
|
||||
Long `where` clauses are formatted this way:
|
||||
|
||||
```rust
|
||||
pub fn new<D,L>(dom:D, logger:L) -> Result<Self,Error>
|
||||
where D:AsRef<str>, L:IsLogger {
|
||||
...
|
||||
}
|
||||
```
|
||||
|
||||
Or, in case they are really long, this way:
|
||||
|
||||
```rust
|
||||
pub fn new<D,L>(dom:D, logger:L) -> Result<Self,Error>
|
||||
where D:AsRef<str>
|
||||
L:IsLogger
|
||||
... {
|
||||
...
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
### Impl definitions
|
||||
Sometimes when browsing code it is hard to understand where the header of
|
||||
an impl declaration is. Thus, the following style allows to find it easier.
|
||||
The `where` block should be placed after a linebreak.
|
||||
All of the following are correct:
|
||||
|
||||
```rust
|
||||
// No constraints
|
||||
impl<T> Printer for Option<T> {
|
||||
...
|
||||
}
|
||||
```
|
||||
|
||||
```rust
|
||||
// Some constraints
|
||||
impl<T:Printer>
|
||||
Printer for Option<T> {
|
||||
...
|
||||
}
|
||||
```
|
||||
|
||||
```rust
|
||||
// Constraints in where block
|
||||
impl<T> Printer for Option<T>
|
||||
where T: Printer {
|
||||
...
|
||||
}
|
||||
```
|
||||
|
||||
### Getters and Setters
|
||||
Getters do not have the `get_` prefix, while setters do. If a setter is
|
||||
provided (method with the `set_` prefix), a `mut` accessor should be
|
||||
provided as well. The correct way of defining getters and setters is
|
||||
presented below:
|
||||
|
||||
Getters do not have the `get_` prefix, while setters do. If a setter is provided (method with
|
||||
the `set_` prefix), a `mut` accessor should be provided as well. The correct way of defining getters
|
||||
and setters is presented below:
|
||||
|
||||
```rust
|
||||
fn field(&self) -> &Type {
|
||||
@ -352,8 +190,8 @@ fn set_field(&mut self, val:Type) {
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
### Trait exporting
|
||||
|
||||
All names should be designed to be used in a qualified fashion. However, this makes one situation
|
||||
tricky. In order to use methods defined in a trait, it has to be in scope. Consider a trait
|
||||
`display::Object`. We want to use it as function bound like `fn test<T:display::Object>(t:T) {...}`,
|
||||
|
74
gui/rustfmt.toml
Normal file
74
gui/rustfmt.toml
Normal file
@ -0,0 +1,74 @@
|
||||
max_width = 100
|
||||
hard_tabs = false
|
||||
tab_spaces = 4
|
||||
newline_style = "Unix"
|
||||
indent_style = "Block"
|
||||
use_small_heuristics = "Max"
|
||||
fn_call_width = 100
|
||||
attr_fn_like_width = 100
|
||||
struct_lit_width = 100
|
||||
struct_variant_width = 100
|
||||
array_width = 100
|
||||
chain_width = 100
|
||||
single_line_if_else_max_width = 100
|
||||
wrap_comments = true
|
||||
format_code_in_doc_comments = true
|
||||
comment_width = 100
|
||||
normalize_comments = false
|
||||
normalize_doc_attributes = false
|
||||
license_template_path = ""
|
||||
format_strings = false
|
||||
format_macro_matchers = false
|
||||
format_macro_bodies = true
|
||||
empty_item_single_line = true
|
||||
struct_lit_single_line = true
|
||||
fn_single_line = false
|
||||
where_single_line = true
|
||||
imports_indent = "Block"
|
||||
imports_layout = "Mixed"
|
||||
imports_granularity = "Item"
|
||||
group_imports = "Preserve"
|
||||
reorder_imports = true
|
||||
reorder_modules = true
|
||||
reorder_impl_items = false
|
||||
type_punctuation_density = "Wide"
|
||||
space_before_colon = false
|
||||
space_after_colon = true
|
||||
spaces_around_ranges = false
|
||||
binop_separator = "Front"
|
||||
remove_nested_parens = true
|
||||
combine_control_expr = true
|
||||
overflow_delimited_expr = true
|
||||
struct_field_align_threshold = 20
|
||||
enum_discrim_align_threshold = 20
|
||||
match_arm_blocks = false
|
||||
match_arm_leading_pipes = "Never"
|
||||
force_multiline_blocks = false
|
||||
fn_args_layout = "Tall"
|
||||
brace_style = "PreferSameLine"
|
||||
control_brace_style = "AlwaysSameLine"
|
||||
trailing_semicolon = true
|
||||
trailing_comma = "Vertical"
|
||||
match_block_trailing_comma = false
|
||||
blank_lines_upper_bound = 3
|
||||
blank_lines_lower_bound = 0
|
||||
edition = "2018"
|
||||
version = "One"
|
||||
inline_attribute_width = 0
|
||||
merge_derives = false
|
||||
use_try_shorthand = false
|
||||
use_field_init_shorthand = true
|
||||
force_explicit_abi = true
|
||||
condense_wildcard_suffixes = false
|
||||
color = "Auto"
|
||||
unstable_features = false
|
||||
disable_all_formatting = false
|
||||
skip_children = false
|
||||
hide_parse_errors = false
|
||||
error_on_line_overflow = false
|
||||
error_on_unformatted = false
|
||||
report_todo = "Never"
|
||||
report_fixme = "Never"
|
||||
ignore = []
|
||||
emit_mode = "Files"
|
||||
make_backup = false
|
@ -1,8 +1,8 @@
|
||||
#![feature(trait_alias)]
|
||||
|
||||
use std::path;
|
||||
use std::io::ErrorKind;
|
||||
use std::fmt::Display;
|
||||
use std::io::ErrorKind;
|
||||
use std::path;
|
||||
|
||||
/// Types that can yield a reference to std::path::Path.
|
||||
pub trait PathRef = AsRef<path::Path>;
|
||||
@ -20,7 +20,8 @@ impl<T:AsRef<str>+Display> GithubRelease<T> {
|
||||
///
|
||||
/// The project_url should be a project's main page on GitHub.
|
||||
pub fn download(&self, destination_dir: &path::Path) {
|
||||
let url = format!("{}/releases/download/{}/{}",self.project_url,self.version,self.filename);
|
||||
let url =
|
||||
format!("{}/releases/download/{}/{}", self.project_url, self.version, self.filename);
|
||||
let destination_file = destination_dir.join(self.filename.as_ref());
|
||||
Self::remove_old_file(&destination_file);
|
||||
let mut resp = reqwest::blocking::get(&url).expect("Download failed.");
|
||||
|
@ -1,10 +1,12 @@
|
||||
use std::{path, env};
|
||||
use std::env;
|
||||
use std::path;
|
||||
|
||||
/// A module with functions generating huge chunk of texts for text component benchmarks.
|
||||
mod huge_text_generator {
|
||||
use std::collections::hash_map::DefaultHasher;
|
||||
use std::fs::File;
|
||||
use std::hash::{Hash,Hasher};
|
||||
use std::hash::Hash;
|
||||
use std::hash::Hasher;
|
||||
use std::io::Write;
|
||||
use std::path::Path;
|
||||
|
||||
@ -43,12 +45,14 @@ mod huge_text_generator {
|
||||
}
|
||||
|
||||
fn write_verse(file: &mut File, i: usize) {
|
||||
write!(file,
|
||||
write!(
|
||||
file,
|
||||
"{i} bottles of beer on the wall, {i} bottles of beer.\
|
||||
Take one down and pass it around, {j} bottles of beer on the wall. ",
|
||||
i = i,
|
||||
j = i - 1
|
||||
).unwrap();
|
||||
)
|
||||
.unwrap();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -2,8 +2,8 @@
|
||||
|
||||
use crate::prelude::*;
|
||||
|
||||
use ensogl_core::system::web;
|
||||
use ensogl_core::application::Application;
|
||||
use ensogl_core::system::web;
|
||||
use ensogl_core::DEPRECATED_Animation;
|
||||
use ensogl_text_msdf_sys::run_once_initialized;
|
||||
use logger::TraceLogger as Logger;
|
||||
@ -34,7 +34,6 @@ pub fn entry_point_animation() {
|
||||
// ========================
|
||||
|
||||
fn init() {
|
||||
|
||||
let logger: Logger = Logger::new("AnimationTest");
|
||||
let network = enso_frp::Network::new("test");
|
||||
let animation = DEPRECATED_Animation::<f32>::new(&network);
|
||||
|
@ -2,15 +2,15 @@
|
||||
|
||||
use ensogl_core::prelude::*;
|
||||
|
||||
use ensogl_core::data::color;
|
||||
use ensogl_core::display::navigation::navigator::Navigator;
|
||||
use ensogl_core::system::web;
|
||||
use wasm_bindgen::prelude::*;
|
||||
use ensogl_core::display::object::ObjectOps;
|
||||
use ensogl_core::display::world::*;
|
||||
use ensogl_core::display::scene;
|
||||
use ensogl_core::display::shape::*;
|
||||
use ensogl_core::data::color;
|
||||
use ensogl_core::display::style::theme;
|
||||
use ensogl_core::display::world::*;
|
||||
use ensogl_core::system::web;
|
||||
use wasm_bindgen::prelude::*;
|
||||
|
||||
|
||||
|
||||
@ -97,7 +97,8 @@ pub fn entry_point_complex_shape_system() {
|
||||
mask.size.set(Vector2::new(300.0, 300.0));
|
||||
mask.mod_position(|t| *t = Vector3::new(-50.0, 0.0, 0.0));
|
||||
|
||||
let scissor_box = scene::layer::ScissorBox::new_with_position_and_size(default(),Vector2(600,600));
|
||||
let scissor_box =
|
||||
scene::layer::ScissorBox::new_with_position_and_size(default(), Vector2(600, 600));
|
||||
scene.layers.main.set_scissor_box(Some(&scissor_box));
|
||||
|
||||
let view2 = shape::View::new(&logger);
|
||||
@ -111,7 +112,8 @@ pub fn entry_point_complex_shape_system() {
|
||||
let scene = world.scene().clone_ref();
|
||||
|
||||
let mut frame = 0;
|
||||
world.on_frame(move |_time| {
|
||||
world
|
||||
.on_frame(move |_time| {
|
||||
mask.set_position_x(((frame as f32) / 30.0).sin() * 100.0);
|
||||
|
||||
let _keep_alive = &navigator;
|
||||
@ -156,6 +158,6 @@ pub fn entry_point_complex_shape_system() {
|
||||
theme_manager.set_enabled(&["theme2".to_string()]);
|
||||
}
|
||||
frame += 1;
|
||||
}).forget();
|
||||
|
||||
})
|
||||
.forget();
|
||||
}
|
||||
|
@ -1,15 +1,15 @@
|
||||
#![allow(missing_docs)]
|
||||
|
||||
use ensogl_core::traits::*;
|
||||
use ensogl_core::prelude::*;
|
||||
|
||||
use ensogl_core::system::web;
|
||||
use ensogl_core::system::web::NodeInserter;
|
||||
use ensogl_core::display::symbol::DomSymbol;
|
||||
|
||||
use ensogl_core::display::navigation::navigator::Navigator;
|
||||
use ensogl_core::display::symbol::geometry::Sprite;
|
||||
use ensogl_core::display::symbol::geometry::SpriteSystem;
|
||||
use ensogl_core::display::symbol::DomSymbol;
|
||||
use ensogl_core::display::world::*;
|
||||
use ensogl_core::display::navigation::navigator::Navigator;
|
||||
use ensogl_core::system::web;
|
||||
use ensogl_core::system::web::NodeInserter;
|
||||
use web::StyleSetter;
|
||||
|
||||
use nalgebra::Vector2;
|
||||
@ -76,7 +76,8 @@ pub fn entry_point_dom_symbols() {
|
||||
let mut iter_to_change = 0;
|
||||
let mut i = 0;
|
||||
world.keep_alive_forever();
|
||||
world.on_frame(move |_| {
|
||||
world
|
||||
.on_frame(move |_| {
|
||||
let _keep_alive = &navigator;
|
||||
let _keep_alive = &sprites;
|
||||
let _keep_alive = &sprite_system;
|
||||
@ -89,5 +90,6 @@ pub fn entry_point_dom_symbols() {
|
||||
}
|
||||
}
|
||||
iter_to_change -= 1;
|
||||
}).forget();
|
||||
})
|
||||
.forget();
|
||||
}
|
||||
|
@ -3,11 +3,11 @@
|
||||
|
||||
use enso_prelude::*;
|
||||
|
||||
use ensogl_core::frp::web;
|
||||
use ensogl_core::display::world::World;
|
||||
use ensogl_core::frp::web;
|
||||
use ensogl_web::drop;
|
||||
use wasm_bindgen::prelude::*;
|
||||
use wasm_bindgen_futures::spawn_local;
|
||||
use ensogl_web::drop;
|
||||
|
||||
fn download_file(file: drop::File) {
|
||||
spawn_local(async move {
|
||||
@ -16,11 +16,11 @@ fn download_file(file:drop::File) {
|
||||
match file.read_chunk().await {
|
||||
Ok(Some(chunk)) => {
|
||||
INFO!("Received chunk: {chunk:?}");
|
||||
},
|
||||
}
|
||||
Ok(None) => {
|
||||
INFO!("All chunks received successfully");
|
||||
break
|
||||
},
|
||||
break;
|
||||
}
|
||||
Err(err) => {
|
||||
ERROR!("Error in receiving chunk promise: {err:?}");
|
||||
break;
|
||||
@ -45,16 +45,16 @@ pub fn entry_point_drop_manager() {
|
||||
}
|
||||
|
||||
let mut loader_hidden = false;
|
||||
world.on_frame(move |_| {
|
||||
world
|
||||
.on_frame(move |_| {
|
||||
if !loader_hidden {
|
||||
web::get_element_by_id("loader").map(|t| {
|
||||
t.parent_node().map(|p| {
|
||||
p.remove_child(&t).unwrap()
|
||||
})
|
||||
}).ok();
|
||||
web::get_element_by_id("loader")
|
||||
.map(|t| t.parent_node().map(|p| p.remove_child(&t).unwrap()))
|
||||
.ok();
|
||||
loader_hidden = true;
|
||||
}
|
||||
}).forget();
|
||||
})
|
||||
.forget();
|
||||
|
||||
std::mem::forget(world);
|
||||
std::mem::forget(network);
|
||||
|
@ -2,21 +2,21 @@
|
||||
|
||||
use crate::prelude::*;
|
||||
|
||||
use ensogl_core::animation::easing::*;
|
||||
use ensogl_core::animation;
|
||||
use ensogl_core::system::web::AttributeSetter;
|
||||
use ensogl_core::animation::easing::*;
|
||||
use ensogl_core::system::web;
|
||||
use ensogl_core::system::web::create_element;
|
||||
use ensogl_core::system::web::get_element_by_id;
|
||||
use ensogl_core::system::web::AttributeSetter;
|
||||
use ensogl_core::system::web::NodeInserter;
|
||||
use ensogl_core::system::web::StyleSetter;
|
||||
use ensogl_core::system::web;
|
||||
use js_sys::Math;
|
||||
use nalgebra::Vector2;
|
||||
use std::ops::Add;
|
||||
use std::ops::Mul;
|
||||
use std::rc::Rc;
|
||||
use wasm_bindgen::JsCast;
|
||||
use wasm_bindgen::prelude::*;
|
||||
use wasm_bindgen::JsCast;
|
||||
use web_sys::CanvasRenderingContext2d;
|
||||
use web_sys::HtmlCanvasElement;
|
||||
use web_sys::HtmlElement;
|
||||
@ -32,7 +32,7 @@ use web_sys::HtmlElement;
|
||||
#[allow(missing_docs)]
|
||||
pub struct SpriteData {
|
||||
pub position: Vector2<f32>,
|
||||
pub size : f64
|
||||
pub size: f64,
|
||||
}
|
||||
|
||||
impl SpriteData {
|
||||
@ -79,7 +79,7 @@ impl Add<SpriteData> for SpriteData {
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct Canvas {
|
||||
canvas: HtmlCanvasElement,
|
||||
context : CanvasRenderingContext2d
|
||||
context: CanvasRenderingContext2d,
|
||||
}
|
||||
|
||||
impl Canvas {
|
||||
@ -123,7 +123,12 @@ impl Canvas {
|
||||
self.context.scale(self.width() / 2.0, self.height() / 2.0).ok();
|
||||
self.context.set_line_width(2.0 / self.height());
|
||||
self.context.translate(1.0, 1.0).ok();
|
||||
self.context.fill_rect(point.x as f64 - size / 2.0,point.y as f64 - size / 2.0,size,size);
|
||||
self.context.fill_rect(
|
||||
point.x as f64 - size / 2.0,
|
||||
point.y as f64 - size / 2.0,
|
||||
size,
|
||||
size,
|
||||
);
|
||||
self.context.restore();
|
||||
}
|
||||
|
||||
@ -185,7 +190,15 @@ impl Sampler {
|
||||
let easing_animator = Animator::new(start, end, easing_function2, animation_cb, ());
|
||||
let time = 0.0;
|
||||
easing_animator.set_duration(2000.0);
|
||||
Self {color,time,left_canvas,right_canvas,easing_animator,properties,easing_function}
|
||||
Self {
|
||||
color,
|
||||
time,
|
||||
left_canvas,
|
||||
right_canvas,
|
||||
easing_animator,
|
||||
properties,
|
||||
easing_function,
|
||||
}
|
||||
}
|
||||
|
||||
fn render(&mut self, time_diff: f32) {
|
||||
@ -209,16 +222,16 @@ impl Sampler {
|
||||
// ===============
|
||||
|
||||
struct Example {
|
||||
_animator : animation::Loop<Box<dyn FnMut(animation::TimeInfo)>>
|
||||
_animator: animation::Loop<Box<dyn FnMut(animation::TimeInfo)>>,
|
||||
}
|
||||
|
||||
impl Example {
|
||||
#[allow(trivial_casts)]
|
||||
pub fn new
|
||||
( name : &str
|
||||
, ease_in : impl CloneableFnEasing
|
||||
, ease_out : impl CloneableFnEasing
|
||||
, ease_in_out : impl CloneableFnEasing
|
||||
pub fn new(
|
||||
name: &str,
|
||||
ease_in: impl CloneableFnEasing,
|
||||
ease_out: impl CloneableFnEasing,
|
||||
ease_in_out: impl CloneableFnEasing,
|
||||
) -> Self {
|
||||
let example = web::create_div();
|
||||
example.set_attribute_or_panic("id", name);
|
||||
|
@ -5,8 +5,8 @@ use ensogl_core::prelude::*;
|
||||
use ensogl_core::data::color;
|
||||
use ensogl_core::display::world::*;
|
||||
use ensogl_core::system::web;
|
||||
use ensogl_text_msdf_sys::run_once_initialized;
|
||||
use ensogl_text::typeface::*;
|
||||
use ensogl_text_msdf_sys::run_once_initialized;
|
||||
use wasm_bindgen::prelude::*;
|
||||
|
||||
|
||||
|
@ -13,7 +13,7 @@
|
||||
/// leaked when the `Leak` is dropped.
|
||||
#[derive(Debug)]
|
||||
pub struct Leak<T> {
|
||||
value: Option<T>
|
||||
value: Option<T>,
|
||||
}
|
||||
|
||||
impl<T> Leak<T> {
|
||||
|
@ -20,18 +20,16 @@
|
||||
#![warn(unsafe_code)]
|
||||
#![warn(unused_import_braces)]
|
||||
#![warn(unused_qualifications)]
|
||||
|
||||
#![recursion_limit = "1024"]
|
||||
|
||||
#[allow(clippy::option_map_unit_fn)]
|
||||
|
||||
mod leak;
|
||||
pub mod animation;
|
||||
pub mod complex_shape_system;
|
||||
pub mod dom_symbols;
|
||||
pub mod drop_manager;
|
||||
pub mod easing_animator;
|
||||
pub mod glyph_system;
|
||||
#[allow(clippy::option_map_unit_fn)]
|
||||
mod leak;
|
||||
pub mod list_view;
|
||||
pub mod mouse_events;
|
||||
pub mod scroll_area;
|
||||
@ -43,6 +41,6 @@ pub mod text_area;
|
||||
|
||||
/// Common types that should be visible across the whole crate.
|
||||
pub mod prelude {
|
||||
pub use ensogl_core::prelude::*;
|
||||
pub use super::leak::*;
|
||||
pub use ensogl_core::prelude::*;
|
||||
}
|
||||
|
@ -2,15 +2,15 @@
|
||||
|
||||
use crate::prelude::*;
|
||||
|
||||
use ensogl_core::system::web;
|
||||
use ensogl_core::application::Application;
|
||||
use ensogl_core::display::object::ObjectOps;
|
||||
use ensogl_text_msdf_sys::run_once_initialized;
|
||||
use ensogl_core::system::web;
|
||||
use ensogl_gui_components::list_view;
|
||||
use ensogl_text::buffer::data::unit::Bytes;
|
||||
use ensogl_text_msdf_sys::run_once_initialized;
|
||||
use ensogl_theme as theme;
|
||||
use logger::TraceLogger as Logger;
|
||||
use wasm_bindgen::prelude::*;
|
||||
use ensogl_text::buffer::data::unit::Bytes;
|
||||
use ensogl_theme as theme;
|
||||
|
||||
|
||||
|
||||
@ -49,7 +49,9 @@ impl MockEntries {
|
||||
}
|
||||
|
||||
impl list_view::entry::ModelProvider<list_view::entry::GlyphHighlightedLabel> for MockEntries {
|
||||
fn entry_count(&self) -> usize { self.entries_count }
|
||||
fn entry_count(&self) -> usize {
|
||||
self.entries_count
|
||||
}
|
||||
|
||||
fn get(&self, id: usize) -> Option<list_view::entry::GlyphHighlightedLabelModel> {
|
||||
if id >= self.entries_count {
|
||||
|
@ -3,7 +3,6 @@
|
||||
use ensogl_core::prelude::*;
|
||||
use wasm_bindgen::prelude::*;
|
||||
|
||||
use ensogl_core::system::web;
|
||||
use ensogl_core::application;
|
||||
use ensogl_core::application::Application;
|
||||
use ensogl_core::data::color;
|
||||
@ -12,6 +11,7 @@ use ensogl_core::display;
|
||||
use ensogl_core::display::navigation::navigator::Navigator;
|
||||
use ensogl_core::display::object::ObjectOps;
|
||||
use ensogl_core::display::shape::*;
|
||||
use ensogl_core::system::web;
|
||||
|
||||
use enso_frp as frp;
|
||||
use ensogl_text_msdf_sys::run_once_initialized;
|
||||
@ -103,22 +103,34 @@ impl View {
|
||||
}
|
||||
|
||||
impl display::Object for View {
|
||||
fn display_object(&self) -> &display::object::Instance { &self.model.display_object }
|
||||
fn display_object(&self) -> &display::object::Instance {
|
||||
&self.model.display_object
|
||||
}
|
||||
}
|
||||
|
||||
impl Deref for View {
|
||||
type Target = Frp;
|
||||
fn deref(&self) -> &Self::Target { &self.frp }
|
||||
fn deref(&self) -> &Self::Target {
|
||||
&self.frp
|
||||
}
|
||||
}
|
||||
|
||||
impl application::command::FrpNetworkProvider for View {
|
||||
fn network(&self) -> &frp::Network { &self.frp.network }
|
||||
fn network(&self) -> &frp::Network {
|
||||
&self.frp.network
|
||||
}
|
||||
}
|
||||
|
||||
impl application::View for View {
|
||||
fn label() -> &'static str { "Circul" }
|
||||
fn new(app:&Application) -> Self { View::new(app) }
|
||||
fn app(&self) -> &Application { &self.model.app }
|
||||
fn label() -> &'static str {
|
||||
"Circul"
|
||||
}
|
||||
fn new(app: &Application) -> Self {
|
||||
View::new(app)
|
||||
}
|
||||
fn app(&self) -> &Application {
|
||||
&self.model.app
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
@ -6,14 +6,16 @@ use wasm_bindgen::prelude::*;
|
||||
use ensogl_core::application::Application;
|
||||
use ensogl_core::data::color;
|
||||
use ensogl_core::display::object::ObjectOps;
|
||||
use ensogl_core::display::shape::Circle;
|
||||
use ensogl_core::display::shape::PixelDistance;
|
||||
use ensogl_core::display::shape::Rect;
|
||||
use ensogl_core::display::shape::ShapeOps;
|
||||
use ensogl_core::display::shape::ShapeSystem;
|
||||
use ensogl_core::display::Sprite;
|
||||
use ensogl_core::system::web;
|
||||
use ensogl_gui_components::scroll_area::ScrollArea;
|
||||
use ensogl_text_msdf_sys::run_once_initialized;
|
||||
use ensogl_theme as theme;
|
||||
use ensogl_gui_components::scroll_area::ScrollArea;
|
||||
use ensogl_core::display::shape::{Circle, Rect, ShapeSystem};
|
||||
use ensogl_core::display::shape::PixelDistance;
|
||||
use ensogl_core::display::shape::ShapeOps;
|
||||
use ensogl_core::display::Sprite;
|
||||
|
||||
|
||||
|
||||
|
@ -2,14 +2,14 @@
|
||||
|
||||
use ensogl_core::prelude::*;
|
||||
|
||||
use ensogl_core::data::color;
|
||||
use ensogl_core::display::navigation::navigator::Navigator;
|
||||
use ensogl_core::system::web;
|
||||
use wasm_bindgen::prelude::*;
|
||||
use ensogl_core::display::object::ObjectOps;
|
||||
use ensogl_core::display::shape::ShapeSystem;
|
||||
use ensogl_core::display::world::*;
|
||||
use ensogl_core::display::shape::*;
|
||||
use ensogl_core::data::color;
|
||||
use ensogl_core::display::world::*;
|
||||
use ensogl_core::system::web;
|
||||
use wasm_bindgen::prelude::*;
|
||||
|
||||
|
||||
|
||||
@ -53,8 +53,10 @@ pub fn entry_point_shape_system() {
|
||||
world.add_child(&sprite_system);
|
||||
world.keep_alive_forever();
|
||||
|
||||
world.on_frame(move |_time| {
|
||||
world
|
||||
.on_frame(move |_time| {
|
||||
let _keep_alive = &sprite;
|
||||
let _keep_alive = &navigator;
|
||||
}).forget();
|
||||
})
|
||||
.forget();
|
||||
}
|
||||
|
@ -7,8 +7,8 @@ use ensogl_core::application::Application;
|
||||
use ensogl_core::data::color;
|
||||
use ensogl_core::display::object::ObjectOps;
|
||||
use ensogl_core::system::web;
|
||||
use ensogl_gui_components::selector::Bounds;
|
||||
use ensogl_gui_components::selector;
|
||||
use ensogl_gui_components::selector::Bounds;
|
||||
use ensogl_text_msdf_sys::run_once_initialized;
|
||||
use ensogl_theme as theme;
|
||||
|
||||
|
@ -6,8 +6,8 @@ use wasm_bindgen::prelude::*;
|
||||
use ensogl_core::display::navigation::navigator::Navigator;
|
||||
use ensogl_core::display::symbol::geometry::SpriteSystem;
|
||||
use ensogl_core::display::world::*;
|
||||
use ensogl_core::system::web::forward_panic_hook_to_console;
|
||||
use ensogl_core::system::web;
|
||||
use ensogl_core::system::web::forward_panic_hook_to_console;
|
||||
|
||||
|
||||
#[wasm_bindgen]
|
||||
@ -30,7 +30,8 @@ pub fn entry_point_sprite_system() {
|
||||
world.keep_alive_forever();
|
||||
|
||||
let mut i = 0;
|
||||
world.on_frame(move |_| {
|
||||
world
|
||||
.on_frame(move |_| {
|
||||
i += 1;
|
||||
let _keep_alive = &navigator;
|
||||
let _keep_alive = &sprite1;
|
||||
@ -40,5 +41,6 @@ pub fn entry_point_sprite_system() {
|
||||
sprite1.mod_position(|p| p.x += 1.0);
|
||||
sprite2.mod_position(|p| p.y += 1.0);
|
||||
}
|
||||
}).forget();
|
||||
})
|
||||
.forget();
|
||||
}
|
||||
|
@ -2,15 +2,15 @@
|
||||
|
||||
use ensogl_core::traits::*;
|
||||
|
||||
use ensogl_core::animation;
|
||||
use ensogl_core::display::camera::Camera2d;
|
||||
use ensogl_core::display::navigation::navigator::Navigator;
|
||||
use ensogl_core::display::symbol::geometry::Sprite;
|
||||
use ensogl_core::display::symbol::geometry::SpriteSystem;
|
||||
use ensogl_core::display::world::*;
|
||||
use ensogl_core::prelude::*;
|
||||
use ensogl_core::system::web::forward_panic_hook_to_console;
|
||||
use ensogl_core::system::web;
|
||||
use ensogl_core::animation;
|
||||
use ensogl_core::system::web::forward_panic_hook_to_console;
|
||||
use nalgebra::Vector2;
|
||||
use nalgebra::Vector3;
|
||||
use wasm_bindgen::prelude::*;
|
||||
@ -45,7 +45,8 @@ pub fn entry_point_sprite_system_benchmark() {
|
||||
|
||||
let mut iter: i32 = 0;
|
||||
let mut i = 0;
|
||||
world.on_frame(move |time| {
|
||||
world
|
||||
.on_frame(move |time| {
|
||||
i += 1;
|
||||
if i <= 100 {
|
||||
sprite1.mod_position(|p| p.x += 1.0);
|
||||
@ -63,18 +64,19 @@ pub fn entry_point_sprite_system_benchmark() {
|
||||
// println!("sprite[5] is visible? {:?}",sprites[5].is_visible());
|
||||
|
||||
on_frame(&camera, time, &mut iter, &sprite1, &mut sprites, &sprite_system)
|
||||
}).forget();
|
||||
})
|
||||
.forget();
|
||||
}
|
||||
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
#[allow(clippy::many_single_char_names)]
|
||||
pub fn on_frame
|
||||
( camera : &Camera2d
|
||||
, time : animation::TimeInfo
|
||||
, iter : &mut i32
|
||||
, sprite1 : &Sprite
|
||||
, sprites : &mut Vec<Sprite>
|
||||
, sprite_system : &SpriteSystem
|
||||
pub fn on_frame(
|
||||
camera: &Camera2d,
|
||||
time: animation::TimeInfo,
|
||||
iter: &mut i32,
|
||||
sprite1: &Sprite,
|
||||
sprites: &mut Vec<Sprite>,
|
||||
sprite_system: &SpriteSystem,
|
||||
) {
|
||||
*iter += 1;
|
||||
|
||||
@ -122,7 +124,11 @@ pub fn on_frame
|
||||
y += (z * 1.25 + t * 2.00).cos() * 0.5;
|
||||
z += (x * 1.25 + t * 3.25).cos() * 0.5;
|
||||
|
||||
let position = Vector3::new(x * 150.0 + half_width - 75.0, y * 150.0 + half_height - 75.0, z * 150.0);
|
||||
let position = Vector3::new(
|
||||
x * 150.0 + half_width - 75.0,
|
||||
y * 150.0 + half_height - 75.0,
|
||||
z * 150.0,
|
||||
);
|
||||
sprite.set_position(position);
|
||||
}
|
||||
}
|
||||
|
@ -2,12 +2,12 @@
|
||||
|
||||
use ensogl_core::prelude::*;
|
||||
|
||||
use ensogl_core::application::Application;
|
||||
use ensogl_core::display::navigation::navigator::Navigator;
|
||||
use ensogl_core::system::web;
|
||||
use ensogl_text::Area;
|
||||
use ensogl_text_msdf_sys::run_once_initialized;
|
||||
use wasm_bindgen::prelude::*;
|
||||
use ensogl_core::application::Application;
|
||||
use ensogl_text::Area;
|
||||
use ensogl_core::display::navigation::navigator::Navigator;
|
||||
|
||||
|
||||
/// Main example runner.
|
||||
@ -26,7 +26,9 @@ pub fn entry_point_text_area() {
|
||||
fn init(app: &Application) {
|
||||
let area = app.new_view::<Area>();
|
||||
area.set_position_x(-100.0);
|
||||
area.set_content("Et Eärello Endorenna utúlien.\nSinome maruvan ar Hildinyar tenn' Ambar-metta");
|
||||
area.set_content(
|
||||
"Et Eärello Endorenna utúlien.\nSinome maruvan ar Hildinyar tenn' Ambar-metta",
|
||||
);
|
||||
area.focus();
|
||||
area.hover();
|
||||
area.set_cursor_at_end();
|
||||
@ -36,8 +38,10 @@ fn init(app:&Application) {
|
||||
|
||||
app.display.scene().add_child(&area);
|
||||
let keep = Some(area);
|
||||
app.display.on_frame(move |_frame| {
|
||||
app.display
|
||||
.on_frame(move |_frame| {
|
||||
let _ = &keep;
|
||||
}).forget();
|
||||
})
|
||||
.forget();
|
||||
std::mem::forget(navigator);
|
||||
}
|
||||
|
@ -9,11 +9,11 @@
|
||||
use crate::prelude::*;
|
||||
|
||||
use enso_frp as frp;
|
||||
use ensogl_core::application::Application;
|
||||
use ensogl_core::application::command::CommandApi;
|
||||
use ensogl_core::application;
|
||||
use ensogl_core::display::shape::*;
|
||||
use ensogl_core::application::command::CommandApi;
|
||||
use ensogl_core::application::Application;
|
||||
use ensogl_core::display;
|
||||
use ensogl_core::display::shape::*;
|
||||
|
||||
|
||||
|
||||
@ -84,10 +84,15 @@ impl<M: display::Object,F> display::Object for Component<M,F> {
|
||||
|
||||
impl<M, F: Frp<M>> Deref for Component<M, F> {
|
||||
type Target = F;
|
||||
fn deref(&self) -> &Self::Target { &self.frp }
|
||||
fn deref(&self) -> &Self::Target {
|
||||
&self.frp
|
||||
}
|
||||
}
|
||||
|
||||
impl<M, F: application::command::FrpNetworkProvider> application::command::FrpNetworkProvider
|
||||
for Component<M,F> {
|
||||
fn network(&self) -> &frp::Network { self.frp.network() }
|
||||
for Component<M, F>
|
||||
{
|
||||
fn network(&self) -> &frp::Network {
|
||||
self.frp.network()
|
||||
}
|
||||
}
|
||||
|
@ -6,12 +6,12 @@ use crate::list_view::entry::ModelProvider;
|
||||
|
||||
use enso_frp as frp;
|
||||
use enso_frp;
|
||||
use ensogl_core::DEPRECATED_Animation;
|
||||
use ensogl_core::application::Application;
|
||||
use ensogl_core::data::color;
|
||||
use ensogl_core::display::shape::*;
|
||||
use ensogl_core::display::shape::primitive::StyleWatch;
|
||||
use ensogl_core::display;
|
||||
use ensogl_core::display::shape::primitive::StyleWatch;
|
||||
use ensogl_core::display::shape::*;
|
||||
use ensogl_core::DEPRECATED_Animation;
|
||||
use ensogl_text as text;
|
||||
use ensogl_theme as theme;
|
||||
|
||||
@ -150,8 +150,10 @@ impl Model {
|
||||
self.selection_menu.unset_parent()
|
||||
}
|
||||
|
||||
fn get_content_item
|
||||
(&self, id:Option<list_view::entry::Id>) -> Option<<Entry as list_view::entry::Entry>::Model> {
|
||||
fn get_content_item(
|
||||
&self,
|
||||
id: Option<list_view::entry::Id>,
|
||||
) -> Option<<Entry as list_view::entry::Entry>::Model> {
|
||||
self.content.borrow().as_ref()?.get(id?)
|
||||
}
|
||||
|
||||
@ -190,7 +192,9 @@ pub struct DropDownMenu {
|
||||
|
||||
impl Deref for DropDownMenu {
|
||||
type Target = Frp;
|
||||
fn deref(&self) -> &Self::Target { &self.frp }
|
||||
fn deref(&self) -> &Self::Target {
|
||||
&self.frp
|
||||
}
|
||||
}
|
||||
|
||||
impl DropDownMenu {
|
||||
|
@ -7,10 +7,10 @@ use crate::prelude::*;
|
||||
|
||||
use crate::file_browser::model::*;
|
||||
|
||||
use ensogl_core::display::shape::*;
|
||||
use std::path::PathBuf;
|
||||
use ensogl_core::display;
|
||||
use ensogl_core::display::shape::*;
|
||||
use ensogl_core::display::Scene;
|
||||
use std::path::PathBuf;
|
||||
|
||||
|
||||
// ===========
|
||||
@ -56,7 +56,9 @@ pub struct FileBrowser {
|
||||
|
||||
impl Deref for FileBrowser {
|
||||
type Target = Frp;
|
||||
fn deref(&self) -> &Self::Target { &self.frp }
|
||||
fn deref(&self) -> &Self::Target {
|
||||
&self.frp
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -71,9 +73,13 @@ impl FileBrowser {
|
||||
}
|
||||
|
||||
impl Default for FileBrowser {
|
||||
fn default() -> Self { Self::new() }
|
||||
fn default() -> Self {
|
||||
Self::new()
|
||||
}
|
||||
}
|
||||
|
||||
impl display::Object for FileBrowser {
|
||||
fn display_object(&self) -> &display::object::Instance<Scene> {&self.display_object }
|
||||
fn display_object(&self) -> &display::object::Instance<Scene> {
|
||||
&self.display_object
|
||||
}
|
||||
}
|
||||
|
@ -4,8 +4,8 @@
|
||||
use crate::prelude::*;
|
||||
|
||||
use enso_frp as frp;
|
||||
use std::path::PathBuf;
|
||||
use std::cmp::Ordering;
|
||||
use std::path::PathBuf;
|
||||
|
||||
|
||||
// =============
|
||||
@ -53,17 +53,22 @@ impl Ord for EntryType {
|
||||
(Self::File, Self::File) => Ordering::Equal,
|
||||
(Self::File, Self::Folder { .. }) => Ordering::Greater,
|
||||
(Self::Folder { .. }, Self::File) => Ordering::Less,
|
||||
(Self::Folder {type_:type1,..},Self::Folder {type_:type2,..}) => type1.cmp(type2),
|
||||
(Self::Folder { type_: type1, .. }, Self::Folder { type_: type2, .. }) =>
|
||||
type1.cmp(type2),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl PartialOrd for EntryType {
|
||||
fn partial_cmp(&self, other: &Self) -> Option<Ordering> { Some(self.cmp(other)) }
|
||||
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
|
||||
Some(self.cmp(other))
|
||||
}
|
||||
}
|
||||
|
||||
impl PartialEq for EntryType {
|
||||
fn eq(&self, other: &Self) -> bool { self.cmp(other) == Ordering::Equal }
|
||||
fn eq(&self, other: &Self) -> bool {
|
||||
self.cmp(other) == Ordering::Equal
|
||||
}
|
||||
}
|
||||
|
||||
impl Eq for EntryType {}
|
||||
@ -87,8 +92,11 @@ pub struct Entry {
|
||||
pub trait FolderContent: Debug {
|
||||
/// Request the list of entries inside the folder. When the list is ready, it is emitted at
|
||||
/// `entries_loaded`. If an error occurs then the error message is emitted at `error_occurred`.
|
||||
fn request_entries
|
||||
(&self, entries_loaded:frp::Any<Rc<Vec<Entry>>>, error_occurred:frp::Any<ImString>);
|
||||
fn request_entries(
|
||||
&self,
|
||||
entries_loaded: frp::Any<Rc<Vec<Entry>>>,
|
||||
error_occurred: frp::Any<ImString>,
|
||||
);
|
||||
}
|
||||
|
||||
/// A wrapper around `Rc<dyn FolderContent>`. Necessary to implement the `Default` trait on this
|
||||
@ -118,8 +126,11 @@ impl<D:'static + FolderContent> From<D> for AnyFolderContent {
|
||||
pub struct EmptyFolderContent;
|
||||
|
||||
impl FolderContent for EmptyFolderContent {
|
||||
fn request_entries
|
||||
(&self, entries_loaded:frp::Any<Rc<Vec<Entry>>>, _error_occured:frp::Any<ImString>) {
|
||||
fn request_entries(
|
||||
&self,
|
||||
entries_loaded: frp::Any<Rc<Vec<Entry>>>,
|
||||
_error_occured: frp::Any<ImString>,
|
||||
) {
|
||||
entries_loaded.emit(Rc::new(vec![]));
|
||||
}
|
||||
}
|
||||
|
@ -7,9 +7,9 @@ use enso_frp as frp;
|
||||
use enso_frp;
|
||||
use ensogl_core::application::Application;
|
||||
use ensogl_core::data::color;
|
||||
use ensogl_core::display;
|
||||
use ensogl_core::display::shape::*;
|
||||
use ensogl_core::display::traits::*;
|
||||
use ensogl_core::display;
|
||||
use ensogl_text as text;
|
||||
|
||||
use ensogl_theme::component::label as theme;
|
||||
@ -174,5 +174,7 @@ impl Label {
|
||||
}
|
||||
|
||||
impl display::Object for Label {
|
||||
fn display_object(&self) -> &display::object::Instance { &self.model.display_object }
|
||||
fn display_object(&self) -> &display::object::Instance {
|
||||
&self.model.display_object
|
||||
}
|
||||
}
|
||||
|
@ -4,7 +4,6 @@
|
||||
|
||||
#![feature(option_result_contains)]
|
||||
#![feature(trait_alias)]
|
||||
|
||||
#![warn(missing_copy_implementations)]
|
||||
#![warn(missing_debug_implementations)]
|
||||
#![warn(missing_docs)]
|
||||
@ -13,7 +12,6 @@
|
||||
#![warn(unsafe_code)]
|
||||
#![warn(unused_import_braces)]
|
||||
#![warn(unused_qualifications)]
|
||||
|
||||
#![recursion_limit = "512"]
|
||||
|
||||
pub mod component;
|
||||
|
@ -10,8 +10,8 @@ use crate::shadow;
|
||||
|
||||
use enso_frp as frp;
|
||||
use ensogl_core::application;
|
||||
use ensogl_core::application::Application;
|
||||
use ensogl_core::application::shortcut;
|
||||
use ensogl_core::application::Application;
|
||||
use ensogl_core::display;
|
||||
use ensogl_core::display::scene::layer::LayerId;
|
||||
use ensogl_core::display::shape::*;
|
||||
@ -111,7 +111,6 @@ struct Model<E:Entry> {
|
||||
}
|
||||
|
||||
impl<E: Entry> Model<E> {
|
||||
|
||||
fn new(app: &Application) -> Self {
|
||||
let app = app.clone_ref();
|
||||
let logger = Logger::new("SelectionContainer");
|
||||
@ -157,12 +156,11 @@ impl<E:Entry> Model<E> {
|
||||
if entry_count == 0 {
|
||||
0..0
|
||||
} else {
|
||||
let entry_at_y_saturating = |y:f32| {
|
||||
match entry::List::<E>::entry_at_y_position(y,entry_count) {
|
||||
let entry_at_y_saturating =
|
||||
|y: f32| match entry::List::<E>::entry_at_y_position(y, entry_count) {
|
||||
entry::list::IdAtYPosition::AboveFirst => 0,
|
||||
entry::list::IdAtYPosition::UnderLast => entry_count - 1,
|
||||
entry::list::IdAtYPosition::Entry(id) => id,
|
||||
}
|
||||
};
|
||||
let first = entry_at_y_saturating(*position_y);
|
||||
let last = entry_at_y_saturating(position_y - size.y) + 1;
|
||||
@ -172,18 +170,25 @@ impl<E:Entry> Model<E> {
|
||||
|
||||
/// Check if the `point` is inside component assuming that it have given `size`.
|
||||
fn is_inside(&self, point: Vector2<f32>, size: Vector2<f32>) -> bool {
|
||||
let pos_obj_space = self.app.display.scene().screen_to_object_space(&self.background,point);
|
||||
let pos_obj_space =
|
||||
self.app.display.scene().screen_to_object_space(&self.background, point);
|
||||
let x_range = (-size.x / 2.0)..=(size.x / 2.0);
|
||||
let y_range = (-size.y / 2.0)..=(size.y / 2.0);
|
||||
x_range.contains(&pos_obj_space.x) && y_range.contains(&pos_obj_space.y)
|
||||
}
|
||||
|
||||
fn selected_entry_after_jump
|
||||
(&self, current_entry:Option<entry::Id>, jump:isize) -> Option<entry::Id> {
|
||||
fn selected_entry_after_jump(
|
||||
&self,
|
||||
current_entry: Option<entry::Id>,
|
||||
jump: isize,
|
||||
) -> Option<entry::Id> {
|
||||
if jump < 0 {
|
||||
let current_entry = current_entry?;
|
||||
if current_entry == 0 { None }
|
||||
else { Some(current_entry.saturating_sub(-jump as usize)) }
|
||||
if current_entry == 0 {
|
||||
None
|
||||
} else {
|
||||
Some(current_entry.saturating_sub(-jump as usize))
|
||||
}
|
||||
} else {
|
||||
let max_entry = self.entries.entry_count().checked_sub(1)?;
|
||||
Some(current_entry.map_or(0, |id| id + (jump as usize)).min(max_entry))
|
||||
@ -251,11 +256,14 @@ pub struct ListView<E:Entry> {
|
||||
|
||||
impl<E: Entry> Deref for ListView<E> {
|
||||
type Target = Frp<E>;
|
||||
fn deref(&self) -> &Self::Target { &self.frp }
|
||||
fn deref(&self) -> &Self::Target {
|
||||
&self.frp
|
||||
}
|
||||
}
|
||||
|
||||
impl<E: Entry> ListView<E>
|
||||
where E::Model : Default {
|
||||
where E::Model: Default
|
||||
{
|
||||
/// Constructor.
|
||||
pub fn new(app: &Application) -> Self {
|
||||
let frp = Frp::new();
|
||||
@ -430,26 +438,40 @@ where E::Model : Default {
|
||||
}
|
||||
|
||||
impl<E: Entry> display::Object for ListView<E> {
|
||||
fn display_object(&self) -> &display::object::Instance { &self.model.display_object }
|
||||
fn display_object(&self) -> &display::object::Instance {
|
||||
&self.model.display_object
|
||||
}
|
||||
}
|
||||
|
||||
impl<E: Entry> application::command::FrpNetworkProvider for ListView<E> {
|
||||
fn network(&self) -> &frp::Network { &self.frp.network }
|
||||
fn network(&self) -> &frp::Network {
|
||||
&self.frp.network
|
||||
}
|
||||
}
|
||||
|
||||
impl<E: Entry> application::View for ListView<E> {
|
||||
fn label() -> &'static str { "ListView" }
|
||||
fn new(app:&Application) -> Self { ListView::new(app) }
|
||||
fn app(&self) -> &Application { &self.model.app }
|
||||
fn label() -> &'static str {
|
||||
"ListView"
|
||||
}
|
||||
fn new(app: &Application) -> Self {
|
||||
ListView::new(app)
|
||||
}
|
||||
fn app(&self) -> &Application {
|
||||
&self.model.app
|
||||
}
|
||||
fn default_shortcuts() -> Vec<shortcut::Shortcut> {
|
||||
use shortcut::ActionType::*;
|
||||
(&[ (PressAndRepeat , "up" , "move_selection_up")
|
||||
, (PressAndRepeat , "down" , "move_selection_down")
|
||||
, (Press , "page-up" , "move_selection_page_up")
|
||||
, (Press , "page-down" , "move_selection_page_down")
|
||||
, (Press , "home" , "move_selection_to_first")
|
||||
, (Press , "end" , "move_selection_to_last")
|
||||
, (Press , "enter" , "chose_selected_entry")
|
||||
]).iter().map(|(a,b,c)|Self::self_shortcut(*a,*b,*c)).collect()
|
||||
(&[
|
||||
(PressAndRepeat, "up", "move_selection_up"),
|
||||
(PressAndRepeat, "down", "move_selection_down"),
|
||||
(Press, "page-up", "move_selection_page_up"),
|
||||
(Press, "page-down", "move_selection_page_down"),
|
||||
(Press, "home", "move_selection_to_first"),
|
||||
(Press, "end", "move_selection_to_last"),
|
||||
(Press, "enter", "chose_selected_entry"),
|
||||
])
|
||||
.iter()
|
||||
.map(|(a, b, c)| Self::self_shortcut(*a, *b, *c))
|
||||
.collect()
|
||||
}
|
||||
}
|
||||
|
@ -118,7 +118,9 @@ impl Entry for Label {
|
||||
}
|
||||
|
||||
impl display::Object for Label {
|
||||
fn display_object(&self) -> &display::object::Instance { &self.display_object }
|
||||
fn display_object(&self) -> &display::object::Instance {
|
||||
&self.display_object
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -147,7 +149,8 @@ impl Entry for GlyphHighlightedLabel {
|
||||
fn new(app: &Application) -> Self {
|
||||
let inner = Label::new(app);
|
||||
let network = &inner.network;
|
||||
let highlight_color = inner.style_watch.get_color(theme::widget::list_view::text::highlight);
|
||||
let highlight_color =
|
||||
inner.style_watch.get_color(theme::widget::list_view::text::highlight);
|
||||
let label = &inner.label;
|
||||
|
||||
frp::extend! { network
|
||||
@ -173,7 +176,9 @@ impl Entry for GlyphHighlightedLabel {
|
||||
}
|
||||
|
||||
impl display::Object for GlyphHighlightedLabel {
|
||||
fn display_object(&self) -> &display::object::Instance { self.inner.display_object() }
|
||||
fn display_object(&self) -> &display::object::Instance {
|
||||
self.inner.display_object()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -206,20 +211,34 @@ pub trait ModelProvider<E> : Debug {
|
||||
#[derive(Debug, Shrinkwrap)]
|
||||
pub struct AnyModelProvider<E>(Rc<dyn ModelProvider<E>>);
|
||||
|
||||
impl<E> Clone for AnyModelProvider<E> { fn clone (&self) -> Self { Self(self.0.clone()) }}
|
||||
impl<E> CloneRef for AnyModelProvider<E> { fn clone_ref(&self) -> Self { Self(self.0.clone_ref()) }}
|
||||
impl<E> Clone for AnyModelProvider<E> {
|
||||
fn clone(&self) -> Self {
|
||||
Self(self.0.clone())
|
||||
}
|
||||
}
|
||||
impl<E> CloneRef for AnyModelProvider<E> {
|
||||
fn clone_ref(&self) -> Self {
|
||||
Self(self.0.clone_ref())
|
||||
}
|
||||
}
|
||||
|
||||
impl<E> AnyModelProvider<E> {
|
||||
/// Create from typed provider.
|
||||
pub fn new<T:ModelProvider<E>+'static>(provider:T) -> Self { Self(Rc::new(provider)) }
|
||||
pub fn new<T: ModelProvider<E> + 'static>(provider: T) -> Self {
|
||||
Self(Rc::new(provider))
|
||||
}
|
||||
}
|
||||
|
||||
impl<E, T: ModelProvider<E> + 'static> From<Rc<T>> for AnyModelProvider<E> {
|
||||
fn from(provider:Rc<T>) -> Self { Self(provider) }
|
||||
fn from(provider: Rc<T>) -> Self {
|
||||
Self(provider)
|
||||
}
|
||||
}
|
||||
|
||||
impl<E> Default for AnyModelProvider<E> {
|
||||
fn default() -> Self { Self::new(EmptyProvider) }
|
||||
fn default() -> Self {
|
||||
Self::new(EmptyProvider)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -232,16 +251,23 @@ impl<E> Default for AnyModelProvider<E> {
|
||||
pub struct EmptyProvider;
|
||||
|
||||
impl<E> ModelProvider<E> for EmptyProvider {
|
||||
fn entry_count(&self) -> usize { 0 }
|
||||
fn get (&self, _:usize) -> Option<E::Model> where E : Entry { None }
|
||||
fn entry_count(&self) -> usize {
|
||||
0
|
||||
}
|
||||
fn get(&self, _: usize) -> Option<E::Model>
|
||||
where E: Entry {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// === ModelProvider for Vectors ===
|
||||
|
||||
impl<E, T> ModelProvider<E> for Vec<T>
|
||||
where E : Entry,
|
||||
T : Debug + Clone + Into<E::Model> {
|
||||
where
|
||||
E: Entry,
|
||||
T: Debug + Clone + Into<E::Model>,
|
||||
{
|
||||
fn entry_count(&self) -> usize {
|
||||
self.len()
|
||||
}
|
||||
@ -277,7 +303,6 @@ impl<E:Debug> ModelProvider<E> for SingleMaskedProvider<E> {
|
||||
}
|
||||
|
||||
impl<E> SingleMaskedProvider<E> {
|
||||
|
||||
/// Return the index to the unmasked underlying data. Will only be valid to use after
|
||||
/// calling `clear_mask`.
|
||||
///
|
||||
|
@ -37,14 +37,19 @@ pub struct DisplayedEntry<E:CloneRef> {
|
||||
#[allow(missing_docs)]
|
||||
#[derive(Copy, Clone, Debug, Eq, Hash, PartialEq)]
|
||||
pub enum IdAtYPosition {
|
||||
AboveFirst, UnderLast, Entry(entry::Id)
|
||||
AboveFirst,
|
||||
UnderLast,
|
||||
Entry(entry::Id),
|
||||
}
|
||||
|
||||
impl IdAtYPosition {
|
||||
/// Returns id of entry if present.
|
||||
pub fn entry(&self) -> Option<entry::Id> {
|
||||
if let Self::Entry(id) = self { Some(*id) }
|
||||
else { None }
|
||||
if let Self::Entry(id) = self {
|
||||
Some(*id)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -63,7 +68,8 @@ pub struct List<E:CloneRef> {
|
||||
}
|
||||
|
||||
impl<E: Entry> List<E>
|
||||
where E::Model : Default {
|
||||
where E::Model: Default
|
||||
{
|
||||
/// Entry List View constructor.
|
||||
pub fn new(parent: impl AnyLogger, app: &Application) -> Self {
|
||||
let app = app.clone_ref();
|
||||
@ -87,7 +93,9 @@ where E::Model : Default {
|
||||
}
|
||||
|
||||
/// Y position of entry with given id, relative to Entry List position.
|
||||
pub fn position_y_of_entry(id:entry::Id) -> f32 { id as f32 * -entry::HEIGHT }
|
||||
pub fn position_y_of_entry(id: entry::Id) -> f32 {
|
||||
id as f32 * -entry::HEIGHT
|
||||
}
|
||||
|
||||
/// Y range of entry with given id, relative to Entry List position.
|
||||
pub fn y_range_of_entry(id: entry::Id) -> Range<f32> {
|
||||
@ -110,9 +118,13 @@ where E::Model : Default {
|
||||
pub fn entry_at_y_position(y: f32, entry_count: usize) -> IdAtYPosition {
|
||||
use IdAtYPosition::*;
|
||||
let all_entries_start = Self::y_range_of_all_entries(entry_count).start;
|
||||
if y > entry::HEIGHT/2.0 { AboveFirst }
|
||||
else if y < all_entries_start { UnderLast }
|
||||
else { Entry((-y/entry::HEIGHT + 0.5) as entry::Id) }
|
||||
if y > entry::HEIGHT / 2.0 {
|
||||
AboveFirst
|
||||
} else if y < all_entries_start {
|
||||
UnderLast
|
||||
} else {
|
||||
Entry((-y / entry::HEIGHT + 0.5) as entry::Id)
|
||||
}
|
||||
}
|
||||
|
||||
/// Update displayed entries to show the given range.
|
||||
@ -121,7 +133,8 @@ where E::Model : Default {
|
||||
if range != self.entries_range.get() {
|
||||
debug!(self.logger, "Update entries for {range:?}");
|
||||
let provider = self.provider.get();
|
||||
let current_entries:HashSet<entry::Id> = with(self.entries.borrow_mut(), |mut entries| {
|
||||
let current_entries: HashSet<entry::Id> =
|
||||
with(self.entries.borrow_mut(), |mut entries| {
|
||||
entries.resize_with(range.len(), || self.create_new_entry());
|
||||
entries.iter().filter_map(|entry| entry.id.get()).collect()
|
||||
});
|
||||
@ -130,7 +143,8 @@ where E::Model : Default {
|
||||
// methods.
|
||||
let models = missing.map(|id| (id, provider.get(id)));
|
||||
with(self.entries.borrow(), |entries| {
|
||||
let is_outdated = |e:&DisplayedEntry<E>| e.id.get().map_or(true, |i| !range.contains(&i));
|
||||
let is_outdated =
|
||||
|e: &DisplayedEntry<E>| e.id.get().map_or(true, |i| !range.contains(&i));
|
||||
let outdated = entries.iter().filter(|e| is_outdated(e));
|
||||
for (entry, (id, model)) in outdated.zip(models) {
|
||||
Self::update_entry(&self.logger, entry, id, &model);
|
||||
@ -141,14 +155,20 @@ where E::Model : Default {
|
||||
}
|
||||
|
||||
/// Update displayed entries, giving new provider.
|
||||
pub fn update_entries_new_provider
|
||||
(&self, provider:impl Into<entry::AnyModelProvider<E>> + 'static, mut range:Range<entry::Id>) {
|
||||
pub fn update_entries_new_provider(
|
||||
&self,
|
||||
provider: impl Into<entry::AnyModelProvider<E>> + 'static,
|
||||
mut range: Range<entry::Id>,
|
||||
) {
|
||||
const MAX_SAFE_ENTRIES_COUNT: usize = 1000;
|
||||
let provider = provider.into();
|
||||
if provider.entry_count() > MAX_SAFE_ENTRIES_COUNT {
|
||||
error!(self.logger, "ListView entry count exceed {MAX_SAFE_ENTRIES_COUNT} - so big \
|
||||
error!(
|
||||
self.logger,
|
||||
"ListView entry count exceed {MAX_SAFE_ENTRIES_COUNT} - so big \
|
||||
number of entries can cause visual glitches, e.g. https://github.com/enso-org/ide/\
|
||||
issues/757 or https://github.com/enso-org/ide/issues/758");
|
||||
issues/757 or https://github.com/enso-org/ide/issues/758"
|
||||
);
|
||||
}
|
||||
range.end = range.end.min(provider.entry_count());
|
||||
let models = range.clone().map(|id| (id, provider.get(id)));
|
||||
@ -168,8 +188,11 @@ where E::Model : Default {
|
||||
entry.entry.set_label_layer(&layer);
|
||||
}
|
||||
} else {
|
||||
error!(self.logger, "Cannot set layer {label_layer:?} for labels: the layer does not \
|
||||
exist in the scene");
|
||||
error!(
|
||||
self.logger,
|
||||
"Cannot set layer {label_layer:?} for labels: the layer does not \
|
||||
exist in the scene"
|
||||
);
|
||||
}
|
||||
self.label_layer.set(label_layer);
|
||||
}
|
||||
@ -177,23 +200,31 @@ where E::Model : Default {
|
||||
fn create_new_entry(&self) -> DisplayedEntry<E> {
|
||||
let layers = &self.app.display.scene().layers;
|
||||
let layer = layers.get_sublayer(self.label_layer.get()).unwrap_or_else(|| {
|
||||
error!(self.logger, "Cannot set layer {self.label_layer:?} for labels: the layer does \
|
||||
not exist in the scene");
|
||||
error!(
|
||||
self.logger,
|
||||
"Cannot set layer {self.label_layer:?} for labels: the layer does \
|
||||
not exist in the scene"
|
||||
);
|
||||
layers.main.clone_ref()
|
||||
});
|
||||
let entry = DisplayedEntry {
|
||||
id : default(),
|
||||
entry : E::new(&self.app)
|
||||
};
|
||||
let entry = DisplayedEntry { id: default(), entry: E::new(&self.app) };
|
||||
entry.entry.set_label_layer(&layer);
|
||||
entry.entry.set_position_x(entry::PADDING);
|
||||
self.add_child(&entry.entry);
|
||||
entry
|
||||
}
|
||||
|
||||
fn update_entry(logger:&Logger, entry:&DisplayedEntry<E>, id:entry::Id, model:&Option<E::Model>) {
|
||||
debug!(logger, "Setting new model {model:?} for entry {id}; \
|
||||
old entry: {entry.id.get():?}.");
|
||||
fn update_entry(
|
||||
logger: &Logger,
|
||||
entry: &DisplayedEntry<E>,
|
||||
id: entry::Id,
|
||||
model: &Option<E::Model>,
|
||||
) {
|
||||
debug!(
|
||||
logger,
|
||||
"Setting new model {model:?} for entry {id}; \
|
||||
old entry: {entry.id.get():?}."
|
||||
);
|
||||
entry.id.set(Some(id));
|
||||
match model {
|
||||
Some(model) => entry.entry.update(model),
|
||||
@ -207,5 +238,7 @@ where E::Model : Default {
|
||||
}
|
||||
|
||||
impl<E: CloneRef> display::Object for List<E> {
|
||||
fn display_object(&self) -> &display::object::Instance { &self.display_object }
|
||||
fn display_object(&self) -> &display::object::Instance {
|
||||
&self.display_object
|
||||
}
|
||||
}
|
||||
|
@ -6,11 +6,11 @@ use crate::scrollbar;
|
||||
use crate::scrollbar::Scrollbar;
|
||||
|
||||
use enso_frp as frp;
|
||||
use ensogl_core::application::Application;
|
||||
use ensogl_core::control::callback;
|
||||
use ensogl_core::control::io::mouse;
|
||||
use ensogl_core::display;
|
||||
use ensogl_core::display::object::ObjectOps;
|
||||
use ensogl_core::application::Application;
|
||||
use ensogl_core::control::io::mouse;
|
||||
use ensogl_core::control::callback;
|
||||
|
||||
|
||||
|
||||
|
@ -3,18 +3,18 @@
|
||||
use crate::prelude::*;
|
||||
|
||||
use enso_frp as frp;
|
||||
use ensogl_core::Animation;
|
||||
use ensogl_core::application::Application;
|
||||
use ensogl_core::application;
|
||||
use ensogl_core::application::Application;
|
||||
use ensogl_core::data::color;
|
||||
use ensogl_core::display::shape::*;
|
||||
use ensogl_core::display::shape::StyleWatchFrp;
|
||||
use ensogl_core::display::shape::*;
|
||||
use ensogl_core::Animation;
|
||||
use ensogl_theme as theme;
|
||||
|
||||
use crate::component;
|
||||
use crate::selector::Bounds;
|
||||
use crate::selector::model::Model;
|
||||
use crate::selector;
|
||||
use crate::selector::model::Model;
|
||||
use crate::selector::Bounds;
|
||||
use ensogl_core::animation::delayed::DelayedAnimation;
|
||||
|
||||
|
||||
@ -71,9 +71,13 @@ ensogl_core::define_endpoints! {
|
||||
}
|
||||
|
||||
impl Frp {
|
||||
fn compute_target_alpha
|
||||
(&recently_active:&bool, &dragging:&bool, &cursor_distance:&f32, &thumb_size:&f32, &max:&f32)
|
||||
-> f32 {
|
||||
fn compute_target_alpha(
|
||||
&recently_active: &bool,
|
||||
&dragging: &bool,
|
||||
&cursor_distance: &f32,
|
||||
&thumb_size: &f32,
|
||||
&max: &f32,
|
||||
) -> f32 {
|
||||
let thumb_fills_bar = thumb_size >= max;
|
||||
if thumb_fills_bar {
|
||||
0.0
|
||||
@ -277,7 +281,13 @@ impl component::Frp<Model> for Frp {
|
||||
pub type Scrollbar = crate::component::Component<Model, Frp>;
|
||||
|
||||
impl application::View for Scrollbar {
|
||||
fn label() -> &'static str { "Scrollbar" }
|
||||
fn new(app:&Application) -> Self { Scrollbar::new(app) }
|
||||
fn app(&self) -> &Application { &self.app }
|
||||
fn label() -> &'static str {
|
||||
"Scrollbar"
|
||||
}
|
||||
fn new(app: &Application) -> Self {
|
||||
Scrollbar::new(app)
|
||||
}
|
||||
fn app(&self) -> &Application {
|
||||
&self.app
|
||||
}
|
||||
}
|
||||
|
@ -40,9 +40,15 @@ use model::*;
|
||||
pub type NumberPicker = crate::component::Component<Model, number::Frp>;
|
||||
|
||||
impl application::View for NumberPicker {
|
||||
fn label() -> &'static str { "NumberPicker" }
|
||||
fn new(app:&Application) -> Self { NumberPicker::new(app) }
|
||||
fn app(&self) -> &Application { &self.app }
|
||||
fn label() -> &'static str {
|
||||
"NumberPicker"
|
||||
}
|
||||
fn new(app: &Application) -> Self {
|
||||
NumberPicker::new(app)
|
||||
}
|
||||
fn app(&self) -> &Application {
|
||||
&self.app
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -63,7 +69,13 @@ impl application::View for NumberPicker {
|
||||
pub type NumberRangePicker = crate::component::Component<Model, range::Frp>;
|
||||
|
||||
impl application::View for NumberRangePicker {
|
||||
fn label() -> &'static str { "RangePicker" }
|
||||
fn new(app:&Application) -> Self { NumberRangePicker::new(app) }
|
||||
fn app(&self) -> &Application { &self.app }
|
||||
fn label() -> &'static str {
|
||||
"RangePicker"
|
||||
}
|
||||
fn new(app: &Application) -> Self {
|
||||
NumberRangePicker::new(app)
|
||||
}
|
||||
fn app(&self) -> &Application {
|
||||
&self.app
|
||||
}
|
||||
}
|
||||
|
@ -66,7 +66,9 @@ impl From<(f32,f32)> for Bounds {
|
||||
/// ````
|
||||
pub fn normalise_value((value, bounds): &(f32, Bounds)) -> f32 {
|
||||
let width = bounds.width();
|
||||
if width == 0.0 { return 0.0 }
|
||||
if width == 0.0 {
|
||||
return 0.0;
|
||||
}
|
||||
(value - bounds.start) / width
|
||||
}
|
||||
|
||||
@ -86,7 +88,9 @@ pub fn absolute_value((bounds,normalised_value):&(Bounds,f32)) -> f32 {
|
||||
/// For use in FRP `map` method, thus taking references.
|
||||
#[allow(clippy::trivially_copy_pass_by_ref)]
|
||||
pub fn position_to_normalised_value(pos: &Vector2, width: &f32) -> f32 {
|
||||
if *width == 0.0 { return 0.0 }
|
||||
if *width == 0.0 {
|
||||
return 0.0;
|
||||
}
|
||||
((pos.x / (width / 2.0)) + 1.0) / 2.0
|
||||
}
|
||||
|
||||
@ -303,8 +307,11 @@ mod tests {
|
||||
fn test_bounds_in_bounds() {
|
||||
let test = |start1, end1, start2, end2, expected| {
|
||||
let result = bounds_in_bounds(Bounds::new(start1, start2), Bounds::new(start2, end2));
|
||||
assert_eq!(result,expected,
|
||||
"Testing whether ]{},{}[ in ]{},{}[", start1,end1,start2,end2);
|
||||
assert_eq!(
|
||||
result, expected,
|
||||
"Testing whether ]{},{}[ in ]{},{}[",
|
||||
start1, end1, start2, end2
|
||||
);
|
||||
};
|
||||
|
||||
test(0.0, 1.0, 0.0, 1.0, true);
|
||||
|
@ -7,11 +7,11 @@ use crate::prelude::*;
|
||||
use crate::component;
|
||||
|
||||
use enso_frp as frp;
|
||||
use ensogl_core::application::Application;
|
||||
use ensogl_core::application;
|
||||
use ensogl_core::application::Application;
|
||||
use ensogl_core::display;
|
||||
use ensogl_core::display::object::ObjectOps;
|
||||
use ensogl_core::display::shape::StyleWatchFrp;
|
||||
use ensogl_core::display;
|
||||
use ensogl_text as text;
|
||||
|
||||
|
||||
@ -94,14 +94,22 @@ impl component::Frp<Model> for Frp {
|
||||
}
|
||||
|
||||
impl display::Object for Model {
|
||||
fn display_object(&self) -> &display::object::Instance { self.root.display_object() }
|
||||
fn display_object(&self) -> &display::object::Instance {
|
||||
self.root.display_object()
|
||||
}
|
||||
}
|
||||
|
||||
/// Decimal aligned text label that shows the text representation of a floating point number.
|
||||
pub type FloatLabel = component::Component<Model, Frp>;
|
||||
|
||||
impl application::View for FloatLabel {
|
||||
fn label() -> &'static str { "FloatLabel" }
|
||||
fn new(app:&Application) -> Self { FloatLabel::new(app) }
|
||||
fn app(&self) -> &Application { &self.app }
|
||||
fn label() -> &'static str {
|
||||
"FloatLabel"
|
||||
}
|
||||
fn new(app: &Application) -> Self {
|
||||
FloatLabel::new(app)
|
||||
}
|
||||
fn app(&self) -> &Application {
|
||||
&self.app
|
||||
}
|
||||
}
|
||||
|
@ -12,8 +12,8 @@ use ensogl_theme as theme;
|
||||
use crate::shadow;
|
||||
|
||||
use super::model::Model;
|
||||
use super::shape::shape_is_dragged;
|
||||
use super::shape::relative_shape_down_position;
|
||||
use super::shape::shape_is_dragged;
|
||||
|
||||
|
||||
|
||||
@ -67,29 +67,31 @@ pub struct Frp {
|
||||
}
|
||||
|
||||
impl Frp {
|
||||
pub fn new
|
||||
(model:&Model, style:&StyleWatchFrp, network:&Network, size:frp::Stream<Vector2>, mouse:&Mouse)
|
||||
-> Frp {
|
||||
pub fn new(
|
||||
model: &Model,
|
||||
style: &StyleWatchFrp,
|
||||
network: &Network,
|
||||
size: frp::Stream<Vector2>,
|
||||
mouse: &Mouse,
|
||||
) -> Frp {
|
||||
let shadow = shadow::frp_from_style(style, theme::shadow);
|
||||
let text_size = style.get_number(theme::text::size);
|
||||
|
||||
let is_dragging_left_overflow = shape_is_dragged(
|
||||
network,&model.left_overflow.events,mouse);
|
||||
let is_dragging_right_overflow = shape_is_dragged(
|
||||
network,&model.right_overflow.events,mouse);
|
||||
let is_dragging_track = shape_is_dragged(
|
||||
network,&model.track.events, mouse);
|
||||
let is_dragging_background = shape_is_dragged(
|
||||
network,&model.background.events,mouse);
|
||||
let is_dragging_left_handle = shape_is_dragged(
|
||||
network,&model.track_handle_left.events,mouse);
|
||||
let is_dragging_right_handle = shape_is_dragged(
|
||||
network,&model.track_handle_right.events,mouse);
|
||||
let is_dragging_left_overflow =
|
||||
shape_is_dragged(network, &model.left_overflow.events, mouse);
|
||||
let is_dragging_right_overflow =
|
||||
shape_is_dragged(network, &model.right_overflow.events, mouse);
|
||||
let is_dragging_track = shape_is_dragged(network, &model.track.events, mouse);
|
||||
let is_dragging_background = shape_is_dragged(network, &model.background.events, mouse);
|
||||
let is_dragging_left_handle =
|
||||
shape_is_dragged(network, &model.track_handle_left.events, mouse);
|
||||
let is_dragging_right_handle =
|
||||
shape_is_dragged(network, &model.track_handle_right.events, mouse);
|
||||
|
||||
let background_click = relative_shape_down_position(
|
||||
network,model.app.display.scene(),&model.background);
|
||||
let track_click = relative_shape_down_position(
|
||||
network,model.app.display.scene(),&model.track);
|
||||
let background_click =
|
||||
relative_shape_down_position(network, model.app.display.scene(), &model.background);
|
||||
let track_click =
|
||||
relative_shape_down_position(network, model.app.display.scene(), &model.track);
|
||||
|
||||
// Initialisation of components. Required for correct layout on startup.
|
||||
model.label_right.set_position_y(text_size.value() / 2.0);
|
||||
@ -142,8 +144,18 @@ impl Frp {
|
||||
|
||||
init_shadow_padding.emit(());
|
||||
|
||||
Frp {track_max_width,is_dragging_left_overflow,is_dragging_right_overflow,
|
||||
is_dragging_track,is_dragging_background,is_dragging_left_handle,
|
||||
is_dragging_right_handle,is_dragging_any,background_click,track_click,track_hover}
|
||||
Frp {
|
||||
track_max_width,
|
||||
is_dragging_left_overflow,
|
||||
is_dragging_right_overflow,
|
||||
is_dragging_track,
|
||||
is_dragging_background,
|
||||
is_dragging_left_handle,
|
||||
is_dragging_right_handle,
|
||||
is_dragging_any,
|
||||
background_click,
|
||||
track_click,
|
||||
track_hover,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -11,9 +11,9 @@ use ensogl_text as text;
|
||||
|
||||
use crate::component;
|
||||
|
||||
use super::Bounds;
|
||||
use super::shape::*;
|
||||
use super::decimal_aligned::FloatLabel;
|
||||
use super::shape::*;
|
||||
use super::Bounds;
|
||||
|
||||
|
||||
|
||||
@ -120,10 +120,26 @@ impl component::Model for Model {
|
||||
caption_center.remove_from_scene_layer(&scene.layers.main);
|
||||
caption_center.add_to_scene_layer(&scene.layers.label);
|
||||
|
||||
Self{background,track,track_handle_left,track_handle_right,left_overflow,right_overflow,
|
||||
label,label_left,label_right,caption_left,caption_center,root,background_color,
|
||||
track_color,background_left_corner_roundness,background_right_corner_roundness,padding,
|
||||
app}
|
||||
Self {
|
||||
background,
|
||||
track,
|
||||
track_handle_left,
|
||||
track_handle_right,
|
||||
left_overflow,
|
||||
right_overflow,
|
||||
label,
|
||||
label_left,
|
||||
label_right,
|
||||
caption_left,
|
||||
caption_center,
|
||||
root,
|
||||
background_color,
|
||||
track_color,
|
||||
background_left_corner_roundness,
|
||||
background_right_corner_roundness,
|
||||
padding,
|
||||
app,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -274,8 +290,10 @@ impl Model {
|
||||
if value {
|
||||
self.background.show_shadow.set(1.0);
|
||||
self.background.color.set(self.background_color.as_ref().clone().into_inner().into());
|
||||
let left_corner_roundness = if self.background_left_corner_roundness.get() { 1.0 } else { 0.0 };
|
||||
let right_corner_roundness = if self.background_right_corner_roundness.get() { 1.0 } else { 0.0 };
|
||||
let left_corner_roundness =
|
||||
if self.background_left_corner_roundness.get() { 1.0 } else { 0.0 };
|
||||
let right_corner_roundness =
|
||||
if self.background_right_corner_roundness.get() { 1.0 } else { 0.0 };
|
||||
self.track.corner_right.set(right_corner_roundness);
|
||||
self.track.corner_left.set(left_corner_roundness);
|
||||
} else {
|
||||
@ -288,5 +306,7 @@ impl Model {
|
||||
}
|
||||
|
||||
impl display::Object for Model {
|
||||
fn display_object(&self) -> &display::object::Instance { &self.root }
|
||||
fn display_object(&self) -> &display::object::Instance {
|
||||
&self.root
|
||||
}
|
||||
}
|
||||
|
@ -2,21 +2,21 @@
|
||||
use crate::prelude::*;
|
||||
|
||||
use enso_frp as frp;
|
||||
use ensogl_core::data::color;
|
||||
use ensogl_core::application::Application;
|
||||
use ensogl_core::display::shape::*;
|
||||
use ensogl_core::data::color;
|
||||
use ensogl_core::display::shape::StyleWatchFrp;
|
||||
use ensogl_core::display::shape::*;
|
||||
use ensogl_theme as theme;
|
||||
|
||||
use crate::component;
|
||||
use crate::selector::shape::*;
|
||||
use super::Bounds;
|
||||
use super::bounds::absolute_value;
|
||||
use super::bounds::clamp_with_overflow;
|
||||
use super::bounds::normalise_value;
|
||||
use super::bounds::position_to_normalised_value;
|
||||
use super::shape::relative_shape_down_position;
|
||||
use super::model::Model;
|
||||
use super::shape::relative_shape_down_position;
|
||||
use super::Bounds;
|
||||
use crate::component;
|
||||
use crate::selector::shape::*;
|
||||
|
||||
|
||||
|
||||
@ -56,10 +56,10 @@ impl component::Frp<Model> for Frp {
|
||||
let track_shape_system = scene.shapes.shape_system(PhantomData::<track::Shape>);
|
||||
track_shape_system.shape_system.set_pointer_events(false);
|
||||
|
||||
let background_click = relative_shape_down_position(
|
||||
network,model.app.display.scene(),&model.background);
|
||||
let track_click = relative_shape_down_position(
|
||||
network,model.app.display.scene(),&model.track);
|
||||
let background_click =
|
||||
relative_shape_down_position(network, model.app.display.scene(), &model.background);
|
||||
let track_click =
|
||||
relative_shape_down_position(network, model.app.display.scene(), &model.track);
|
||||
|
||||
let style_track_color = style.get_color(theme::component::slider::track::color);
|
||||
|
||||
|
@ -2,19 +2,19 @@
|
||||
use crate::prelude::*;
|
||||
|
||||
use enso_frp as frp;
|
||||
use ensogl_core::data::color;
|
||||
use ensogl_core::application::Application;
|
||||
use ensogl_core::display::shape::*;
|
||||
use ensogl_core::data::color;
|
||||
use ensogl_core::display::shape::StyleWatchFrp;
|
||||
use ensogl_core::display::shape::*;
|
||||
use ensogl_theme as theme;
|
||||
|
||||
use crate::component;
|
||||
|
||||
use super::Model;
|
||||
use super::Bounds;
|
||||
use super::bounds::absolute_value;
|
||||
use super::bounds::normalise_value;
|
||||
use super::bounds::should_clamp_with_overflow;
|
||||
use super::Bounds;
|
||||
use super::Model;
|
||||
|
||||
|
||||
|
||||
@ -131,6 +131,5 @@ impl component::Frp<Model> for Frp {
|
||||
frp.set_left_corner_round(true);
|
||||
frp.set_right_corner_round(true);
|
||||
frp.set_track_color(style_track_color.value());
|
||||
|
||||
}
|
||||
}
|
||||
|
@ -26,8 +26,7 @@ struct Background {
|
||||
}
|
||||
|
||||
impl Background {
|
||||
fn new(corner_left:&Var<f32>, corner_right:&Var<f32>, style:&StyleWatch)
|
||||
-> Background {
|
||||
fn new(corner_left: &Var<f32>, corner_right: &Var<f32>, style: &StyleWatch) -> Background {
|
||||
let sprite_width: Var<Pixels> = "input_size.x".into();
|
||||
let sprite_height: Var<Pixels> = "input_size.y".into();
|
||||
|
||||
@ -36,7 +35,8 @@ impl Background {
|
||||
let corner_radius = &height / 2.0;
|
||||
let rect_left = Rect((&width / 2.0, &height)).corners_radius(&corner_radius * corner_left);
|
||||
let rect_left = rect_left.translate_x(-&width / 4.0);
|
||||
let rect_right = Rect((&width/2.0,&height)).corners_radius(&corner_radius*corner_right);
|
||||
let rect_right =
|
||||
Rect((&width / 2.0, &height)).corners_radius(&corner_radius * corner_right);
|
||||
let rect_right = rect_right.translate_x(&width / 4.0);
|
||||
let rect_center = Rect((&corner_radius * 2.0, &height));
|
||||
|
||||
@ -137,7 +137,7 @@ struct OverflowShape {
|
||||
#[allow(dead_code)]
|
||||
// This field is not used but should stay as part of the API for future use.
|
||||
pub height: Var<Pixels>,
|
||||
pub shape : AnyShape
|
||||
pub shape: AnyShape,
|
||||
}
|
||||
|
||||
impl OverflowShape {
|
||||
@ -207,8 +207,11 @@ use ensogl_core::display::Scene;
|
||||
/// Return whether a dragging action has been started from the shape passed to this function. A
|
||||
/// dragging action is started by a mouse down on the shape, followed by a movement of the mouse.
|
||||
/// Dragging is ended by a mouse up.
|
||||
pub fn shape_is_dragged
|
||||
(network:&Network, shape:&ShapeViewEvents, mouse:&Mouse) -> enso_frp::Stream<bool> {
|
||||
pub fn shape_is_dragged(
|
||||
network: &Network,
|
||||
shape: &ShapeViewEvents,
|
||||
mouse: &Mouse,
|
||||
) -> enso_frp::Stream<bool> {
|
||||
enso_frp::extend! { network
|
||||
mouse_up <- mouse.up.constant(());
|
||||
mouse_down <- mouse.down.constant(());
|
||||
@ -221,10 +224,10 @@ pub fn shape_is_dragged
|
||||
|
||||
/// Returns the position of a mouse down on a shape. The position is given in the shape's local
|
||||
/// coordinate system
|
||||
pub fn relative_shape_down_position<T:'static+display::Object+CloneRef>
|
||||
( network : &Network
|
||||
, scene : &Scene
|
||||
, shape : &ShapeView<T>
|
||||
pub fn relative_shape_down_position<T: 'static + display::Object + CloneRef>(
|
||||
network: &Network,
|
||||
scene: &Scene,
|
||||
shape: &ShapeView<T>,
|
||||
) -> enso_frp::Stream<Vector2> {
|
||||
let mouse = &scene.mouse.frp;
|
||||
enso_frp::extend! { network
|
||||
|
@ -2,10 +2,10 @@
|
||||
use crate::prelude::*;
|
||||
|
||||
use ensogl_core::data::color;
|
||||
use ensogl_core::display::DomSymbol;
|
||||
use ensogl_core::display::shape::*;
|
||||
use ensogl_core::display::shape::AnyShape;
|
||||
use ensogl_core::display::shape::*;
|
||||
use ensogl_core::display::style;
|
||||
use ensogl_core::display::DomSymbol;
|
||||
use ensogl_core::frp;
|
||||
use ensogl_core::system::web::StyleSetter;
|
||||
use ensogl_theme as theme;
|
||||
@ -20,7 +20,7 @@ pub struct Parameters {
|
||||
spread: f32,
|
||||
exponent: f32,
|
||||
offset_x: f32,
|
||||
offset_y : f32
|
||||
offset_y: f32,
|
||||
}
|
||||
|
||||
/// Loads shadow parameters from the given style, at the given path. The structure of the style
|
||||
@ -34,7 +34,7 @@ pub fn parameters_from_style_path(style:&StyleWatch,path:impl Into<style::Path>)
|
||||
spread: style.get_number(&path.sub("spread")),
|
||||
exponent: style.get_number(&path.sub("exponent")),
|
||||
offset_x: style.get_number(&path.sub("offset_x")),
|
||||
offset_y: style.get_number(&path.sub("offset_y"))
|
||||
offset_y: style.get_number(&path.sub("offset_y")),
|
||||
}
|
||||
}
|
||||
|
||||
@ -53,7 +53,11 @@ pub fn from_shape(base_shape:AnyShape, style:&StyleWatch) -> AnyShape {
|
||||
/// Return a shadow for the given shape. Exact appearance will depend on the theme parameters.
|
||||
/// The color will be multiplied with the given alpha value, which is useful for fade-in/out
|
||||
/// animations.
|
||||
pub fn from_shape_with_alpha(base_shape:AnyShape,alpha:&Var<f32>,style:&StyleWatch) -> AnyShape {
|
||||
pub fn from_shape_with_alpha(
|
||||
base_shape: AnyShape,
|
||||
alpha: &Var<f32>,
|
||||
style: &StyleWatch,
|
||||
) -> AnyShape {
|
||||
let parameters = parameters_from_style_path(style, theme::shadow);
|
||||
from_shape_with_parameters_and_alpha(base_shape, parameters, alpha)
|
||||
}
|
||||
@ -66,9 +70,11 @@ pub fn from_shape_with_parameters(base_shape:AnyShape,parameters:Parameters) ->
|
||||
|
||||
/// Return a shadow for the given shape, shadow parameters and alpha. As in `from_shape_with_alpha`,
|
||||
/// the shadow colors will be multiplied with the given alpha value.
|
||||
pub fn from_shape_with_parameters_and_alpha
|
||||
(base_shape:AnyShape,parameters:Parameters,alpha:&Var<f32>)
|
||||
-> AnyShape {
|
||||
pub fn from_shape_with_parameters_and_alpha(
|
||||
base_shape: AnyShape,
|
||||
parameters: Parameters,
|
||||
alpha: &Var<f32>,
|
||||
) -> AnyShape {
|
||||
let grow = Var::<f32>::from(parameters.size);
|
||||
let shadow = base_shape.grow(grow);
|
||||
let shadow = shadow.translate((parameters.offset_x.px(), parameters.offset_y.px()));
|
||||
@ -79,9 +85,14 @@ pub fn from_shape_with_parameters_and_alpha
|
||||
let fading_color = Var::<color::Rgba>::from(parameters.fading);
|
||||
let fading_color = fading_color.multiply_alpha(alpha);
|
||||
|
||||
let shadow_color = color::gradient::Linear::<Var<color::LinearRgba>>
|
||||
::new(fading_color.into_linear(),base_color.into_linear());
|
||||
let shadow_color = shadow_color.sdf_sampler().size(parameters.size).spread(parameters.spread)
|
||||
let shadow_color = color::gradient::Linear::<Var<color::LinearRgba>>::new(
|
||||
fading_color.into_linear(),
|
||||
base_color.into_linear(),
|
||||
);
|
||||
let shadow_color = shadow_color
|
||||
.sdf_sampler()
|
||||
.size(parameters.size)
|
||||
.spread(parameters.spread)
|
||||
.exponent(parameters.exponent);
|
||||
let shadow = shadow.fill(shadow_color);
|
||||
shadow.into()
|
||||
@ -109,7 +120,7 @@ pub struct ParametersFrp {
|
||||
pub spread: frp::Sampler<f32>,
|
||||
pub exponent: frp::Sampler<f32>,
|
||||
pub offset_x: frp::Sampler<f32>,
|
||||
pub offset_y : frp::Sampler<f32>
|
||||
pub offset_y: frp::Sampler<f32>,
|
||||
}
|
||||
|
||||
/// Return FRP endpoints for the parameters that define a shadow.
|
||||
@ -122,6 +133,6 @@ pub fn frp_from_style(style:&StyleWatchFrp,path:impl Into<style::Path>) -> Param
|
||||
spread: style.get_number(&path.sub("spread")),
|
||||
exponent: style.get_number(&path.sub("exponent")),
|
||||
offset_x: style.get_number(&path.sub("offset_x")),
|
||||
offset_y : style.get_number(&path.sub("offset_y"))
|
||||
offset_y: style.get_number(&path.sub("offset_y")),
|
||||
}
|
||||
}
|
||||
|
@ -13,7 +13,6 @@ use ensogl_core::display::shape::system::DynamicShapeInternals;
|
||||
|
||||
|
||||
|
||||
|
||||
// =================
|
||||
// === Colorable ===
|
||||
// =================
|
||||
|
@ -16,8 +16,8 @@ pub use loops::*;
|
||||
// === Utils ===
|
||||
// =============
|
||||
|
||||
use std::ops::Mul;
|
||||
use std::ops::Add;
|
||||
use std::ops::Mul;
|
||||
|
||||
/// A generic trait constraint for interpolable types.
|
||||
pub trait Interpolable<T: Copy> = Mul<f32, Output = T> + Add<T, Output = T> + Copy;
|
||||
|
@ -26,7 +26,8 @@ pub trait AnyFnEasing = 'static + Fn(f32) -> f32;
|
||||
pub trait CloneableFnEasing = 'static + Clone + Fn(f32) -> f32;
|
||||
|
||||
macro_rules! define_in_out_easing_fn {
|
||||
(fn $tname:ident $name:ident $lambda:expr) => { paste::item! {
|
||||
(fn $tname:ident $name:ident $lambda:expr) => {
|
||||
paste::item! {
|
||||
/// A $name-in function type.
|
||||
pub type [<$tname In>] = impl Clone + Fn(f32) -> f32;
|
||||
/// A $name-out function type.
|
||||
@ -49,7 +50,8 @@ macro_rules! define_in_out_easing_fn {
|
||||
// impl Default for [<$tname In>] { fn default() -> Self { [<$name _in>]() } }
|
||||
// impl Default for [<$tname Out>] { fn default() -> Self { [<$name _out>]() } }
|
||||
// impl Default for [<$tname InOut>] { fn default() -> Self { [<$name _in_out>]() } }
|
||||
}};
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
macro_rules! define_in_out_easing_fns {
|
||||
@ -87,10 +89,14 @@ define_in_out_easing_fns! {
|
||||
pub type Linear = impl Clone + Fn(f32) -> f32;
|
||||
|
||||
/// Linear transition.
|
||||
pub fn linear() -> Linear { |t| { t } }
|
||||
pub fn linear() -> Linear {
|
||||
|t| t
|
||||
}
|
||||
|
||||
/// A back-in transition with params.
|
||||
pub fn back_in_params(t:f32, overshoot:f32) -> f32 { t * t * ((overshoot + 1.0) * t - overshoot) }
|
||||
pub fn back_in_params(t: f32, overshoot: f32) -> f32 {
|
||||
t * t * ((overshoot + 1.0) * t - overshoot)
|
||||
}
|
||||
|
||||
/// A back-out transition with params.
|
||||
pub fn back_out_params(t: f32, overshoot: f32) -> f32 {
|
||||
@ -191,7 +197,11 @@ pub struct AnimatorData<T,F,OnStep,OnEnd> {
|
||||
}
|
||||
|
||||
impl<T: Value, F, OnStep, OnEnd> AnimatorData<T, F, OnStep, OnEnd>
|
||||
where F:AnyFnEasing, OnStep:Callback<T>, OnEnd:Callback<EndStatus> {
|
||||
where
|
||||
F: AnyFnEasing,
|
||||
OnStep: Callback<T>,
|
||||
OnEnd: Callback<EndStatus>,
|
||||
{
|
||||
fn new(start: T, end: T, tween_fn: F, callback: OnStep, on_end: OnEnd) -> Self {
|
||||
let duration = Cell::new(1000.0);
|
||||
let value = Cell::new(start);
|
||||
@ -216,7 +226,11 @@ where F:AnyFnEasing, OnStep:Callback<T>, OnEnd:Callback<EndStatus> {
|
||||
}
|
||||
|
||||
impl<T: Value, F, OnStep, OnEnd> Animator<T, F, OnStep, OnEnd>
|
||||
where F:AnyFnEasing, OnStep:Callback<T>, OnEnd:Callback<EndStatus> {
|
||||
where
|
||||
F: AnyFnEasing,
|
||||
OnStep: Callback<T>,
|
||||
OnEnd: Callback<EndStatus>,
|
||||
{
|
||||
/// Constructor.
|
||||
pub fn new_not_started(start: T, end: T, tween_fn: F, callback: OnStep, on_end: OnEnd) -> Self {
|
||||
let data = Rc::new(AnimatorData::new(start, end, tween_fn, callback, on_end));
|
||||
@ -306,7 +320,11 @@ where F:AnyFnEasing, OnStep:Callback<T>, OnEnd:Callback<EndStatus> {
|
||||
|
||||
#[allow(missing_docs)]
|
||||
impl<T: Value, F, OnStep, OnEnd> Animator<T, F, OnStep, OnEnd>
|
||||
where F:AnyFnEasing, OnStep:Callback<T>, OnEnd:Callback<EndStatus> {
|
||||
where
|
||||
F: AnyFnEasing,
|
||||
OnStep: Callback<T>,
|
||||
OnEnd: Callback<EndStatus>,
|
||||
{
|
||||
pub fn start_value(&self) -> T {
|
||||
self.data.start_value.get()
|
||||
}
|
||||
@ -388,8 +406,13 @@ pub type AnimationStep<T,F,OnStep,OnEnd> = animation::Loop<Step<T,F,OnStep,OnEnd
|
||||
/// Callback for an animation step.
|
||||
pub type Step<T, F, OnStep, OnEnd> = impl Fn(animation::TimeInfo);
|
||||
|
||||
fn step<T:Value,F,OnStep,OnEnd>(easing:&Animator<T,F,OnStep,OnEnd>) -> Step<T,F,OnStep,OnEnd>
|
||||
where F:AnyFnEasing, OnStep:Callback<T>, OnEnd:Callback<EndStatus> {
|
||||
fn step<T: Value, F, OnStep, OnEnd>(
|
||||
easing: &Animator<T, F, OnStep, OnEnd>,
|
||||
) -> Step<T, F, OnStep, OnEnd>
|
||||
where
|
||||
F: AnyFnEasing,
|
||||
OnStep: Callback<T>,
|
||||
OnEnd: Callback<EndStatus>, {
|
||||
let data = easing.data.clone_ref();
|
||||
let animation_loop = easing.animation_loop.downgrade();
|
||||
move |time: animation::TimeInfo| {
|
||||
|
@ -1,7 +1,7 @@
|
||||
//! FRP bindings to the animation engine.
|
||||
|
||||
pub mod hysteretic;
|
||||
pub mod delayed;
|
||||
pub mod hysteretic;
|
||||
|
||||
use crate::prelude::*;
|
||||
|
||||
@ -45,7 +45,8 @@ pub struct Animation<T:mix::Mixable+frp::Data> {
|
||||
|
||||
#[allow(missing_docs)]
|
||||
impl<T: mix::Mixable + frp::Data> Animation<T>
|
||||
where mix::Repr<T> : inertia::Value {
|
||||
where mix::Repr<T>: inertia::Value
|
||||
{
|
||||
/// Constructor. The initial value of the animation is set to `default`.
|
||||
pub fn new(network: &frp::Network) -> Self {
|
||||
frp::extend! { network
|
||||
@ -127,7 +128,8 @@ pub struct DEPRECATED_Animation<T:mix::Mixable> {
|
||||
|
||||
#[allow(missing_docs)]
|
||||
impl<T: mix::Mixable + frp::Data> DEPRECATED_Animation<T>
|
||||
where mix::Repr<T> : inertia::Value {
|
||||
where mix::Repr<T>: inertia::Value
|
||||
{
|
||||
/// Constructor.
|
||||
pub fn new(network: &frp::Network) -> Self {
|
||||
frp::extend! { network
|
||||
|
@ -16,7 +16,6 @@
|
||||
//! the time the cursor is between icons is less than the `end_delay_duration`. Instead, the hiding
|
||||
//! will only start iof the cursos has left any icon triggering the pop-up for longer than the
|
||||
//! `end_delay_duration`.
|
||||
//!
|
||||
use crate::prelude::*;
|
||||
|
||||
use crate::Animation;
|
||||
|
@ -51,8 +51,11 @@ impl Easing {
|
||||
Self { frp }.init(network, &animator)
|
||||
}
|
||||
|
||||
fn init
|
||||
(self, network:&frp::Network, animator:&easing::DynAnimator<f32,easing::QuadInOut>) -> Self {
|
||||
fn init(
|
||||
self,
|
||||
network: &frp::Network,
|
||||
animator: &easing::DynAnimator<f32, easing::QuadInOut>,
|
||||
) -> Self {
|
||||
let frp = &self.frp;
|
||||
frp::extend! { network
|
||||
eval frp.set_duration ((t) animator.set_duration(*t));
|
||||
|
@ -62,7 +62,8 @@ pub struct RawLoop<Callback> {
|
||||
}
|
||||
|
||||
impl<Callback> RawLoop<Callback>
|
||||
where Callback : RawLoopCallback {
|
||||
where Callback: RawLoopCallback
|
||||
{
|
||||
/// Create and start a new animation loop.
|
||||
pub fn new(callback: Callback) -> Self {
|
||||
let data = Rc::new(RefCell::new(RawLoopData::new(callback)));
|
||||
@ -135,7 +136,8 @@ pub struct Loop<Callback> {
|
||||
}
|
||||
|
||||
impl<Callback> Loop<Callback>
|
||||
where Callback : LoopCallback {
|
||||
where Callback: LoopCallback
|
||||
{
|
||||
/// Constructor.
|
||||
pub fn new(callback: Callback) -> Self {
|
||||
let time_info = Rc::new(Cell::new(TimeInfo::new()));
|
||||
@ -146,8 +148,13 @@ where Callback : LoopCallback {
|
||||
|
||||
/// Callback for an animation frame.
|
||||
pub type OnFrame<Callback> = impl FnMut(f32);
|
||||
fn on_frame<Callback>(mut callback:Callback, time_info_ref:Rc<Cell<TimeInfo>>) -> OnFrame<Callback>
|
||||
where Callback : LoopCallback {
|
||||
fn on_frame<Callback>(
|
||||
mut callback: Callback,
|
||||
time_info_ref: Rc<Cell<TimeInfo>>,
|
||||
) -> OnFrame<Callback>
|
||||
where
|
||||
Callback: LoopCallback,
|
||||
{
|
||||
move |current_time: f32| {
|
||||
let time_info = time_info_ref.get();
|
||||
let start = if time_info.start == 0.0 { current_time } else { time_info.start };
|
||||
@ -200,7 +207,7 @@ impl<Callback:FnMut<(TimeInfo,)>> FnMut<(TimeInfo,)> for FixedFrameRateSampler<C
|
||||
self.time_buffer += time.frame;
|
||||
loop {
|
||||
if self.time_buffer < 0.0 {
|
||||
break
|
||||
break;
|
||||
} else {
|
||||
self.time_buffer -= self.frame_time;
|
||||
let start = time.start;
|
||||
@ -224,7 +231,8 @@ impl<Callback:FnMut<(TimeInfo,)>> FnMut<(TimeInfo,)> for FixedFrameRateSampler<C
|
||||
pub type FixedFrameRateLoop<Callback> = Loop<FixedFrameRateSampler<Callback>>;
|
||||
|
||||
impl<Callback> FixedFrameRateLoop<Callback>
|
||||
where Callback:LoopCallback {
|
||||
where Callback: LoopCallback
|
||||
{
|
||||
/// Constructor.
|
||||
pub fn new_with_fixed_frame_rate(frame_rate: f32, callback: Callback) -> Self {
|
||||
Self::new(FixedFrameRateSampler::new(frame_rate, callback))
|
||||
@ -265,8 +273,7 @@ impl Default for DynamicLoop {
|
||||
fn default() -> Self {
|
||||
let data = Rc::new(RefCell::new(DynamicLoopData::default()));
|
||||
let weak = Rc::downgrade(&data);
|
||||
let raw_loop : Loop<Box<dyn FnMut(TimeInfo)>> =
|
||||
Loop::new(Box::new(move |time| {
|
||||
let raw_loop: Loop<Box<dyn FnMut(TimeInfo)>> = Loop::new(Box::new(move |time| {
|
||||
weak.upgrade().for_each(|data| {
|
||||
let mut data_mut = data.borrow_mut();
|
||||
data_mut.on_before_frame.run_all(time);
|
||||
|
@ -16,8 +16,9 @@ use crate::data::function::Fn1;
|
||||
|
||||
/// The type of the value of the simulation. In particular, the Value could be `f32`
|
||||
/// (1-dimensional simulation), or `Vector3<f32>` (3-dimensional simulation).
|
||||
pub trait Value
|
||||
= 'static + Copy + Default
|
||||
pub trait Value = 'static
|
||||
+ Copy
|
||||
+ Default
|
||||
+ PartialEq
|
||||
+ Normalize
|
||||
+ Magnitude<Output = f32>
|
||||
@ -34,7 +35,7 @@ macro_rules! define_f32_opr_mods {
|
||||
($name:ident $opr:ident $f:ident) => {
|
||||
define_f32_opr_mods_lhs! {$name $opr $f}
|
||||
define_f32_opr_mods_rhs! {$name $opr $f}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
macro_rules! define_f32_opr_mods_lhs {
|
||||
@ -66,7 +67,7 @@ macro_rules! define_f32_opr_mods_lhs {
|
||||
$name { value: self.$f(rhs.value) }
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
macro_rules! define_f32_opr_mods_rhs {
|
||||
@ -98,7 +99,7 @@ macro_rules! define_f32_opr_mods_rhs {
|
||||
$name { value: self.value.$f(rhs) }
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
macro_rules! define_self_opr_mods {
|
||||
@ -130,7 +131,7 @@ macro_rules! define_self_opr_mods {
|
||||
$name { value: self.value.$f(rhs.value) }
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
macro_rules! define_property {
|
||||
@ -178,7 +179,7 @@ define_property! { Mass = 30.0 }
|
||||
#[allow(missing_docs)]
|
||||
pub struct Thresholds {
|
||||
pub distance: f32,
|
||||
pub speed : f32
|
||||
pub speed: f32,
|
||||
}
|
||||
|
||||
impl Default for Thresholds {
|
||||
@ -223,19 +224,19 @@ pub struct SimulationData<T> {
|
||||
// We store the current value as an offset from the target rather than an absolute value. This
|
||||
// reduces numerical errors when animating floating point numbers: The offset will become very
|
||||
// small towards the end of the animation. Small floating point numbers offer higher precision
|
||||
// than large ones, because more digits can be used behind the point, for the fractional part of
|
||||
// the number. This higher precision helps us to avoid non-termination that could otherwise
|
||||
// happen due to rounding errors in our `step` function.
|
||||
// than large ones, because more digits can be used behind the point, for the fractional part
|
||||
// of the number. This higher precision helps us to avoid non-termination that could
|
||||
// otherwise happen due to rounding errors in our `step` function.
|
||||
//
|
||||
// For example: The precision of `f32` values is so low that we can only represent every second
|
||||
// integer above 16 777 216. If we simulate values that large and represented the simulation's
|
||||
// state by its current total value then this internal state would have to jump over those gaps.
|
||||
// The animation would either become to fast (if we rounded the steps up) or slow down too early
|
||||
// (if we rounded the steps down). Generally, it would be difficult to handle the rounding
|
||||
// errors gracefully. By representing the state as an offset, we achieve the highest possible
|
||||
// precision as the animation approaches its target. Large rounding errors might only happen
|
||||
// when the simulation is still far away from the target. But in those situations, high
|
||||
// precision is not as important.
|
||||
// state by its current total value then this internal state would have to jump over those
|
||||
// gaps. The animation would either become to fast (if we rounded the steps up) or slow
|
||||
// down too early (if we rounded the steps down). Generally, it would be difficult to
|
||||
// handle the rounding errors gracefully. By representing the state as an offset, we
|
||||
// achieve the highest possible precision as the animation approaches its target. Large
|
||||
// rounding errors might only happen when the simulation is still far away from the target.
|
||||
// But in those situations, high precision is not as important.
|
||||
offset_from_target: T,
|
||||
target_value: T,
|
||||
velocity: T,
|
||||
@ -300,14 +301,30 @@ impl<T:Value> SimulationData<T> {
|
||||
|
||||
#[allow(missing_docs)]
|
||||
impl<T: Value> SimulationData<T> {
|
||||
pub fn value (&self) -> T { self.target_value + self.offset_from_target }
|
||||
pub fn target_value (&self) -> T { self.target_value }
|
||||
pub fn velocity (&self) -> T { self.velocity }
|
||||
pub fn mass (&self) -> Mass { self.mass }
|
||||
pub fn spring (&self) -> Spring { self.spring }
|
||||
pub fn drag (&self) -> Drag { self.drag }
|
||||
pub fn thresholds (&self) -> Thresholds { self.thresholds }
|
||||
pub fn active (&self) -> bool { self.active }
|
||||
pub fn value(&self) -> T {
|
||||
self.target_value + self.offset_from_target
|
||||
}
|
||||
pub fn target_value(&self) -> T {
|
||||
self.target_value
|
||||
}
|
||||
pub fn velocity(&self) -> T {
|
||||
self.velocity
|
||||
}
|
||||
pub fn mass(&self) -> Mass {
|
||||
self.mass
|
||||
}
|
||||
pub fn spring(&self) -> Spring {
|
||||
self.spring
|
||||
}
|
||||
pub fn drag(&self) -> Drag {
|
||||
self.drag
|
||||
}
|
||||
pub fn thresholds(&self) -> Thresholds {
|
||||
self.thresholds
|
||||
}
|
||||
pub fn active(&self) -> bool {
|
||||
self.active
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -315,11 +332,21 @@ impl<T:Value> SimulationData<T> {
|
||||
|
||||
#[allow(missing_docs)]
|
||||
impl<T: Value> SimulationData<T> {
|
||||
pub fn set_velocity (&mut self, velocity:T) { self.velocity = velocity; }
|
||||
pub fn set_mass (&mut self, mass:Mass) { self.mass = mass; }
|
||||
pub fn set_spring (&mut self, spring:Spring) { self.spring = spring; }
|
||||
pub fn set_drag (&mut self, drag:Drag) { self.drag = drag; }
|
||||
pub fn set_thresholds (&mut self, thresholds:Thresholds) { self.thresholds = thresholds; }
|
||||
pub fn set_velocity(&mut self, velocity: T) {
|
||||
self.velocity = velocity;
|
||||
}
|
||||
pub fn set_mass(&mut self, mass: Mass) {
|
||||
self.mass = mass;
|
||||
}
|
||||
pub fn set_spring(&mut self, spring: Spring) {
|
||||
self.spring = spring;
|
||||
}
|
||||
pub fn set_drag(&mut self, drag: Drag) {
|
||||
self.drag = drag;
|
||||
}
|
||||
pub fn set_thresholds(&mut self, thresholds: Thresholds) {
|
||||
self.thresholds = thresholds;
|
||||
}
|
||||
|
||||
pub fn set_value(&mut self, value: T) {
|
||||
self.active = true;
|
||||
@ -382,7 +409,7 @@ impl<T:Value> SimulationData<T> {
|
||||
#[derive(Derivative, Default)]
|
||||
#[derivative(Debug(bound = "T:Copy+Debug"))]
|
||||
pub struct SimulationDataCell<T> {
|
||||
data : Cell<SimulationData<T>>
|
||||
data: Cell<SimulationData<T>>,
|
||||
}
|
||||
|
||||
impl<T: Value> SimulationDataCell<T> {
|
||||
@ -435,51 +462,87 @@ impl<T:Value> SimulationDataCell<T> {
|
||||
#[allow(missing_docs)]
|
||||
impl<T: Value> SimulationDataCell<T> {
|
||||
pub fn set_drag(&self, drag: Drag) {
|
||||
self.data.update(|mut sim| {sim.set_drag(drag); sim});
|
||||
self.data.update(|mut sim| {
|
||||
sim.set_drag(drag);
|
||||
sim
|
||||
});
|
||||
}
|
||||
|
||||
pub fn update_drag<F: FnOnce(Drag) -> Drag>(&self, f: F) {
|
||||
self.data.update(|mut sim| {sim.update_drag(f); sim});
|
||||
self.data.update(|mut sim| {
|
||||
sim.update_drag(f);
|
||||
sim
|
||||
});
|
||||
}
|
||||
|
||||
pub fn set_spring(&self, spring: Spring) {
|
||||
self.data.update(|mut sim| {sim.set_spring(spring); sim});
|
||||
self.data.update(|mut sim| {
|
||||
sim.set_spring(spring);
|
||||
sim
|
||||
});
|
||||
}
|
||||
|
||||
pub fn update_spring<F: FnOnce(Spring) -> Spring>(&self, f: F) {
|
||||
self.data.update(|mut sim| {sim.update_spring(f); sim});
|
||||
self.data.update(|mut sim| {
|
||||
sim.update_spring(f);
|
||||
sim
|
||||
});
|
||||
}
|
||||
|
||||
pub fn set_mass(&self, mass: Mass) {
|
||||
self.data.update(|mut sim| {sim.set_mass(mass); sim});
|
||||
self.data.update(|mut sim| {
|
||||
sim.set_mass(mass);
|
||||
sim
|
||||
});
|
||||
}
|
||||
|
||||
pub fn update_mass<F: FnOnce(Mass) -> Mass>(&self, f: F) {
|
||||
self.data.update(|mut sim| {sim.update_mass(f); sim});
|
||||
self.data.update(|mut sim| {
|
||||
sim.update_mass(f);
|
||||
sim
|
||||
});
|
||||
}
|
||||
|
||||
pub fn set_velocity(&self, velocity: T) {
|
||||
self.data.update(|mut sim| {sim.set_velocity(velocity); sim});
|
||||
self.data.update(|mut sim| {
|
||||
sim.set_velocity(velocity);
|
||||
sim
|
||||
});
|
||||
}
|
||||
|
||||
pub fn set_value(&self, value: T) {
|
||||
self.data.update(|mut sim| {sim.set_value(value); sim});
|
||||
self.data.update(|mut sim| {
|
||||
sim.set_value(value);
|
||||
sim
|
||||
});
|
||||
}
|
||||
|
||||
pub fn set_target_value(&self, target_value: T) {
|
||||
self.data.update(|mut sim| {sim.set_target_value(target_value); sim});
|
||||
self.data.update(|mut sim| {
|
||||
sim.set_target_value(target_value);
|
||||
sim
|
||||
});
|
||||
}
|
||||
|
||||
pub fn update_target_value<F: FnOnce(T) -> T>(&self, f: F) {
|
||||
self.data.update(|mut sim| {sim.update_target_value(f); sim});
|
||||
self.data.update(|mut sim| {
|
||||
sim.update_target_value(f);
|
||||
sim
|
||||
});
|
||||
}
|
||||
|
||||
pub fn set_precision(&self, precision: f32) {
|
||||
self.data.update(|mut sim| {sim.set_precision(precision); sim});
|
||||
self.data.update(|mut sim| {
|
||||
sim.set_precision(precision);
|
||||
sim
|
||||
});
|
||||
}
|
||||
|
||||
pub fn skip(&self) {
|
||||
self.data.update(|mut sim| {sim.skip(); sim});
|
||||
self.data.update(|mut sim| {
|
||||
sim.skip();
|
||||
sim
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@ -521,7 +584,12 @@ impl<T,OnStep,OnStart,OnEnd> Deref for SimulatorData<T,OnStep,OnStart,OnEnd> {
|
||||
}
|
||||
|
||||
impl<T, OnStep, OnStart, OnEnd> SimulatorData<T, OnStep, OnStart, OnEnd>
|
||||
where T:Value, OnStep:Callback1<T>, OnStart:Callback0, OnEnd:Callback1<EndStatus> {
|
||||
where
|
||||
T: Value,
|
||||
OnStep: Callback1<T>,
|
||||
OnStart: Callback0,
|
||||
OnEnd: Callback1<EndStatus>,
|
||||
{
|
||||
/// Constructor.
|
||||
pub fn new(on_step: OnStep, on_start: OnStart, on_end: OnEnd) -> Self {
|
||||
let simulation = SimulationDataCell::new();
|
||||
@ -569,7 +637,12 @@ impl<T,OnStep,OnStart,OnEnd> Deref for Simulator<T,OnStep,OnStart,OnEnd> {
|
||||
}
|
||||
|
||||
impl<T, OnStep, OnStart, OnEnd> Simulator<T, OnStep, OnStart, OnEnd>
|
||||
where T:Value, OnStep:Callback1<T>, OnStart:Callback0, OnEnd:Callback1<EndStatus> {
|
||||
where
|
||||
T: Value,
|
||||
OnStep: Callback1<T>,
|
||||
OnStart: Callback0,
|
||||
OnEnd: Callback1<EndStatus>,
|
||||
{
|
||||
/// Constructor.
|
||||
pub fn new(callback: OnStep, on_start: OnStart, on_end: OnEnd) -> Self {
|
||||
let data = Rc::new(SimulatorData::new(callback, on_start, on_end));
|
||||
@ -589,7 +662,12 @@ impl<T,OnStep,OnStart,OnEnd> Debug for Simulator<T,OnStep,OnStart,OnEnd> {
|
||||
|
||||
#[allow(missing_docs)]
|
||||
impl<T, OnStep, OnStart, OnEnd> Simulator<T, OnStep, OnStart, OnEnd>
|
||||
where T:Value, OnStep:Callback1<T>, OnStart:Callback0, OnEnd:Callback1<EndStatus> {
|
||||
where
|
||||
T: Value,
|
||||
OnStep: Callback1<T>,
|
||||
OnStart: Callback0,
|
||||
OnEnd: Callback1<EndStatus>,
|
||||
{
|
||||
pub fn set_value(&self, value: T) {
|
||||
self.simulation.set_value(value);
|
||||
self.start();
|
||||
@ -611,7 +689,12 @@ where T:Value, OnStep:Callback1<T>, OnStart:Callback0, OnEnd:Callback1<EndStatus
|
||||
// === Private API ===
|
||||
|
||||
impl<T, OnStep, OnStart, OnEnd> Simulator<T, OnStep, OnStart, OnEnd>
|
||||
where T:Value, OnStep:Callback1<T>, OnStart:Callback0, OnEnd:Callback1<EndStatus> {
|
||||
where
|
||||
T: Value,
|
||||
OnStep: Callback1<T>,
|
||||
OnStart: Callback0,
|
||||
OnEnd: Callback1<EndStatus>,
|
||||
{
|
||||
fn init(self) -> Self {
|
||||
self.start();
|
||||
self
|
||||
@ -700,9 +783,14 @@ pub type FixedFrameRateAnimationStep<T,OnStep,OnStart,OnEnd> =
|
||||
/// Callback for an animation step.
|
||||
pub type Step<T, OnStep, OnStart, OnEnd> = impl Fn(animation::TimeInfo);
|
||||
|
||||
fn step<T,OnStep,OnStart,OnEnd>(simulator:&Simulator<T,OnStep,OnStart,OnEnd>)
|
||||
-> Step<T,OnStep,OnStart,OnEnd>
|
||||
where T:Value, OnStep:Callback1<T>, OnStart:Callback0, OnEnd:Callback1<EndStatus> {
|
||||
fn step<T, OnStep, OnStart, OnEnd>(
|
||||
simulator: &Simulator<T, OnStep, OnStart, OnEnd>,
|
||||
) -> Step<T, OnStep, OnStart, OnEnd>
|
||||
where
|
||||
T: Value,
|
||||
OnStep: Callback1<T>,
|
||||
OnStart: Callback0,
|
||||
OnEnd: Callback1<EndStatus>, {
|
||||
let data = simulator.data.clone_ref();
|
||||
let animation_loop = simulator.animation_loop.downgrade();
|
||||
move |time: animation::TimeInfo| {
|
||||
@ -725,12 +813,19 @@ where T:Value, OnStep:Callback1<T>, OnStart:Callback0, OnEnd:Callback1<EndStatus
|
||||
/// forced, which means that it was forced by the user (for example by using the `stop` function).
|
||||
#[derive(Clone, Copy, Debug, Eq, PartialEq, Hash)]
|
||||
#[allow(missing_docs)]
|
||||
pub enum EndStatus { Normal, Forced }
|
||||
pub enum EndStatus {
|
||||
Normal,
|
||||
Forced,
|
||||
}
|
||||
|
||||
#[allow(missing_docs)]
|
||||
impl EndStatus {
|
||||
pub fn is_normal(self) -> bool { self == Self::Normal }
|
||||
pub fn is_forced(self) -> bool { self == Self::Forced }
|
||||
pub fn is_normal(self) -> bool {
|
||||
self == Self::Normal
|
||||
}
|
||||
pub fn is_forced(self) -> bool {
|
||||
self == Self::Forced
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for EndStatus {
|
||||
|
@ -2,18 +2,18 @@
|
||||
|
||||
pub mod args;
|
||||
pub mod command;
|
||||
pub mod frp;
|
||||
pub mod shortcut;
|
||||
pub mod view;
|
||||
pub mod frp;
|
||||
|
||||
pub use view::View;
|
||||
|
||||
use crate::prelude::*;
|
||||
|
||||
use crate::control::callback;
|
||||
use crate::display;
|
||||
use crate::display::style::theme;
|
||||
use crate::display::world::World;
|
||||
use crate::display;
|
||||
use crate::gui::cursor::Cursor;
|
||||
use crate::system::web;
|
||||
use ensogl_system_web::StyleSetter;
|
||||
@ -46,7 +46,8 @@ impl Application {
|
||||
let display = World::new(dom);
|
||||
let scene = display.scene();
|
||||
let commands = command::Registry::create(&logger);
|
||||
let shortcuts = shortcut::Registry::new(&logger,&scene.mouse.frp,&scene.keyboard.frp,&commands);
|
||||
let shortcuts =
|
||||
shortcut::Registry::new(&logger, &scene.mouse.frp, &scene.keyboard.frp, &commands);
|
||||
let views = view::Registry::create(&logger, &display, &commands, &shortcuts);
|
||||
let themes = theme::Manager::from(&display.scene().style_sheet);
|
||||
let cursor = Cursor::new(display.scene());
|
||||
|
@ -10,7 +10,8 @@ use crate::prelude::*;
|
||||
// =================
|
||||
|
||||
/// Marker trait used to disambiguate overlapping impls of [`ArgReader`].
|
||||
#[marker] pub trait ArgMarker {}
|
||||
#[marker]
|
||||
pub trait ArgMarker {}
|
||||
|
||||
/// Trait used to convert provided string arguments to the desired type.
|
||||
#[allow(missing_docs)]
|
||||
@ -28,7 +29,8 @@ pub trait ArgReaderFromString : Sized {
|
||||
}
|
||||
|
||||
impl<T> ArgReaderFromString for T
|
||||
where String:TryInto<T> {
|
||||
where String: TryInto<T>
|
||||
{
|
||||
fn read_arg_from_string(str: String) -> Option<Self> {
|
||||
str.try_into().ok()
|
||||
}
|
||||
@ -41,7 +43,9 @@ impl<T> ArgReaderFromString for T {
|
||||
}
|
||||
|
||||
impl<T> ArgMarker for T where T: TryFrom<String> {}
|
||||
impl<T> ArgReader for T where T : ArgMarker {
|
||||
impl<T> ArgReader for T
|
||||
where T: ArgMarker
|
||||
{
|
||||
default fn read_arg(str: String) -> Option<Self> {
|
||||
ArgReaderFromString::read_arg_from_string(str)
|
||||
}
|
||||
|
@ -1,8 +1,8 @@
|
||||
//! Definition of commands, labeled FPR endpoints useful when implementing actions which can be
|
||||
//! altered at runtime, like a keyboard shortcut management.
|
||||
|
||||
use crate::prelude::*;
|
||||
use crate::frp;
|
||||
use crate::prelude::*;
|
||||
|
||||
use crate::application::shortcut;
|
||||
use crate::application::shortcut::Shortcut;
|
||||
@ -36,31 +36,38 @@ pub trait View : FrpNetworkProvider + DerefToCommandApi {
|
||||
}
|
||||
|
||||
/// Add a new shortcut targeting the self object.
|
||||
fn self_shortcut
|
||||
( action_type : shortcut::ActionType
|
||||
, pattern : impl Into<String>
|
||||
, command : impl Into<shortcut::Command>
|
||||
fn self_shortcut(
|
||||
action_type: shortcut::ActionType,
|
||||
pattern: impl Into<String>,
|
||||
command: impl Into<shortcut::Command>,
|
||||
) -> Shortcut {
|
||||
Shortcut::new(shortcut::Rule::new(action_type, pattern), Self::label(), command)
|
||||
}
|
||||
|
||||
/// Add a new shortcut targeting the self object.
|
||||
fn self_shortcut_when
|
||||
( action_type : shortcut::ActionType
|
||||
, pattern : impl Into<String>
|
||||
, command : impl Into<shortcut::Command>
|
||||
, condition : impl Into<shortcut::Condition>
|
||||
fn self_shortcut_when(
|
||||
action_type: shortcut::ActionType,
|
||||
pattern: impl Into<String>,
|
||||
command: impl Into<shortcut::Command>,
|
||||
condition: impl Into<shortcut::Condition>,
|
||||
) -> Shortcut {
|
||||
Shortcut::new_when(shortcut::Rule::new(action_type,pattern),Self::label(),command,condition)
|
||||
Shortcut::new_when(
|
||||
shortcut::Rule::new(action_type, pattern),
|
||||
Self::label(),
|
||||
command,
|
||||
condition,
|
||||
)
|
||||
}
|
||||
|
||||
/// Disable the command in this component instance.
|
||||
fn disable_command(&self, name:impl AsRef<str>) where Self:Sized {
|
||||
fn disable_command(&self, name: impl AsRef<str>)
|
||||
where Self: Sized {
|
||||
self.app().commands.disable_command(self, name)
|
||||
}
|
||||
|
||||
/// Enable the command in this component instance.
|
||||
fn enable_command(&self, name:impl AsRef<str>) where Self:Sized {
|
||||
fn enable_command(&self, name: impl AsRef<str>)
|
||||
where Self: Sized {
|
||||
self.app().commands.enable_command(self, name)
|
||||
}
|
||||
}
|
||||
@ -111,8 +118,12 @@ impl Command {
|
||||
/// Use the `define_endpoints!` macro to auto-derive it.
|
||||
#[allow(missing_docs)]
|
||||
pub trait CommandApi: Sized {
|
||||
fn command_api(&self) -> Rc<RefCell<HashMap<String,Command>>> { default() }
|
||||
fn status_api(&self) -> Rc<RefCell<HashMap<String,frp::Sampler<bool>>>> { default() }
|
||||
fn command_api(&self) -> Rc<RefCell<HashMap<String, Command>>> {
|
||||
default()
|
||||
}
|
||||
fn status_api(&self) -> Rc<RefCell<HashMap<String, frp::Sampler<bool>>>> {
|
||||
default()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -189,10 +200,12 @@ impl Registry {
|
||||
let was_registered = self.name_map.borrow().get(label).is_some();
|
||||
if !was_registered {
|
||||
self.register::<T>();
|
||||
warning!(&self.logger,
|
||||
warning!(
|
||||
&self.logger,
|
||||
"The command provider '{label}' was created but never registered. You should \
|
||||
always register available command providers as soon as possible to spread the \
|
||||
information about their API.");
|
||||
information about their API."
|
||||
);
|
||||
};
|
||||
let id = instance.id();
|
||||
self.name_map.borrow_mut().get_mut(label).unwrap().push(instance.clone_ref());
|
||||
@ -201,11 +214,11 @@ impl Registry {
|
||||
|
||||
/// Queries the command map by command name and applies the provided function to the result.
|
||||
/// Emits warnings in case the command could not be found.
|
||||
fn with_command_mut<T:View>
|
||||
( &self
|
||||
, target : &T
|
||||
, name : impl AsRef<str>
|
||||
, f : impl Fn(&mut Command)
|
||||
fn with_command_mut<T: View>(
|
||||
&self,
|
||||
target: &T,
|
||||
name: impl AsRef<str>,
|
||||
f: impl Fn(&mut Command),
|
||||
) {
|
||||
let name = name.as_ref();
|
||||
let id = T::network(target).id();
|
||||
@ -213,8 +226,8 @@ impl Registry {
|
||||
None => warning!(&self.logger, "The provided component ID is invalid {id}."),
|
||||
Some(instance) => match instance.command_map.borrow_mut().get_mut(name) {
|
||||
None => warning!(&self.logger, "The command name {name} is invalid."),
|
||||
Some(command) => f(command)
|
||||
}
|
||||
Some(command) => f(command),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -416,7 +416,7 @@ macro_rules! build_status_map {
|
||||
($map:ident $field:ident (bool) $frp:expr) => {
|
||||
$map.insert(stringify!($field).into(), $frp.clone_ref());
|
||||
};
|
||||
($($ts:tt)*) => {}
|
||||
($($ts:tt)*) => {};
|
||||
}
|
||||
|
||||
/// Internal helper of `define_endpoints` macro.
|
||||
@ -425,7 +425,7 @@ macro_rules! build_command_map {
|
||||
($map:ident $field:ident () $frp:expr) => {
|
||||
$map.insert(stringify!($field).into(), Command::new($frp.clone_ref()));
|
||||
};
|
||||
($($ts:tt)*) => {}
|
||||
($($ts:tt)*) => {};
|
||||
}
|
||||
|
||||
/// Defines a method which is an alias to FRP emit method. Used internally by the `define_endpoints`
|
||||
@ -441,11 +441,7 @@ macro_rules! define_endpoints_emit_alias {
|
||||
|
||||
($field:ident ($t1:ty,$t2:ty)) => {
|
||||
#[allow(missing_docs)]
|
||||
pub fn $field
|
||||
( &self
|
||||
, t1:impl IntoParam<$t1>
|
||||
, t2:impl IntoParam<$t2>
|
||||
) {
|
||||
pub fn $field(&self, t1: impl IntoParam<$t1>, t2: impl IntoParam<$t2>) {
|
||||
let t1 = t1.into_param();
|
||||
let t2 = t2.into_param();
|
||||
self.$field.emit((t1, t2));
|
||||
@ -454,11 +450,11 @@ macro_rules! define_endpoints_emit_alias {
|
||||
|
||||
($field:ident ($t1:ty,$t2:ty,$t3:ty)) => {
|
||||
#[allow(missing_docs)]
|
||||
pub fn $field
|
||||
( &self
|
||||
, t1:impl IntoParam<$t1>
|
||||
, t2:impl IntoParam<$t2>
|
||||
, t3:impl IntoParam<$t3>
|
||||
pub fn $field(
|
||||
&self,
|
||||
t1: impl IntoParam<$t1>,
|
||||
t2: impl IntoParam<$t2>,
|
||||
t3: impl IntoParam<$t3>,
|
||||
) {
|
||||
let t1 = t1.into_param();
|
||||
let t2 = t2.into_param();
|
||||
@ -469,12 +465,12 @@ macro_rules! define_endpoints_emit_alias {
|
||||
|
||||
($field:ident ($t1:ty,$t2:ty,$t3:ty,$t4:ty)) => {
|
||||
#[allow(missing_docs)]
|
||||
pub fn $field
|
||||
( &self
|
||||
, t1:impl IntoParam<$t1>
|
||||
, t2:impl IntoParam<$t2>
|
||||
, t3:impl IntoParam<$t3>
|
||||
, t4:impl IntoParam<$t4>
|
||||
pub fn $field(
|
||||
&self,
|
||||
t1: impl IntoParam<$t1>,
|
||||
t2: impl IntoParam<$t2>,
|
||||
t3: impl IntoParam<$t3>,
|
||||
t4: impl IntoParam<$t4>,
|
||||
) {
|
||||
let t1 = t1.into_param();
|
||||
let t2 = t2.into_param();
|
||||
@ -486,13 +482,13 @@ macro_rules! define_endpoints_emit_alias {
|
||||
|
||||
($field:ident ($t1:ty,$t2:ty,$t3:ty,$t4:ty,$t5:ty)) => {
|
||||
#[allow(missing_docs)]
|
||||
pub fn $field
|
||||
( &self
|
||||
, t1:impl IntoParam<$t1>
|
||||
, t2:impl IntoParam<$t2>
|
||||
, t3:impl IntoParam<$t3>
|
||||
, t4:impl IntoParam<$t4>
|
||||
, t5:impl IntoParam<$t5>
|
||||
pub fn $field(
|
||||
&self,
|
||||
t1: impl IntoParam<$t1>,
|
||||
t2: impl IntoParam<$t2>,
|
||||
t3: impl IntoParam<$t3>,
|
||||
t4: impl IntoParam<$t4>,
|
||||
t5: impl IntoParam<$t5>,
|
||||
) {
|
||||
let t1 = t1.into_param();
|
||||
let t2 = t2.into_param();
|
||||
|
@ -4,9 +4,9 @@ use crate::prelude::*;
|
||||
|
||||
use super::command;
|
||||
|
||||
use crate::frp;
|
||||
use crate::frp::io::keyboard;
|
||||
use crate::frp::io::mouse::Mouse;
|
||||
use crate::frp;
|
||||
|
||||
use enso_shortcuts as shortcuts;
|
||||
use enso_shortcuts::traits::*;
|
||||
@ -92,11 +92,11 @@ impl Condition {
|
||||
|
||||
/// Split the input on the provided `separator`, process each chunk with `f`, and fold results
|
||||
/// using the `cons`.
|
||||
fn split_parse
|
||||
( input : &str
|
||||
, separator : char
|
||||
, cons : impl Fn(Self,Self) -> Self
|
||||
, f : impl Fn(&str) -> Self
|
||||
fn split_parse(
|
||||
input: &str,
|
||||
separator: char,
|
||||
cons: impl Fn(Self, Self) -> Self,
|
||||
f: impl Fn(&str) -> Self,
|
||||
) -> Self {
|
||||
input.split(separator).map(|t| t.trim()).map(f).fold1(cons).unwrap_or(Self::Never)
|
||||
}
|
||||
@ -108,8 +108,10 @@ impl Condition {
|
||||
#[allow(clippy::redundant_closure)] // Rust TC does not agree.
|
||||
fn parse(s: impl AsRef<str>) -> Self {
|
||||
let s = s.as_ref().trim();
|
||||
if s.is_empty() { Self::Always } else {
|
||||
Self::split_parse(s,'|',Self::or,|s|
|
||||
if s.is_empty() {
|
||||
Self::Always
|
||||
} else {
|
||||
Self::split_parse(s, '|', Self::or, |s| {
|
||||
Self::split_parse(s, '&', Self::and, |s| {
|
||||
if let Some(expr) = s.strip_prefix('!') {
|
||||
Self::not(Self::when(expr.trim()))
|
||||
@ -117,7 +119,7 @@ impl Condition {
|
||||
Self::when(s)
|
||||
}
|
||||
})
|
||||
)
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -151,8 +153,11 @@ impl Action {
|
||||
}
|
||||
|
||||
/// Constructor.
|
||||
pub fn new_when
|
||||
(target:impl Into<String>, command:impl Into<Command>, condition:impl Into<Condition>) -> Self {
|
||||
pub fn new_when(
|
||||
target: impl Into<String>,
|
||||
command: impl Into<Command>,
|
||||
condition: impl Into<Condition>,
|
||||
) -> Self {
|
||||
let target = target.into();
|
||||
let condition = condition.into();
|
||||
let command = command.into();
|
||||
@ -176,10 +181,10 @@ pub struct Shortcut {
|
||||
|
||||
impl Shortcut {
|
||||
/// Constructor. Version without condition checker.
|
||||
pub fn new
|
||||
( rule : impl Into<Rule>
|
||||
, target : impl Into<String>
|
||||
, command : impl Into<Command>
|
||||
pub fn new(
|
||||
rule: impl Into<Rule>,
|
||||
target: impl Into<String>,
|
||||
command: impl Into<Command>,
|
||||
) -> Self {
|
||||
let action = Action::new(target, command);
|
||||
let rule = rule.into();
|
||||
@ -187,11 +192,11 @@ impl Shortcut {
|
||||
}
|
||||
|
||||
/// Constructor.
|
||||
pub fn new_when
|
||||
( rule : impl Into<Rule>
|
||||
, target : impl Into<String>
|
||||
, command : impl Into<Command>
|
||||
, condition : impl Into<Condition>
|
||||
pub fn new_when(
|
||||
rule: impl Into<Rule>,
|
||||
target: impl Into<String>,
|
||||
command: impl Into<Command>,
|
||||
condition: impl Into<Condition>,
|
||||
) -> Self {
|
||||
let action = Action::new_when(target, command, condition);
|
||||
let rule = rule.into();
|
||||
@ -239,9 +244,12 @@ impl Deref for Registry {
|
||||
|
||||
impl Registry {
|
||||
/// Constructor.
|
||||
pub fn new
|
||||
(logger:&Logger, mouse:&Mouse, keyboard:&keyboard::Keyboard, cmd_registry:&command::Registry)
|
||||
-> Self {
|
||||
pub fn new(
|
||||
logger: &Logger,
|
||||
mouse: &Mouse,
|
||||
keyboard: &keyboard::Keyboard,
|
||||
cmd_registry: &command::Registry,
|
||||
) -> Self {
|
||||
let model = RegistryModel::new(logger, mouse, keyboard, cmd_registry);
|
||||
let mouse = &model.mouse;
|
||||
|
||||
@ -259,11 +267,11 @@ impl Registry {
|
||||
|
||||
impl RegistryModel {
|
||||
/// Constructor.
|
||||
pub fn new
|
||||
( logger : impl AnyLogger
|
||||
, mouse : &Mouse
|
||||
, keyboard : &keyboard::Keyboard
|
||||
, command_registry : &command::Registry
|
||||
pub fn new(
|
||||
logger: impl AnyLogger,
|
||||
mouse: &Mouse,
|
||||
keyboard: &keyboard::Keyboard,
|
||||
command_registry: &command::Registry,
|
||||
) -> Self {
|
||||
let logger = Logger::new_sub(logger, "ShortcutRegistry");
|
||||
let keyboard = keyboard.clone_ref();
|
||||
@ -284,9 +292,14 @@ impl RegistryModel {
|
||||
if Self::condition_checker(&rule.condition, &instance.status_map) {
|
||||
let command_name = &rule.command.name;
|
||||
match instance.command_map.borrow().get(command_name) {
|
||||
Some(cmd) => if cmd.enabled { targets.push(cmd.frp.clone_ref()) },
|
||||
None => warning!(&self.logger,
|
||||
"Command {command_name} was not found on {target}."),
|
||||
Some(cmd) =>
|
||||
if cmd.enabled {
|
||||
targets.push(cmd.frp.clone_ref())
|
||||
},
|
||||
None => warning!(
|
||||
&self.logger,
|
||||
"Command {command_name} was not found on {target}."
|
||||
),
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -298,8 +311,10 @@ impl RegistryModel {
|
||||
}
|
||||
}
|
||||
|
||||
fn condition_checker
|
||||
(condition:&Condition, status:&Rc<RefCell<HashMap<String,frp::Sampler<bool>>>>) -> bool {
|
||||
fn condition_checker(
|
||||
condition: &Condition,
|
||||
status: &Rc<RefCell<HashMap<String, frp::Sampler<bool>>>>,
|
||||
) -> bool {
|
||||
use Condition::*;
|
||||
match condition {
|
||||
Always => true,
|
||||
@ -315,6 +330,10 @@ impl RegistryModel {
|
||||
impl Add<Shortcut> for &Registry {
|
||||
type Output = ();
|
||||
fn add(self, shortcut: Shortcut) {
|
||||
self.model.shortcuts_registry.add(shortcut.rule.tp,&shortcut.rule.pattern,shortcut.clone());
|
||||
self.model.shortcuts_registry.add(
|
||||
shortcut.rule.tp,
|
||||
&shortcut.rule.pattern,
|
||||
shortcut.clone(),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -2,10 +2,10 @@
|
||||
|
||||
use crate::prelude::*;
|
||||
|
||||
use crate::display::world::World;
|
||||
use super::command;
|
||||
use super::shortcut;
|
||||
use super::Application;
|
||||
use crate::display::world::World;
|
||||
|
||||
pub use command::View;
|
||||
|
||||
@ -29,11 +29,11 @@ pub struct Registry {
|
||||
|
||||
impl Registry {
|
||||
/// Constructor.
|
||||
pub fn create
|
||||
( logger : impl AnyLogger
|
||||
, display : &World
|
||||
, command_registry : &command::Registry
|
||||
, shortcut_registry : &shortcut::Registry
|
||||
pub fn create(
|
||||
logger: impl AnyLogger,
|
||||
display: &World,
|
||||
command_registry: &command::Registry,
|
||||
shortcut_registry: &shortcut::Registry,
|
||||
) -> Self {
|
||||
let logger = Logger::new_sub(logger, "view_registry");
|
||||
let display = display.clone_ref();
|
||||
@ -58,10 +58,12 @@ impl Registry {
|
||||
let label = V::label();
|
||||
let was_registered = self.definitions.borrow().get(label).is_some();
|
||||
if !was_registered {
|
||||
warning!(&self.logger,
|
||||
warning!(
|
||||
&self.logger,
|
||||
"The view '{label}' was created but never registered, performing automatic \
|
||||
registration. You should always register available views as soon as possible to \
|
||||
enable their default shortcuts and spread the information about their API.");
|
||||
enable their default shortcuts and spread the information about their API."
|
||||
);
|
||||
self.register::<V>();
|
||||
}
|
||||
let view = V::new(app);
|
||||
|
@ -9,12 +9,12 @@ use crate::system::web;
|
||||
|
||||
use std::cell::RefCell;
|
||||
use std::rc::Rc;
|
||||
use wasm_bindgen::prelude::Closure;
|
||||
use wasm_bindgen::JsCast;
|
||||
use wasm_bindgen::JsValue;
|
||||
use wasm_bindgen::prelude::Closure;
|
||||
|
||||
pub use event::*;
|
||||
pub use crate::frp::io::mouse::*;
|
||||
pub use event::*;
|
||||
|
||||
|
||||
|
||||
@ -29,7 +29,7 @@ pub use crate::frp::io::mouse::*;
|
||||
#[derivative(Clone(bound = ""))]
|
||||
#[derivative(Default(bound = ""))]
|
||||
pub struct EventDispatcher<T> {
|
||||
rc: Rc<RefCell<callback::Registry1<T>>>
|
||||
rc: Rc<RefCell<callback::Registry1<T>>>,
|
||||
}
|
||||
|
||||
impl<T> EventDispatcher<T> {
|
||||
@ -57,7 +57,7 @@ pub struct MouseManager {
|
||||
#[shrinkwrap(main_field)]
|
||||
dispatchers: MouseManagerDispatchers,
|
||||
closures: Rc<MouseManagerClosures>,
|
||||
dom : web::dom::WithKnownShape<web::EventTarget>
|
||||
dom: web::dom::WithKnownShape<web::EventTarget>,
|
||||
}
|
||||
|
||||
/// A JavaScript callback closure for any mouse event.
|
||||
@ -158,13 +158,12 @@ pub struct MouseFrpCallbackHandles {
|
||||
on_move: callback::Handle,
|
||||
on_down: callback::Handle,
|
||||
on_up: callback::Handle,
|
||||
on_wheel : callback::Handle
|
||||
on_wheel: callback::Handle,
|
||||
}
|
||||
|
||||
// FIXME: This is obsolete. Use mouse bindings from scene instead.
|
||||
/// Bind FRP graph to MouseManager.
|
||||
pub fn bind_frp_to_mouse(frp:&Mouse, mouse_manager:&MouseManager)
|
||||
-> MouseFrpCallbackHandles {
|
||||
pub fn bind_frp_to_mouse(frp: &Mouse, mouse_manager: &MouseManager) -> MouseFrpCallbackHandles {
|
||||
let dom_shape = mouse_manager.dom.clone_ref().shape();
|
||||
let on_move = enclose!((frp.position => frp) move |e:&OnMove| {
|
||||
let position = Vector2(e.client_x() as f32,e.client_y() as f32);
|
||||
@ -178,6 +177,6 @@ pub fn bind_frp_to_mouse(frp:&Mouse, mouse_manager:&MouseManager)
|
||||
on_move: mouse_manager.on_move.add(on_move),
|
||||
on_down: mouse_manager.on_down.add(on_down),
|
||||
on_up: mouse_manager.on_up.add(on_up),
|
||||
on_wheel : mouse_manager.on_wheel.add(on_wheel)
|
||||
on_wheel: mouse_manager.on_wheel.add(on_wheel),
|
||||
}
|
||||
}
|
||||
|
@ -2,8 +2,8 @@
|
||||
|
||||
use crate::prelude::*;
|
||||
|
||||
use enso_frp::io::mouse;
|
||||
use crate::system::web::dom::Shape;
|
||||
use enso_frp::io::mouse;
|
||||
|
||||
use wasm_bindgen::JsCast;
|
||||
|
||||
|
@ -15,15 +15,15 @@
|
||||
//! make them more pleasant to work with, however, the equations you will fnd will probably work on
|
||||
//! different value ranges. Read documentation for each color space very carefully.
|
||||
|
||||
pub mod animation;
|
||||
pub mod component;
|
||||
pub mod data;
|
||||
pub mod gradient;
|
||||
pub mod mix;
|
||||
pub mod space;
|
||||
pub mod animation;
|
||||
|
||||
pub use self::data::*;
|
||||
pub use animation::Animation;
|
||||
pub use component::*;
|
||||
pub use mix::mix;
|
||||
pub use self::data::*;
|
||||
pub use space::*;
|
||||
|
@ -3,8 +3,8 @@
|
||||
//! discontinuities of the polar coordinates of Lcha (i.e., a transition from hue 1 to 359 would go
|
||||
//! through all hues instead of taking the shorter trip "backwards").
|
||||
|
||||
use crate::prelude::*;
|
||||
use super::*;
|
||||
use crate::prelude::*;
|
||||
|
||||
use enso_frp as frp;
|
||||
|
||||
@ -28,8 +28,8 @@ crate::define_endpoints! {
|
||||
}
|
||||
|
||||
/// The `Animation` provides color better animations for colors than the raw
|
||||
/// `component::DEPRECATED_Animation<_>`, as it allows controlling the alpha channel separately which is
|
||||
/// important for nice fade outs.
|
||||
/// `component::DEPRECATED_Animation<_>`, as it allows controlling the alpha channel separately
|
||||
/// which is important for nice fade outs.
|
||||
#[derive(Clone, CloneRef, Debug)]
|
||||
#[allow(missing_docs)]
|
||||
pub struct Animation {
|
||||
|
@ -17,7 +17,7 @@ use nalgebra::Vector4;
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
#[allow(missing_docs)]
|
||||
pub struct Components<T> {
|
||||
pub tuple : T
|
||||
pub tuple: T,
|
||||
}
|
||||
|
||||
/// Smart constructor.
|
||||
@ -61,11 +61,16 @@ pub type ComponentsOf<T> = Components<ComponentsReprOf<T>>;
|
||||
|
||||
// === Generics ===
|
||||
|
||||
impl<T:KnownLast> KnownLast for Components<T> { type Last = Last<T>; }
|
||||
impl<T:KnownInit> KnownInit for Components<T> { type Init = Components<Init<T>>; }
|
||||
impl<T: KnownLast> KnownLast for Components<T> {
|
||||
type Last = Last<T>;
|
||||
}
|
||||
impl<T: KnownInit> KnownInit for Components<T> {
|
||||
type Init = Components<Init<T>>;
|
||||
}
|
||||
|
||||
impl<T, X> PushBack<X> for Components<T>
|
||||
where T:PushBack<X> {
|
||||
where T: PushBack<X>
|
||||
{
|
||||
type Output = Components<<T as PushBack<X>>::Output>;
|
||||
fn push_back(self, t: X) -> Self::Output {
|
||||
Components(self.tuple.push_back(t))
|
||||
@ -73,7 +78,8 @@ impl<T,X> PushBack<X> for Components<T>
|
||||
}
|
||||
|
||||
impl<T> PopBack for Components<T>
|
||||
where T:PopBack {
|
||||
where T: PopBack
|
||||
{
|
||||
fn pop_back(self) -> (Self::Last, Self::Init) {
|
||||
let (last, init) = self.tuple.pop_back();
|
||||
let init = Components(init);
|
||||
@ -216,9 +222,15 @@ define_operators_for_component_refs! { Add::add, Sub::sub, Mul::mul, Div::div }
|
||||
// === Vector Conversions ===
|
||||
// ==========================
|
||||
|
||||
impl<T:Scalar> HasComponentsRepr for Vector2<T> { type ComponentsRepr = (T,T); }
|
||||
impl<T:Scalar> HasComponentsRepr for Vector3<T> { type ComponentsRepr = (T,T,T); }
|
||||
impl<T:Scalar> HasComponentsRepr for Vector4<T> { type ComponentsRepr = (T,T,T,T); }
|
||||
impl<T: Scalar> HasComponentsRepr for Vector2<T> {
|
||||
type ComponentsRepr = (T, T);
|
||||
}
|
||||
impl<T: Scalar> HasComponentsRepr for Vector3<T> {
|
||||
type ComponentsRepr = (T, T, T);
|
||||
}
|
||||
impl<T: Scalar> HasComponentsRepr for Vector4<T> {
|
||||
type ComponentsRepr = (T, T, T, T);
|
||||
}
|
||||
|
||||
impl<T: Scalar> From<Vector2<T>> for ComponentsOf<Vector2<T>> {
|
||||
fn from(t: Vector2<T>) -> Self {
|
||||
|
@ -5,8 +5,8 @@ use crate::prelude::*;
|
||||
|
||||
use enso_generics::*;
|
||||
|
||||
use super::component::*;
|
||||
use super::component::HasComponents;
|
||||
use super::component::*;
|
||||
use nalgebra::Vector3;
|
||||
use nalgebra::Vector4;
|
||||
|
||||
@ -30,7 +30,7 @@ use nalgebra::Vector4;
|
||||
#[derive(Clone, Copy, Default, PartialEq)]
|
||||
pub struct Color<D> {
|
||||
/// The underlying color representation. It is either `Alpha` or a color space instance.
|
||||
pub data : D
|
||||
pub data: D,
|
||||
}
|
||||
|
||||
/// Smart constructor.
|
||||
@ -70,8 +70,12 @@ impl<D> DerefMut for Color<D> {
|
||||
|
||||
/// Type family for accessing color models.
|
||||
#[allow(missing_docs)]
|
||||
pub trait HasModel { type Model; }
|
||||
impl<M> HasModel for Color<M> { type Model = M; }
|
||||
pub trait HasModel {
|
||||
type Model;
|
||||
}
|
||||
impl<M> HasModel for Color<M> {
|
||||
type Model = M;
|
||||
}
|
||||
|
||||
/// Accessor for `HasModel::Model`.
|
||||
pub type Model<T> = <T as HasModel>::Model;
|
||||
@ -93,7 +97,8 @@ impl<D:ComponentMap> ComponentMap for Color<D> {
|
||||
// === Conversions ===
|
||||
|
||||
impl<D1, D2> From<&Color<D1>> for Color<D2>
|
||||
where Color<D1> : Clone + Into<Color<D2>> {
|
||||
where Color<D1>: Clone + Into<Color<D2>>
|
||||
{
|
||||
fn from(color: &Color<D1>) -> Self {
|
||||
color.clone().into()
|
||||
}
|
||||
@ -107,70 +112,82 @@ impl<C> From<Color<C>> for Color<Alpha<C>> {
|
||||
}
|
||||
|
||||
impl<D> From<Color<D>> for ComponentsOf<Color<D>>
|
||||
where D:HasComponents {
|
||||
where D: HasComponents
|
||||
{
|
||||
fn from(color: Color<D>) -> Self {
|
||||
color.data.into()
|
||||
}
|
||||
}
|
||||
|
||||
impl<D> From<ComponentsOf<D>> for Color<D>
|
||||
where D:HasComponentsRepr, ComponentsOf<D>:Into<D> {
|
||||
where
|
||||
D: HasComponentsRepr,
|
||||
ComponentsOf<D>: Into<D>,
|
||||
{
|
||||
fn from(components: ComponentsOf<D>) -> Self {
|
||||
Self { data: components.into() }
|
||||
}
|
||||
}
|
||||
|
||||
impl<D> From<Color<D>> for Vector3<f32>
|
||||
where Color<D> : HasComponents<ComponentsRepr=(f32,f32,f32)> {
|
||||
where Color<D>: HasComponents<ComponentsRepr = (f32, f32, f32)>
|
||||
{
|
||||
fn from(value: Color<D>) -> Self {
|
||||
Into::<Vector3<f32>>::into(value.into_components())
|
||||
}
|
||||
}
|
||||
|
||||
impl<D> From<Color<D>> for Vector4<f32>
|
||||
where Color<D> : HasComponents<ComponentsRepr=(f32,f32,f32,f32)> {
|
||||
where Color<D>: HasComponents<ComponentsRepr = (f32, f32, f32, f32)>
|
||||
{
|
||||
fn from(value: Color<D>) -> Self {
|
||||
Into::<Vector4<f32>>::into(value.into_components())
|
||||
}
|
||||
}
|
||||
|
||||
impl<D> From<&Color<D>> for Vector3<f32>
|
||||
where Color<D> : HasComponents<ComponentsRepr=(f32,f32,f32)> + Copy {
|
||||
where Color<D>: HasComponents<ComponentsRepr = (f32, f32, f32)> + Copy
|
||||
{
|
||||
fn from(value: &Color<D>) -> Self {
|
||||
Into::<Vector3<f32>>::into(value.into_components())
|
||||
}
|
||||
}
|
||||
|
||||
impl<D> From<&Color<D>> for Vector4<f32>
|
||||
where Color<D> : HasComponents<ComponentsRepr=(f32,f32,f32,f32)> + Copy {
|
||||
where Color<D>: HasComponents<ComponentsRepr = (f32, f32, f32, f32)> + Copy
|
||||
{
|
||||
fn from(value: &Color<D>) -> Self {
|
||||
Into::<Vector4<f32>>::into(value.into_components())
|
||||
}
|
||||
}
|
||||
|
||||
impl<D> From<Vector3<f32>> for Color<D>
|
||||
where D : HasComponents<ComponentsRepr=(f32,f32,f32)> {
|
||||
where D: HasComponents<ComponentsRepr = (f32, f32, f32)>
|
||||
{
|
||||
fn from(t: Vector3<f32>) -> Self {
|
||||
Self::from(t.into_components())
|
||||
}
|
||||
}
|
||||
|
||||
impl<D> From<Vector4<f32>> for Color<D>
|
||||
where D : HasComponents<ComponentsRepr=(f32,f32,f32,f32)> {
|
||||
where D: HasComponents<ComponentsRepr = (f32, f32, f32, f32)>
|
||||
{
|
||||
fn from(t: Vector4<f32>) -> Self {
|
||||
Self::from(t.into_components())
|
||||
}
|
||||
}
|
||||
|
||||
impl<D> From<&Vector3<f32>> for Color<D>
|
||||
where D : Copy + HasComponents<ComponentsRepr=(f32,f32,f32)> {
|
||||
where D: Copy + HasComponents<ComponentsRepr = (f32, f32, f32)>
|
||||
{
|
||||
fn from(t: &Vector3<f32>) -> Self {
|
||||
Self::from((*t).into_components())
|
||||
}
|
||||
}
|
||||
|
||||
impl<D> From<&Vector4<f32>> for Color<D>
|
||||
where D : Copy + HasComponents<ComponentsRepr=(f32,f32,f32,f32)> {
|
||||
where D: Copy + HasComponents<ComponentsRepr = (f32, f32, f32, f32)>
|
||||
{
|
||||
fn from(t: &Vector4<f32>) -> Self {
|
||||
Self::from((*t).into_components())
|
||||
}
|
||||
@ -249,20 +266,29 @@ pub struct Alpha<C> {
|
||||
// === Component Generics ===
|
||||
|
||||
impl<C> HasComponentsRepr for Alpha<C>
|
||||
where C:HasComponentsRepr, ComponentsReprOf<C>:PushBack<f32> {
|
||||
where
|
||||
C: HasComponentsRepr,
|
||||
ComponentsReprOf<C>: PushBack<f32>,
|
||||
{
|
||||
type ComponentsRepr = <ComponentsReprOf<C> as PushBack<f32>>::Output;
|
||||
}
|
||||
|
||||
impl<C> From<Alpha<C>> for ComponentsOf<Alpha<C>>
|
||||
where C:HasComponents, ComponentsReprOf<C>:PushBack<f32> {
|
||||
where
|
||||
C: HasComponents,
|
||||
ComponentsReprOf<C>: PushBack<f32>,
|
||||
{
|
||||
fn from(t: Alpha<C>) -> Self {
|
||||
t.opaque.data.into().push_back(t.alpha)
|
||||
}
|
||||
}
|
||||
|
||||
impl<C> From<ComponentsOf<Alpha<C>>> for Alpha<C>
|
||||
where C:HasComponents, ComponentsReprOf<C>:PushBack<f32>,
|
||||
<ComponentsReprOf<C> as PushBack<f32>>::Output : PopBack<Last=f32,Init=ComponentsReprOf<C>> {
|
||||
where
|
||||
C: HasComponents,
|
||||
ComponentsReprOf<C>: PushBack<f32>,
|
||||
<ComponentsReprOf<C> as PushBack<f32>>::Output: PopBack<Last = f32, Init = ComponentsReprOf<C>>,
|
||||
{
|
||||
fn from(components: ComponentsOf<Self>) -> Self {
|
||||
let (alpha, init) = components.pop_back();
|
||||
let opaque = from_components(init);
|
||||
|
@ -2,8 +2,8 @@
|
||||
|
||||
use crate::prelude::*;
|
||||
|
||||
use crate::system::gpu::shader::glsl::Glsl;
|
||||
use crate::system::gpu::shader::glsl::traits::*;
|
||||
use crate::system::gpu::shader::glsl::Glsl;
|
||||
|
||||
|
||||
|
||||
@ -18,7 +18,7 @@ pub struct ControlPoint<Color> {
|
||||
/// Offset of the control point in [0..1] range.
|
||||
pub offset: f32,
|
||||
/// Color of this control point.
|
||||
pub color : Color
|
||||
pub color: Color,
|
||||
}
|
||||
|
||||
impl<Color> ControlPoint<Color> {
|
||||
@ -102,7 +102,7 @@ pub struct SdfSampler<Gradient> {
|
||||
/// The gradient slope modifier. Defines how fast the gradient values change.
|
||||
pub slope: Slope,
|
||||
/// The underlying gradient.
|
||||
pub gradient : Gradient
|
||||
pub gradient: Gradient,
|
||||
}
|
||||
|
||||
impl<Gradient> SdfSampler<Gradient> {
|
||||
|
@ -1,7 +1,7 @@
|
||||
//! Color mixing utilities.
|
||||
|
||||
use crate::prelude::*;
|
||||
use super::*;
|
||||
use crate::prelude::*;
|
||||
|
||||
use crate::data::mix;
|
||||
use crate::data::mix::Mixable;
|
||||
@ -33,7 +33,9 @@ macro_rules! define_mix_impls {
|
||||
|
||||
macro_rules! define_mix_impl_repr {
|
||||
($tp:ty => $via_tp:ty [$repr:ident]) => {
|
||||
impl Mixable for $tp { type Repr = $repr; }
|
||||
impl Mixable for $tp {
|
||||
type Repr = $repr;
|
||||
}
|
||||
|
||||
impl From<$tp> for mix::Space<$tp> {
|
||||
fn from(value: $tp) -> mix::Space<$tp> {
|
||||
@ -46,7 +48,7 @@ macro_rules! define_mix_impl_repr {
|
||||
<$via_tp>::from(t.value).into()
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
|
@ -14,11 +14,11 @@
|
||||
#![allow(clippy::unreadable_literal)]
|
||||
#![allow(clippy::excessive_precision)]
|
||||
|
||||
use super::def::*;
|
||||
use super::super::component::*;
|
||||
use super::super::data::*;
|
||||
use super::white_point::traits::*;
|
||||
use super::def::*;
|
||||
use super::white_point;
|
||||
use super::white_point::traits::*;
|
||||
|
||||
|
||||
|
||||
@ -101,7 +101,7 @@ macro_rules! color_convert_via {
|
||||
<Color<Alpha<$src>>>::from(src).into()
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@ -113,13 +113,19 @@ macro_rules! color_convert_via {
|
||||
/// More info: http://www.brucelindbloom.com/index.html?Eqn_RGB_XYZ_Matrix.html
|
||||
|
||||
fn into_linear(x: f32) -> f32 {
|
||||
if x <= 0.04045 { x / 12.92 }
|
||||
else { ((x + 0.055) / 1.055).powf(2.4) }
|
||||
if x <= 0.04045 {
|
||||
x / 12.92
|
||||
} else {
|
||||
((x + 0.055) / 1.055).powf(2.4)
|
||||
}
|
||||
}
|
||||
|
||||
fn from_linear(x: f32) -> f32 {
|
||||
if x <= 0.0031308 { x * 12.92 }
|
||||
else { x.powf(1.0/2.4) * 1.055 - 0.055 }
|
||||
if x <= 0.0031308 {
|
||||
x * 12.92
|
||||
} else {
|
||||
x.powf(1.0 / 2.4) * 1.055 - 0.055
|
||||
}
|
||||
}
|
||||
|
||||
color_conversion! {
|
||||
|
@ -1,8 +1,8 @@
|
||||
//! This module contains definitions of various color spaces, including `Rgb`, `Hsl`, `Lch`, etc.
|
||||
|
||||
use crate::prelude::*;
|
||||
use super::super::data::*;
|
||||
use super::super::component::*;
|
||||
use super::super::data::*;
|
||||
use crate::prelude::*;
|
||||
|
||||
|
||||
|
||||
@ -17,12 +17,15 @@ macro_rules! define_color_parsing {
|
||||
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||
let (head, args) = generic_parse(s)?;
|
||||
if &head != stringify!($name) {
|
||||
return Err(ParseError::new(format!("No '{}' header found.",stringify!($name))))
|
||||
return Err(ParseError::new(format!(
|
||||
"No '{}' header found.",
|
||||
stringify!($name)
|
||||
)));
|
||||
}
|
||||
Ok($name::from_slice(&args))
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
macro_rules! define_color_spaces {
|
||||
@ -73,8 +76,12 @@ macro_rules! define_color_spaces {
|
||||
}
|
||||
|
||||
impl<C> From<AnyFormat> for Color<C>
|
||||
where Rgb: Into<Color<C>>, Rgba: Into<Color<C>>,
|
||||
Lch: Into<Color<C>>, Lcha: Into<Color<C>> {
|
||||
where
|
||||
Rgb: Into<Color<C>>,
|
||||
Rgba: Into<Color<C>>,
|
||||
Lch: Into<Color<C>>,
|
||||
Lcha: Into<Color<C>>,
|
||||
{
|
||||
fn from(c: AnyFormat) -> Self {
|
||||
match c {
|
||||
AnyFormat::Rgb(t) => t.into(),
|
||||
@ -85,7 +92,7 @@ where Rgb: Into<Color<C>>, Rgba: Into<Color<C>>,
|
||||
// it requires a lot more conversions than we support currently. To be implemented
|
||||
// one day.
|
||||
// https://github.com/enso-org/ide/issues/1404
|
||||
_ => panic!("Not implemented.")
|
||||
_ => panic!("Not implemented."),
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -465,7 +472,9 @@ impl LabData {
|
||||
None
|
||||
} else {
|
||||
let mut hue = self.b.atan2(self.a) * 180.0 / std::f32::consts::PI;
|
||||
if hue < 0.0 { hue += 360.0 }
|
||||
if hue < 0.0 {
|
||||
hue += 360.0
|
||||
}
|
||||
Some(hue)
|
||||
}
|
||||
}
|
||||
@ -485,46 +494,110 @@ pub(crate) const LCH_MAX_CHROMA_IN_SRGB_IN_STD_EQUATIONS : usize = 120;
|
||||
|
||||
#[allow(missing_docs)]
|
||||
impl Lch {
|
||||
pub fn pink_hue () -> f32 { 0.0 } // approx. 0.0 degrees
|
||||
pub fn red_hue () -> f32 { 0.111 } // approx. 40.0 degrees
|
||||
pub fn orange_hue () -> f32 { 0.18 } // approx. 65.0 degrees
|
||||
pub fn yellow_hue () -> f32 { 0.236 } // approx. 85.0 degrees
|
||||
pub fn olive_hue () -> f32 { 0.291 } // approx. 105.0 degrees
|
||||
pub fn green_hue () -> f32 { 0.378 } // approx. 136.0 degrees
|
||||
pub fn blue_green_hue () -> f32 { 0.6 } // approx. 216.0 degrees
|
||||
pub fn blue_hue () -> f32 { 0.672 } // approx. 242.0 degrees
|
||||
pub fn violet_hue () -> f32 { 0.847 } // approx. 305.0 degrees
|
||||
pub fn pink_hue() -> f32 {
|
||||
0.0
|
||||
} // approx. 0.0 degrees
|
||||
pub fn red_hue() -> f32 {
|
||||
0.111
|
||||
} // approx. 40.0 degrees
|
||||
pub fn orange_hue() -> f32 {
|
||||
0.18
|
||||
} // approx. 65.0 degrees
|
||||
pub fn yellow_hue() -> f32 {
|
||||
0.236
|
||||
} // approx. 85.0 degrees
|
||||
pub fn olive_hue() -> f32 {
|
||||
0.291
|
||||
} // approx. 105.0 degrees
|
||||
pub fn green_hue() -> f32 {
|
||||
0.378
|
||||
} // approx. 136.0 degrees
|
||||
pub fn blue_green_hue() -> f32 {
|
||||
0.6
|
||||
} // approx. 216.0 degrees
|
||||
pub fn blue_hue() -> f32 {
|
||||
0.672
|
||||
} // approx. 242.0 degrees
|
||||
pub fn violet_hue() -> f32 {
|
||||
0.847
|
||||
} // approx. 305.0 degrees
|
||||
}
|
||||
|
||||
#[allow(missing_docs)]
|
||||
impl Lch {
|
||||
pub fn white () -> Lch { Lch::new(1.0,0.0,0.0) }
|
||||
pub fn black () -> Lch { Lch::new(0.0,0.0,0.0) }
|
||||
pub fn pink (l:f32, c:f32) -> Lch { Lch::new(l,c,Lch::pink_hue()) }
|
||||
pub fn red (l:f32, c:f32) -> Lch { Lch::new(l,c,Lch::red_hue()) }
|
||||
pub fn orange (l:f32, c:f32) -> Lch { Lch::new(l,c,Lch::orange_hue()) }
|
||||
pub fn yellow (l:f32, c:f32) -> Lch { Lch::new(l,c,Lch::yellow_hue()) }
|
||||
pub fn olive (l:f32, c:f32) -> Lch { Lch::new(l,c,Lch::olive_hue()) }
|
||||
pub fn green (l:f32, c:f32) -> Lch { Lch::new(l,c,Lch::green_hue()) }
|
||||
pub fn blue_green (l:f32, c:f32) -> Lch { Lch::new(l,c,Lch::blue_green_hue()) }
|
||||
pub fn blue (l:f32, c:f32) -> Lch { Lch::new(l,c,Lch::blue_hue()) }
|
||||
pub fn violet (l:f32, c:f32) -> Lch { Lch::new(l,c,Lch::violet_hue()) }
|
||||
pub fn white() -> Lch {
|
||||
Lch::new(1.0, 0.0, 0.0)
|
||||
}
|
||||
pub fn black() -> Lch {
|
||||
Lch::new(0.0, 0.0, 0.0)
|
||||
}
|
||||
pub fn pink(l: f32, c: f32) -> Lch {
|
||||
Lch::new(l, c, Lch::pink_hue())
|
||||
}
|
||||
pub fn red(l: f32, c: f32) -> Lch {
|
||||
Lch::new(l, c, Lch::red_hue())
|
||||
}
|
||||
pub fn orange(l: f32, c: f32) -> Lch {
|
||||
Lch::new(l, c, Lch::orange_hue())
|
||||
}
|
||||
pub fn yellow(l: f32, c: f32) -> Lch {
|
||||
Lch::new(l, c, Lch::yellow_hue())
|
||||
}
|
||||
pub fn olive(l: f32, c: f32) -> Lch {
|
||||
Lch::new(l, c, Lch::olive_hue())
|
||||
}
|
||||
pub fn green(l: f32, c: f32) -> Lch {
|
||||
Lch::new(l, c, Lch::green_hue())
|
||||
}
|
||||
pub fn blue_green(l: f32, c: f32) -> Lch {
|
||||
Lch::new(l, c, Lch::blue_green_hue())
|
||||
}
|
||||
pub fn blue(l: f32, c: f32) -> Lch {
|
||||
Lch::new(l, c, Lch::blue_hue())
|
||||
}
|
||||
pub fn violet(l: f32, c: f32) -> Lch {
|
||||
Lch::new(l, c, Lch::violet_hue())
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(missing_docs)]
|
||||
impl Lcha {
|
||||
pub fn transparent () -> Lcha { Lcha::new(0.0,0.0,0.0,0.0) }
|
||||
pub fn white () -> Lcha { Lch::white () . into() }
|
||||
pub fn black () -> Lcha { Lch::black () . into() }
|
||||
pub fn pink (l:f32, c:f32) -> Lcha { Lch::pink (l,c) . into() }
|
||||
pub fn red (l:f32, c:f32) -> Lcha { Lch::red (l,c) . into() }
|
||||
pub fn orange (l:f32, c:f32) -> Lcha { Lch::orange (l,c) . into() }
|
||||
pub fn yellow (l:f32, c:f32) -> Lcha { Lch::yellow (l,c) . into() }
|
||||
pub fn olive (l:f32, c:f32) -> Lcha { Lch::olive (l,c) . into() }
|
||||
pub fn green (l:f32, c:f32) -> Lcha { Lch::green (l,c) . into() }
|
||||
pub fn blue_green (l:f32, c:f32) -> Lcha { Lch::blue_green (l,c) . into() }
|
||||
pub fn blue (l:f32, c:f32) -> Lcha { Lch::blue (l,c) . into() }
|
||||
pub fn violet (l:f32, c:f32) -> Lcha { Lch::violet (l,c) . into() }
|
||||
pub fn transparent() -> Lcha {
|
||||
Lcha::new(0.0, 0.0, 0.0, 0.0)
|
||||
}
|
||||
pub fn white() -> Lcha {
|
||||
Lch::white().into()
|
||||
}
|
||||
pub fn black() -> Lcha {
|
||||
Lch::black().into()
|
||||
}
|
||||
pub fn pink(l: f32, c: f32) -> Lcha {
|
||||
Lch::pink(l, c).into()
|
||||
}
|
||||
pub fn red(l: f32, c: f32) -> Lcha {
|
||||
Lch::red(l, c).into()
|
||||
}
|
||||
pub fn orange(l: f32, c: f32) -> Lcha {
|
||||
Lch::orange(l, c).into()
|
||||
}
|
||||
pub fn yellow(l: f32, c: f32) -> Lcha {
|
||||
Lch::yellow(l, c).into()
|
||||
}
|
||||
pub fn olive(l: f32, c: f32) -> Lcha {
|
||||
Lch::olive(l, c).into()
|
||||
}
|
||||
pub fn green(l: f32, c: f32) -> Lcha {
|
||||
Lch::green(l, c).into()
|
||||
}
|
||||
pub fn blue_green(l: f32, c: f32) -> Lcha {
|
||||
Lch::blue_green(l, c).into()
|
||||
}
|
||||
pub fn blue(l: f32, c: f32) -> Lcha {
|
||||
Lch::blue(l, c).into()
|
||||
}
|
||||
pub fn violet(l: f32, c: f32) -> Lcha {
|
||||
Lch::violet(l, c).into()
|
||||
}
|
||||
|
||||
/// Convert the color to JavaScript representation.
|
||||
pub fn to_javascript_string(self) -> String {
|
||||
@ -576,16 +649,109 @@ impl Lcha {
|
||||
/// 0 12.5 25.0 37.5 50.0 62.5 75.0 75.5 100.0
|
||||
/// LIGHTNESS
|
||||
/// ```
|
||||
pub const LCH_MAX_LIGHTNESS_CHROMA_IN_SRGB_CORRELATION : &[(usize,usize)] =
|
||||
&[(0,0),(1,1),(2,2),(3,5),(4,5),(5,6),(6,8),(7,9),(8,10),(9,11),(10,12),(11,13),(12,13),(13,14)
|
||||
,(14,14),(15,15),(16,15),(17,16),(18,16),(19,17),(20,17),(21,18),(22,18),(23,18),(24,19)
|
||||
,(25,19),(26,20),(27,20),(28,21),(29,21),(30,22),(31,22),(32,23),(33,23),(34,24),(35,24)
|
||||
,(36,25),(37,25),(38,26),(39,26),(40,27),(41,27),(42,28),(43,28),(44,29),(45,29),(46,30)
|
||||
,(47,30),(48,31),(49,31),(50,32),(51,32),(52,33),(53,33),(54,34),(55,34),(56,35),(57,35)
|
||||
,(58,36),(59,36),(60,36),(61,37),(62,37),(63,38),(64,38),(65,39),(66,39),(67,40),(68,40)
|
||||
,(69,41),(70,41),(71,42),(72,42),(73,41),(74,39),(75,38),(76,36),(77,35),(78,33),(79,31)
|
||||
,(80,30),(81,28),(82,27),(83,25),(84,24),(85,22),(86,20),(87,19),(88,17),(89,16),(90,14)
|
||||
,(91,12),(92,11),(93,9),(94,8),(95,6),(96,5),(97,4),(98,2),(99,1),(100,0)];
|
||||
pub const LCH_MAX_LIGHTNESS_CHROMA_IN_SRGB_CORRELATION: &[(usize, usize)] = &[
|
||||
(0, 0),
|
||||
(1, 1),
|
||||
(2, 2),
|
||||
(3, 5),
|
||||
(4, 5),
|
||||
(5, 6),
|
||||
(6, 8),
|
||||
(7, 9),
|
||||
(8, 10),
|
||||
(9, 11),
|
||||
(10, 12),
|
||||
(11, 13),
|
||||
(12, 13),
|
||||
(13, 14),
|
||||
(14, 14),
|
||||
(15, 15),
|
||||
(16, 15),
|
||||
(17, 16),
|
||||
(18, 16),
|
||||
(19, 17),
|
||||
(20, 17),
|
||||
(21, 18),
|
||||
(22, 18),
|
||||
(23, 18),
|
||||
(24, 19),
|
||||
(25, 19),
|
||||
(26, 20),
|
||||
(27, 20),
|
||||
(28, 21),
|
||||
(29, 21),
|
||||
(30, 22),
|
||||
(31, 22),
|
||||
(32, 23),
|
||||
(33, 23),
|
||||
(34, 24),
|
||||
(35, 24),
|
||||
(36, 25),
|
||||
(37, 25),
|
||||
(38, 26),
|
||||
(39, 26),
|
||||
(40, 27),
|
||||
(41, 27),
|
||||
(42, 28),
|
||||
(43, 28),
|
||||
(44, 29),
|
||||
(45, 29),
|
||||
(46, 30),
|
||||
(47, 30),
|
||||
(48, 31),
|
||||
(49, 31),
|
||||
(50, 32),
|
||||
(51, 32),
|
||||
(52, 33),
|
||||
(53, 33),
|
||||
(54, 34),
|
||||
(55, 34),
|
||||
(56, 35),
|
||||
(57, 35),
|
||||
(58, 36),
|
||||
(59, 36),
|
||||
(60, 36),
|
||||
(61, 37),
|
||||
(62, 37),
|
||||
(63, 38),
|
||||
(64, 38),
|
||||
(65, 39),
|
||||
(66, 39),
|
||||
(67, 40),
|
||||
(68, 40),
|
||||
(69, 41),
|
||||
(70, 41),
|
||||
(71, 42),
|
||||
(72, 42),
|
||||
(73, 41),
|
||||
(74, 39),
|
||||
(75, 38),
|
||||
(76, 36),
|
||||
(77, 35),
|
||||
(78, 33),
|
||||
(79, 31),
|
||||
(80, 30),
|
||||
(81, 28),
|
||||
(82, 27),
|
||||
(83, 25),
|
||||
(84, 24),
|
||||
(85, 22),
|
||||
(86, 20),
|
||||
(87, 19),
|
||||
(88, 17),
|
||||
(89, 16),
|
||||
(90, 14),
|
||||
(91, 12),
|
||||
(92, 11),
|
||||
(93, 9),
|
||||
(94, 8),
|
||||
(95, 6),
|
||||
(96, 5),
|
||||
(97, 4),
|
||||
(98, 2),
|
||||
(99, 1),
|
||||
(100, 0),
|
||||
];
|
||||
|
||||
lazy_static! {
|
||||
/// Map from LCH lightness to max chroma, so every hue value will be included in the sRGB color
|
||||
@ -646,7 +812,7 @@ fn lch_lightness_to_max_chroma_in_srgb(l:f32) -> f32 {
|
||||
#[derive(Debug, Clone)]
|
||||
#[allow(missing_docs)]
|
||||
pub struct ParseError {
|
||||
pub reason:String
|
||||
pub reason: String,
|
||||
}
|
||||
|
||||
impl ParseError {
|
||||
@ -677,8 +843,7 @@ fn generic_parse(s:&str) -> Result<(String,Vec<f32>),ParseError> {
|
||||
let mut splitter = s.splitn(2, '(');
|
||||
match splitter.next() {
|
||||
None => Err(ParseError::new("Empty input.")),
|
||||
Some(head) => {
|
||||
match splitter.next() {
|
||||
Some(head) => match splitter.next() {
|
||||
None => Err(ParseError::new("No arguments provided.")),
|
||||
Some(rest) => {
|
||||
let head = uppercase_first_letter(&head.to_lowercase());
|
||||
@ -692,7 +857,6 @@ fn generic_parse(s:&str) -> Result<(String,Vec<f32>),ParseError> {
|
||||
Ok((head, args))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
|
@ -9,8 +9,8 @@
|
||||
//! Note that this module implements currently only `D65` white point. In case other will be needed
|
||||
//! they can be easily ported from the Rust `Palette` library implementation.
|
||||
|
||||
use super::def::*;
|
||||
use super::super::component::*;
|
||||
use super::def::*;
|
||||
|
||||
|
||||
/// Common traits.
|
||||
|
@ -28,7 +28,7 @@ pub trait AddMut<T> {
|
||||
#[derive(Debug)]
|
||||
pub struct CachingIterator<T: Clone, It: Iterator<Item = T>> {
|
||||
last: Option<T>,
|
||||
iter : It
|
||||
iter: It,
|
||||
}
|
||||
|
||||
impl<T: Clone, It: Iterator<Item = T>> Iterator for CachingIterator<T, It> {
|
||||
@ -58,10 +58,7 @@ impl<T : Clone, It : Iterator<Item=T>> IntoCachingIterator for It {
|
||||
type Iter = Self;
|
||||
|
||||
fn cache_last_value(self) -> CachingIterator<Self::Item, Self::Iter> {
|
||||
CachingIterator {
|
||||
last : None,
|
||||
iter : self
|
||||
}
|
||||
CachingIterator { last: None, iter: self }
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -24,29 +24,57 @@ pub mod traits {
|
||||
use super::*;
|
||||
|
||||
// === Arg ===
|
||||
pub trait HasArg { type Arg; }
|
||||
pub trait HasArg {
|
||||
type Arg;
|
||||
}
|
||||
pub type Arg<T> = <T as HasArg>::Arg;
|
||||
|
||||
// === Global Operations ===
|
||||
pub trait HasCheckAll { fn check_all (&self) -> bool; }
|
||||
pub trait HasUnsetAll { fn unset_all (&mut self); }
|
||||
pub trait HasCheckAll {
|
||||
fn check_all(&self) -> bool;
|
||||
}
|
||||
pub trait HasUnsetAll {
|
||||
fn unset_all(&mut self);
|
||||
}
|
||||
|
||||
// === Arity-0 Operations ===
|
||||
pub trait HasCheck0 { fn check (&self) -> bool; }
|
||||
pub trait HasSet0 { fn set (&mut self); }
|
||||
pub trait HasUnset0 { fn unset (&mut self); }
|
||||
pub trait HasCheck0 {
|
||||
fn check(&self) -> bool;
|
||||
}
|
||||
pub trait HasSet0 {
|
||||
fn set(&mut self);
|
||||
}
|
||||
pub trait HasUnset0 {
|
||||
fn unset(&mut self);
|
||||
}
|
||||
|
||||
// === Arity-1 Operations ===
|
||||
pub trait HasCheck1 : HasArg { fn check (& self, arg: &Self::Arg) -> bool; }
|
||||
pub trait HasSet1 : HasArg { fn set (&mut self, arg: Self::Arg); }
|
||||
pub trait HasUnset1 : HasArg { fn unset (&mut self, arg: &Self::Arg); }
|
||||
pub trait HasCheck1: HasArg {
|
||||
fn check(&self, arg: &Self::Arg) -> bool;
|
||||
}
|
||||
pub trait HasSet1: HasArg {
|
||||
fn set(&mut self, arg: Self::Arg);
|
||||
}
|
||||
pub trait HasUnset1: HasArg {
|
||||
fn unset(&mut self, arg: &Self::Arg);
|
||||
}
|
||||
|
||||
// === Shared Operations ===
|
||||
pub trait SharedHasUnsetAll { fn unset_all (&self); }
|
||||
pub trait SharedHasSet0 { fn set (&self); }
|
||||
pub trait SharedHasUnset0 { fn unset (&self); }
|
||||
pub trait SharedHasSet1 : HasArg { fn set (&self, arg: Self::Arg); }
|
||||
pub trait SharedHasUnset1 : HasArg { fn unset (&self, arg:&Self::Arg); }
|
||||
pub trait SharedHasUnsetAll {
|
||||
fn unset_all(&self);
|
||||
}
|
||||
pub trait SharedHasSet0 {
|
||||
fn set(&self);
|
||||
}
|
||||
pub trait SharedHasUnset0 {
|
||||
fn unset(&self);
|
||||
}
|
||||
pub trait SharedHasSet1: HasArg {
|
||||
fn set(&self, arg: Self::Arg);
|
||||
}
|
||||
pub trait SharedHasUnset1: HasArg {
|
||||
fn unset(&self, arg: &Self::Arg);
|
||||
}
|
||||
|
||||
// === Type Aliases ===
|
||||
pub trait DirtyFlagOps = Debug + HasCheckAll + HasUnsetAll;
|
||||
@ -93,36 +121,35 @@ impl<OnMut,T:Default> DirtyFlag<T,OnMut> {
|
||||
|
||||
// === Arguments ===
|
||||
|
||||
impl<T:HasArg,OnMut>
|
||||
HasArg for DirtyFlag<T,OnMut> {
|
||||
impl<T: HasArg, OnMut> HasArg for DirtyFlag<T, OnMut> {
|
||||
type Arg = Arg<T>;
|
||||
}
|
||||
|
||||
|
||||
// === Global Operations ===
|
||||
|
||||
impl<T:HasCheckAll,OnMut>
|
||||
HasCheckAll for DirtyFlag<T,OnMut> {
|
||||
fn check_all(&self) -> bool { self.data.check_all() }
|
||||
impl<T: HasCheckAll, OnMut> HasCheckAll for DirtyFlag<T, OnMut> {
|
||||
fn check_all(&self) -> bool {
|
||||
self.data.check_all()
|
||||
}
|
||||
}
|
||||
|
||||
impl<T:HasUnsetAll,OnMut>
|
||||
HasUnsetAll for DirtyFlag<T,OnMut> {
|
||||
fn unset_all(&mut self) { self.data.unset_all() }
|
||||
impl<T: HasUnsetAll, OnMut> HasUnsetAll for DirtyFlag<T, OnMut> {
|
||||
fn unset_all(&mut self) {
|
||||
self.data.unset_all()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// === Check ===
|
||||
|
||||
impl<T:DirtyFlagOps0,OnMut>
|
||||
HasCheck0 for DirtyFlag<T,OnMut> {
|
||||
impl<T: DirtyFlagOps0, OnMut> HasCheck0 for DirtyFlag<T, OnMut> {
|
||||
fn check(&self) -> bool {
|
||||
self.data.check()
|
||||
}
|
||||
}
|
||||
|
||||
impl<T:DirtyFlagOps1,OnMut>
|
||||
HasCheck1 for DirtyFlag<T,OnMut> {
|
||||
impl<T: DirtyFlagOps1, OnMut> HasCheck1 for DirtyFlag<T, OnMut> {
|
||||
fn check(&self, arg: &Self::Arg) -> bool {
|
||||
self.data.check(arg)
|
||||
}
|
||||
@ -131,8 +158,7 @@ HasCheck1 for DirtyFlag<T,OnMut> {
|
||||
|
||||
// === Set ===
|
||||
|
||||
impl<T:DirtyFlagOps0,OnMut:FnMut0>
|
||||
HasSet0 for DirtyFlag<T,OnMut> {
|
||||
impl<T: DirtyFlagOps0, OnMut: FnMut0> HasSet0 for DirtyFlag<T, OnMut> {
|
||||
fn set(&mut self) {
|
||||
let is_set = self.data.check_all();
|
||||
if !is_set {
|
||||
@ -144,15 +170,16 @@ HasSet0 for DirtyFlag<T,OnMut> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<T:DirtyFlagOps1,OnMut:FnMut0>
|
||||
HasSet1 for DirtyFlag<T,OnMut> {
|
||||
impl<T: DirtyFlagOps1, OnMut: FnMut0> HasSet1 for DirtyFlag<T, OnMut> {
|
||||
fn set(&mut self, arg: Self::Arg) {
|
||||
let first_set = !self.check_all();
|
||||
let is_set = self.data.check(&arg);
|
||||
if !is_set {
|
||||
self.data.set(arg);
|
||||
debug!(self.logger, "Setting to {self.data:?}.", || {
|
||||
if first_set { self.on_set.call(); }
|
||||
if first_set {
|
||||
self.on_set.call();
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
@ -161,17 +188,16 @@ HasSet1 for DirtyFlag<T,OnMut> {
|
||||
|
||||
// === Unset ===
|
||||
|
||||
impl<T:HasUnset0,OnMut>
|
||||
HasUnset0 for DirtyFlag<T,OnMut> {
|
||||
impl<T: HasUnset0, OnMut> HasUnset0 for DirtyFlag<T, OnMut> {
|
||||
fn unset(&mut self) {
|
||||
info!(self.logger, "Unsetting.");
|
||||
self.data.unset()
|
||||
}
|
||||
}
|
||||
|
||||
impl<T:HasUnset1,OnMut>
|
||||
HasUnset1 for DirtyFlag<T,OnMut>
|
||||
where Arg<T>:Display {
|
||||
impl<T: HasUnset1, OnMut> HasUnset1 for DirtyFlag<T, OnMut>
|
||||
where Arg<T>: Display
|
||||
{
|
||||
fn unset(&mut self, arg: &Self::Arg) {
|
||||
info!(self.logger, "Unsetting {arg}.");
|
||||
self.data.unset(arg)
|
||||
@ -192,14 +218,13 @@ HasUnset1 for DirtyFlag<T,OnMut>
|
||||
#[derivative(Debug(bound = "T:Debug"))]
|
||||
#[derivative(Clone(bound = ""))]
|
||||
pub struct SharedDirtyFlag<T, OnMut> {
|
||||
rc: Rc<RefCell<DirtyFlag<T,OnMut>>>
|
||||
rc: Rc<RefCell<DirtyFlag<T, OnMut>>>,
|
||||
}
|
||||
|
||||
|
||||
// === API ===
|
||||
|
||||
impl<T:Default,OnMut>
|
||||
SharedDirtyFlag<T,OnMut> {
|
||||
impl<T: Default, OnMut> SharedDirtyFlag<T, OnMut> {
|
||||
pub fn new(logger: Logger, on_set: OnMut) -> Self {
|
||||
Self { rc: Rc::new(RefCell::new(DirtyFlag::new(logger, on_set))) }
|
||||
}
|
||||
@ -209,22 +234,19 @@ SharedDirtyFlag<T,OnMut> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<T,OnMut>
|
||||
SharedDirtyFlag<T,OnMut> {
|
||||
impl<T, OnMut> SharedDirtyFlag<T, OnMut> {
|
||||
pub fn clone_ref(&self) -> Self {
|
||||
self.clone()
|
||||
}
|
||||
}
|
||||
|
||||
impl<T,OnMut>
|
||||
SharedDirtyFlag<T,OnMut> {
|
||||
impl<T, OnMut> SharedDirtyFlag<T, OnMut> {
|
||||
pub fn set_callback(&self, on_set: OnMut) {
|
||||
self.rc.borrow_mut().on_set = on_set;
|
||||
}
|
||||
}
|
||||
|
||||
impl<T,OnMut>
|
||||
From<Rc<RefCell<DirtyFlag<T,OnMut>>>> for SharedDirtyFlag<T,OnMut> {
|
||||
impl<T, OnMut> From<Rc<RefCell<DirtyFlag<T, OnMut>>>> for SharedDirtyFlag<T, OnMut> {
|
||||
fn from(rc: Rc<RefCell<DirtyFlag<T, OnMut>>>) -> Self {
|
||||
Self { rc }
|
||||
}
|
||||
@ -240,15 +262,13 @@ impl<T:HasArg,OnMut> HasArg for SharedDirtyFlag<T,OnMut> {
|
||||
|
||||
// === Global Operations ===
|
||||
|
||||
impl<T:HasUnsetAll,OnMut>
|
||||
SharedHasUnsetAll for SharedDirtyFlag<T,OnMut> {
|
||||
impl<T: HasUnsetAll, OnMut> SharedHasUnsetAll for SharedDirtyFlag<T, OnMut> {
|
||||
fn unset_all(&self) {
|
||||
self.rc.borrow_mut().unset_all()
|
||||
}
|
||||
}
|
||||
|
||||
impl<T:HasCheckAll,OnMut>
|
||||
HasCheckAll for SharedDirtyFlag<T,OnMut> {
|
||||
impl<T: HasCheckAll, OnMut> HasCheckAll for SharedDirtyFlag<T, OnMut> {
|
||||
fn check_all(&self) -> bool {
|
||||
self.rc.borrow().check_all()
|
||||
}
|
||||
@ -256,39 +276,43 @@ HasCheckAll for SharedDirtyFlag<T,OnMut> {
|
||||
|
||||
// === Check ===
|
||||
|
||||
impl<T:DirtyFlagOps0,OnMut>
|
||||
HasCheck0 for SharedDirtyFlag<T,OnMut> {
|
||||
fn check (&self) -> bool { self.rc.borrow().check() }
|
||||
impl<T: DirtyFlagOps0, OnMut> HasCheck0 for SharedDirtyFlag<T, OnMut> {
|
||||
fn check(&self) -> bool {
|
||||
self.rc.borrow().check()
|
||||
}
|
||||
}
|
||||
|
||||
impl<T:DirtyFlagOps1,OnMut>
|
||||
HasCheck1 for SharedDirtyFlag<T,OnMut> {
|
||||
fn check (&self, arg:&Arg<T>) -> bool { self.rc.borrow().check(arg) }
|
||||
impl<T: DirtyFlagOps1, OnMut> HasCheck1 for SharedDirtyFlag<T, OnMut> {
|
||||
fn check(&self, arg: &Arg<T>) -> bool {
|
||||
self.rc.borrow().check(arg)
|
||||
}
|
||||
}
|
||||
|
||||
// === Set ===
|
||||
|
||||
impl<T:DirtyFlagOps0,OnMut:FnMut0>
|
||||
SharedHasSet0 for SharedDirtyFlag<T,OnMut> {
|
||||
fn set (&self) { self.rc.borrow_mut().set() }
|
||||
impl<T: DirtyFlagOps0, OnMut: FnMut0> SharedHasSet0 for SharedDirtyFlag<T, OnMut> {
|
||||
fn set(&self) {
|
||||
self.rc.borrow_mut().set()
|
||||
}
|
||||
}
|
||||
|
||||
impl<T:DirtyFlagOps1,OnMut:FnMut0>
|
||||
SharedHasSet1 for SharedDirtyFlag<T,OnMut> {
|
||||
fn set (&self, arg: Arg<T>) { self.rc.borrow_mut().set(arg) }
|
||||
impl<T: DirtyFlagOps1, OnMut: FnMut0> SharedHasSet1 for SharedDirtyFlag<T, OnMut> {
|
||||
fn set(&self, arg: Arg<T>) {
|
||||
self.rc.borrow_mut().set(arg)
|
||||
}
|
||||
}
|
||||
|
||||
// === Unset ===
|
||||
|
||||
impl<T:HasUnset0,OnMut>
|
||||
SharedHasUnset0 for SharedDirtyFlag<T,OnMut> {
|
||||
impl<T: HasUnset0, OnMut> SharedHasUnset0 for SharedDirtyFlag<T, OnMut> {
|
||||
fn unset(&self) {
|
||||
self.rc.borrow_mut().unset()
|
||||
}
|
||||
}
|
||||
|
||||
impl<T:HasUnset1,OnMut>
|
||||
SharedHasUnset1 for SharedDirtyFlag<T,OnMut> where Arg<T>:Display {
|
||||
impl<T: HasUnset1, OnMut> SharedHasUnset1 for SharedDirtyFlag<T, OnMut>
|
||||
where Arg<T>: Display
|
||||
{
|
||||
fn unset(&self, arg: &Self::Arg) {
|
||||
self.rc.borrow_mut().unset(arg)
|
||||
}
|
||||
@ -312,12 +336,34 @@ pub type SharedBool <OnMut=()> = SharedDirtyFlag <BoolData,OnMut>;
|
||||
pub trait BoolCtx<OnMut> = where OnMut: FnMut0;
|
||||
|
||||
#[derive(Clone, Copy, Debug, Display, Default)]
|
||||
pub struct BoolData { is_dirty: bool }
|
||||
impl HasCheckAll for BoolData { fn check_all (&self) -> bool { self.is_dirty } }
|
||||
impl HasUnsetAll for BoolData { fn unset_all (&mut self) { self.is_dirty = false } }
|
||||
impl HasCheck0 for BoolData { fn check (& self) -> bool { self.is_dirty } }
|
||||
impl HasSet0 for BoolData { fn set (&mut self) { self.is_dirty = true } }
|
||||
impl HasUnset0 for BoolData { fn unset (&mut self) { self.is_dirty = false } }
|
||||
pub struct BoolData {
|
||||
is_dirty: bool,
|
||||
}
|
||||
impl HasCheckAll for BoolData {
|
||||
fn check_all(&self) -> bool {
|
||||
self.is_dirty
|
||||
}
|
||||
}
|
||||
impl HasUnsetAll for BoolData {
|
||||
fn unset_all(&mut self) {
|
||||
self.is_dirty = false
|
||||
}
|
||||
}
|
||||
impl HasCheck0 for BoolData {
|
||||
fn check(&self) -> bool {
|
||||
self.is_dirty
|
||||
}
|
||||
}
|
||||
impl HasSet0 for BoolData {
|
||||
fn set(&mut self) {
|
||||
self.is_dirty = true
|
||||
}
|
||||
}
|
||||
impl HasUnset0 for BoolData {
|
||||
fn unset(&mut self) {
|
||||
self.is_dirty = false
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
@ -334,11 +380,23 @@ pub trait RangeCtx <OnMut> = where OnMut:FnMut0;
|
||||
pub trait RangeIx = PartialOrd + Copy + Debug;
|
||||
|
||||
#[derive(Debug, Default)]
|
||||
pub struct RangeData<Ix=usize> { pub range: Option<RangeInclusive<Ix>> }
|
||||
pub struct RangeData<Ix = usize> {
|
||||
pub range: Option<RangeInclusive<Ix>>,
|
||||
}
|
||||
|
||||
impl<Ix> HasArg for RangeData<Ix> { type Arg = Ix; }
|
||||
impl<Ix> HasCheckAll for RangeData<Ix> { fn check_all(&self) -> bool { self.range.is_some() } }
|
||||
impl<Ix> HasUnsetAll for RangeData<Ix> { fn unset_all(&mut self) { self.range = None } }
|
||||
impl<Ix> HasArg for RangeData<Ix> {
|
||||
type Arg = Ix;
|
||||
}
|
||||
impl<Ix> HasCheckAll for RangeData<Ix> {
|
||||
fn check_all(&self) -> bool {
|
||||
self.range.is_some()
|
||||
}
|
||||
}
|
||||
impl<Ix> HasUnsetAll for RangeData<Ix> {
|
||||
fn unset_all(&mut self) {
|
||||
self.range = None
|
||||
}
|
||||
}
|
||||
|
||||
impl<Ix: RangeIx> HasCheck1 for RangeData<Ix> {
|
||||
fn check(&self, ix: &Ix) -> bool {
|
||||
@ -350,11 +408,14 @@ impl<Ix:RangeIx> HasSet1 for RangeData<Ix> {
|
||||
fn set(&mut self, ix: Ix) {
|
||||
self.range = match &self.range {
|
||||
None => Some(ix..=ix),
|
||||
Some(r) => {
|
||||
if ix < *r.start() { Some (ix ..= *r.end()) }
|
||||
else if ix > *r.end() { Some (*r.start() ..= ix) }
|
||||
else { Some (r.clone()) }
|
||||
}
|
||||
Some(r) =>
|
||||
if ix < *r.start() {
|
||||
Some(ix..=*r.end())
|
||||
} else if ix > *r.end() {
|
||||
Some(*r.start()..=ix)
|
||||
} else {
|
||||
Some(r.clone())
|
||||
},
|
||||
};
|
||||
}
|
||||
}
|
||||
@ -365,8 +426,12 @@ impl<Ix:RangeIx> HasUnset1 for RangeData<Ix> {
|
||||
|
||||
impl<Ix: RangeIx> Display for RangeData<Ix> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
write!(f, "{}", self.range.as_ref().map(|t|
|
||||
format!("[{:?}...{:?}]",t.start(),t.end()))
|
||||
write!(
|
||||
f,
|
||||
"{}",
|
||||
self.range
|
||||
.as_ref()
|
||||
.map(|t| format!("[{:?}...{:?}]", t.start(), t.end()))
|
||||
.unwrap_or_else(|| "false".into())
|
||||
)
|
||||
}
|
||||
@ -390,11 +455,23 @@ pub trait SetItem = Eq + Hash + Debug;
|
||||
#[derive(Derivative, Shrinkwrap)]
|
||||
#[derivative(Debug(bound = "Item:SetItem"))]
|
||||
#[derivative(Default(bound = "Item:SetItem"))]
|
||||
pub struct SetData<Item> { pub set: FxHashSet<Item> }
|
||||
pub struct SetData<Item> {
|
||||
pub set: FxHashSet<Item>,
|
||||
}
|
||||
|
||||
impl<Item> HasArg for SetData<Item> { type Arg = Item; }
|
||||
impl<Item> HasCheckAll for SetData<Item> { fn check_all(&self) -> bool { !self.set.is_empty() } }
|
||||
impl<Item> HasUnsetAll for SetData<Item> { fn unset_all(&mut self) { self.set.clear(); } }
|
||||
impl<Item> HasArg for SetData<Item> {
|
||||
type Arg = Item;
|
||||
}
|
||||
impl<Item> HasCheckAll for SetData<Item> {
|
||||
fn check_all(&self) -> bool {
|
||||
!self.set.is_empty()
|
||||
}
|
||||
}
|
||||
impl<Item> HasUnsetAll for SetData<Item> {
|
||||
fn unset_all(&mut self) {
|
||||
self.set.clear();
|
||||
}
|
||||
}
|
||||
|
||||
impl<Item: SetItem> HasCheck1 for SetData<Item> {
|
||||
fn check(&self, a: &Item) -> bool {
|
||||
@ -441,11 +518,23 @@ pub trait VectorItem = Debug + PartialEq;
|
||||
|
||||
#[derive(Derivative, Debug, Shrinkwrap)]
|
||||
#[derivative(Default(bound = ""))]
|
||||
pub struct VectorData<Item> { pub vec: Vec<Item> }
|
||||
pub struct VectorData<Item> {
|
||||
pub vec: Vec<Item>,
|
||||
}
|
||||
|
||||
impl<Item> HasArg for VectorData<Item> { type Arg = Item; }
|
||||
impl<Item> HasCheckAll for VectorData<Item> { fn check_all(&self) -> bool { !self.vec.is_empty() } }
|
||||
impl<Item> HasUnsetAll for VectorData<Item> { fn unset_all(&mut self) { self.vec.clear(); } }
|
||||
impl<Item> HasArg for VectorData<Item> {
|
||||
type Arg = Item;
|
||||
}
|
||||
impl<Item> HasCheckAll for VectorData<Item> {
|
||||
fn check_all(&self) -> bool {
|
||||
!self.vec.is_empty()
|
||||
}
|
||||
}
|
||||
impl<Item> HasUnsetAll for VectorData<Item> {
|
||||
fn unset_all(&mut self) {
|
||||
self.vec.clear();
|
||||
}
|
||||
}
|
||||
|
||||
impl<Item: PartialEq> HasCheck1 for VectorData<Item> {
|
||||
fn check(&self, a: &Item) -> bool {
|
||||
@ -507,7 +596,7 @@ pub type SharedBitField <Prim,OnMut> = SharedEnum <Prim,usize,OnMut>;
|
||||
#[derivative(Default(bound = "Prim:Default"))]
|
||||
pub struct EnumData<Prim = u32, T = usize> {
|
||||
pub bits: Prim,
|
||||
phantom : PhantomData<T>
|
||||
phantom: PhantomData<T>,
|
||||
}
|
||||
|
||||
impl<Prim, T> HasArg for EnumData<Prim, T> {
|
||||
|
@ -41,7 +41,8 @@
|
||||
/// }
|
||||
///
|
||||
/// impl<T, T1> FnMut1<T1> for Option<T>
|
||||
/// where T: FnMut1<T1> {
|
||||
/// where T: FnMut1<T1>
|
||||
/// {
|
||||
/// type Output = Option<T::Output>;
|
||||
/// fn call(&mut self, t1: T1) -> Self::Output {
|
||||
/// match self {
|
||||
@ -52,14 +53,14 @@
|
||||
/// }
|
||||
///
|
||||
/// impl<F, T, T1> FnMut1<T1> for F
|
||||
/// where F: FnMut(T1) -> T {
|
||||
/// where F: FnMut(T1) -> T
|
||||
/// {
|
||||
/// type Output = T;
|
||||
/// fn call(&mut self, t1: T1) -> Self::Output {
|
||||
/// self(t1)
|
||||
/// }
|
||||
/// }
|
||||
/// ```
|
||||
///
|
||||
|
||||
macro_rules! define_fn {
|
||||
($( [$($mut:ident)?] $fn_name:ident $name:ident $(<$($arg:ident),*>)?; )*) => {$(
|
||||
|
@ -14,7 +14,7 @@ use crate::prelude::*;
|
||||
/// Strongly typed value representation in the mix space.
|
||||
#[allow(missing_docs)]
|
||||
pub struct Space<T: Mixable> {
|
||||
pub value : Repr<T>
|
||||
pub value: Repr<T>,
|
||||
}
|
||||
|
||||
impl<T: Mixable> Space<T> {
|
||||
@ -25,7 +25,10 @@ impl<T:Mixable> Space<T> {
|
||||
}
|
||||
|
||||
impl<T: Mixable> Debug for Space<T>
|
||||
where T:Mixable, Repr<T>:Debug {
|
||||
where
|
||||
T: Mixable,
|
||||
Repr<T>: Debug,
|
||||
{
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
write!(f, "Space({:?})", self.value)
|
||||
}
|
||||
|
@ -1,8 +1,8 @@
|
||||
#![allow(missing_docs)]
|
||||
|
||||
use crate::prelude::*;
|
||||
use crate::data::function::traits::FnMut0;
|
||||
use crate::data::function::traits::FnMut1;
|
||||
use crate::prelude::*;
|
||||
|
||||
|
||||
|
||||
@ -24,16 +24,14 @@ pub struct Observable<T,OnMut,OnResize> {
|
||||
pub on_resize: OnResize,
|
||||
}
|
||||
|
||||
impl<T:Default,OnMut,OnResize>
|
||||
Observable<T,OnMut,OnResize> {
|
||||
impl<T: Default, OnMut, OnResize> Observable<T, OnMut, OnResize> {
|
||||
pub fn new(on_mut: OnMut, on_resize: OnResize) -> Self {
|
||||
let data = default();
|
||||
Self { data, on_mut, on_resize }
|
||||
}
|
||||
}
|
||||
|
||||
impl<T:Index<Ix>,OnMut,OnResize,Ix>
|
||||
Index<Ix> for Observable<T,OnMut,OnResize> {
|
||||
impl<T: Index<Ix>, OnMut, OnResize, Ix> Index<Ix> for Observable<T, OnMut, OnResize> {
|
||||
type Output = <T as Index<Ix>>::Output;
|
||||
#[inline]
|
||||
fn index(&self, index: Ix) -> &Self::Output {
|
||||
@ -41,8 +39,9 @@ Index<Ix> for Observable<T,OnMut,OnResize> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<T:IndexMut<Ix>, OnMut: FnMut1<Ix> ,OnResize, Ix:Copy>
|
||||
IndexMut<Ix> for Observable<T,OnMut,OnResize> {
|
||||
impl<T: IndexMut<Ix>, OnMut: FnMut1<Ix>, OnResize, Ix: Copy> IndexMut<Ix>
|
||||
for Observable<T, OnMut, OnResize>
|
||||
{
|
||||
#[inline]
|
||||
fn index_mut(&mut self, index: Ix) -> &mut Self::Output {
|
||||
self.on_mut.call(index);
|
||||
@ -50,8 +49,7 @@ IndexMut<Ix> for Observable<T,OnMut,OnResize> {
|
||||
}
|
||||
}
|
||||
|
||||
impl <T:Extend<S>,S,OnMut,OnResize:FnMut0>
|
||||
Extend<S> for Observable<T,OnMut,OnResize> {
|
||||
impl<T: Extend<S>, S, OnMut, OnResize: FnMut0> Extend<S> for Observable<T, OnMut, OnResize> {
|
||||
#[inline]
|
||||
fn extend<I: IntoIterator<Item = S>>(&mut self, iter: I) {
|
||||
self.on_resize.call();
|
||||
|
@ -10,9 +10,9 @@ use js_sys::ArrayBuffer;
|
||||
use js_sys::WebAssembly::Memory;
|
||||
use std::collections::VecDeque;
|
||||
use std::f64;
|
||||
use wasm_bindgen::JsCast;
|
||||
use wasm_bindgen::prelude::*;
|
||||
use wasm_bindgen;
|
||||
use wasm_bindgen::prelude::*;
|
||||
use wasm_bindgen::JsCast;
|
||||
|
||||
|
||||
|
||||
@ -140,7 +140,7 @@ impl Config {
|
||||
/// as to use `Drop` to clean the HTML when not used anymore.
|
||||
#[derive(Clone, Debug, Shrinkwrap)]
|
||||
pub struct Dom {
|
||||
rc : Rc<DomData>
|
||||
rc: Rc<DomData>,
|
||||
}
|
||||
|
||||
/// Internal representation of `Dom`.
|
||||
@ -231,7 +231,9 @@ impl Default for Monitor {
|
||||
|
||||
impl Monitor {
|
||||
/// Cnstructor.
|
||||
pub fn new() -> Self { default() }
|
||||
pub fn new() -> Self {
|
||||
default()
|
||||
}
|
||||
|
||||
/// Modify the Monitor's config and update the view.
|
||||
pub fn mod_config<F: FnOnce(&mut Config)>(&mut self, f: F) {
|
||||
@ -326,8 +328,19 @@ impl Monitor {
|
||||
let width = self.width;
|
||||
let height = self.height;
|
||||
let shift = -(self.config.plot_step_size);
|
||||
dom.context.draw_image_with_html_canvas_element_and_sw_and_sh_and_dx_and_dy_and_dw_and_dh
|
||||
(&dom.canvas,0.0,0.0,width,height,shift,0.0,self.width,self.height).unwrap();
|
||||
dom.context
|
||||
.draw_image_with_html_canvas_element_and_sw_and_sh_and_dx_and_dy_and_dw_and_dh(
|
||||
&dom.canvas,
|
||||
0.0,
|
||||
0.0,
|
||||
width,
|
||||
height,
|
||||
shift,
|
||||
0.0,
|
||||
self.width,
|
||||
self.height,
|
||||
)
|
||||
.unwrap();
|
||||
}
|
||||
|
||||
fn clear_labels_area(&mut self, dom: &Dom) {
|
||||
@ -366,7 +379,6 @@ impl Monitor {
|
||||
|
||||
|
||||
|
||||
|
||||
// =============
|
||||
// === Panel ===
|
||||
// =============
|
||||
@ -375,13 +387,12 @@ impl Monitor {
|
||||
/// a `Sampler` under the hood, which defines both its behavior and its look and feel.
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct Panel {
|
||||
rc: Rc<RefCell<PanelData>>
|
||||
rc: Rc<RefCell<PanelData>>,
|
||||
}
|
||||
|
||||
impl Panel {
|
||||
/// Creates a new, empty Panel with a given sampler.
|
||||
pub fn new<S:Sampler+'static>
|
||||
(config:SamplerConfig, sampler:S) -> Self {
|
||||
pub fn new<S: Sampler + 'static>(config: SamplerConfig, sampler: S) -> Self {
|
||||
let rc = Rc::new(RefCell::new(PanelData::new(config, sampler)));
|
||||
Self { rc }
|
||||
}
|
||||
@ -416,7 +427,11 @@ impl Panel {
|
||||
/// It affects the way they are visually displayed.
|
||||
#[derive(Copy, Clone, Debug)]
|
||||
#[allow(missing_docs)]
|
||||
pub enum ValueCheck {Correct,Warning,Error}
|
||||
pub enum ValueCheck {
|
||||
Correct,
|
||||
Warning,
|
||||
Error,
|
||||
}
|
||||
|
||||
impl Default for ValueCheck {
|
||||
fn default() -> Self {
|
||||
@ -430,13 +445,21 @@ impl ValueCheck {
|
||||
/// Construct the check by comparing the provided value to two threshold values.
|
||||
pub fn from_threshold(warn_threshold: f64, err_threshold: f64, value: f64) -> Self {
|
||||
if warn_threshold > err_threshold {
|
||||
if value >= warn_threshold { ValueCheck::Correct }
|
||||
else if value >= err_threshold { ValueCheck::Warning }
|
||||
else { ValueCheck::Error }
|
||||
if value >= warn_threshold {
|
||||
ValueCheck::Correct
|
||||
} else if value >= err_threshold {
|
||||
ValueCheck::Warning
|
||||
} else {
|
||||
if value <= warn_threshold { ValueCheck::Correct }
|
||||
else if value <= err_threshold { ValueCheck::Warning }
|
||||
else { ValueCheck::Error }
|
||||
ValueCheck::Error
|
||||
}
|
||||
} else {
|
||||
if value <= warn_threshold {
|
||||
ValueCheck::Correct
|
||||
} else if value <= err_threshold {
|
||||
ValueCheck::Warning
|
||||
} else {
|
||||
ValueCheck::Error
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -463,27 +486,39 @@ pub trait Sampler: Debug {
|
||||
fn value(&self) -> f64;
|
||||
|
||||
/// Check whether the newest value is correct, or should be displayed as warning or error.
|
||||
fn check(&self) -> ValueCheck { ValueCheck::Correct }
|
||||
fn check(&self) -> ValueCheck {
|
||||
ValueCheck::Correct
|
||||
}
|
||||
|
||||
/// Returns the maximum expected value in order to set proper scaling of the monitor plots.
|
||||
/// If the real value will be bigger than this parameter, it will be clamped.
|
||||
fn max_value(&self) -> Option<f64> { None }
|
||||
fn max_value(&self) -> Option<f64> {
|
||||
None
|
||||
}
|
||||
|
||||
/// Returns the minimum expected value in order to set proper scaling of the monitor plots.
|
||||
/// If the real value will be smaller than this parameter, it will be clamped.
|
||||
fn min_value(&self) -> Option<f64> { None }
|
||||
fn min_value(&self) -> Option<f64> {
|
||||
None
|
||||
}
|
||||
|
||||
/// Returns the maximum expected value in order to set proper scaling of the monitor plots.
|
||||
/// If the real value will be bigger than this parameter, the graphs will be re-scaled
|
||||
/// automatically.
|
||||
fn min_size(&self) -> Option<f64> { None }
|
||||
fn min_size(&self) -> Option<f64> {
|
||||
None
|
||||
}
|
||||
|
||||
/// Returns the number describing the amount of last values which should be consideration
|
||||
/// when displaying the final value. The final value will be the average of # previous values.
|
||||
fn smooth_range(&self) -> usize { 2 }
|
||||
fn smooth_range(&self) -> usize {
|
||||
2
|
||||
}
|
||||
|
||||
/// The number of digits after the dot which should be displayed in the monitor panel.
|
||||
fn precision(&self) -> usize { 2 }
|
||||
fn precision(&self) -> usize {
|
||||
2
|
||||
}
|
||||
|
||||
// === Utils ===
|
||||
|
||||
@ -514,7 +549,7 @@ pub struct PanelData {
|
||||
draw_offset: f64,
|
||||
value_check: ValueCheck,
|
||||
precision: usize,
|
||||
sampler : Box<dyn Sampler>
|
||||
sampler: Box<dyn Sampler>,
|
||||
}
|
||||
|
||||
|
||||
@ -522,8 +557,7 @@ pub struct PanelData {
|
||||
|
||||
impl PanelData {
|
||||
/// Constructor.
|
||||
pub fn new<S:Sampler+'static>
|
||||
(config:SamplerConfig, sampler:S) -> Self {
|
||||
pub fn new<S: Sampler + 'static>(config: SamplerConfig, sampler: S) -> Self {
|
||||
let label = sampler.label().into();
|
||||
let performance = web::performance();
|
||||
let min_value = f64::INFINITY;
|
||||
@ -536,8 +570,21 @@ impl PanelData {
|
||||
let value_check = default();
|
||||
let sampler = Box::new(sampler);
|
||||
let precision = sampler.precision();
|
||||
Self {label,performance,config,min_value,max_value,begin_value,value,last_values
|
||||
,norm_value,draw_offset,value_check,precision,sampler}
|
||||
Self {
|
||||
label,
|
||||
performance,
|
||||
config,
|
||||
min_value,
|
||||
max_value,
|
||||
begin_value,
|
||||
value,
|
||||
last_values,
|
||||
norm_value,
|
||||
draw_offset,
|
||||
value_check,
|
||||
precision,
|
||||
sampler,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -565,13 +612,21 @@ impl PanelData {
|
||||
/// Clamp the measured values to the `max_value` and `min_value`.
|
||||
fn clamp_value(&mut self) {
|
||||
if let Some(max_value) = self.sampler.max_value() {
|
||||
if self.value > max_value { self.value = max_value; }
|
||||
if self.value > max_value {
|
||||
self.value = max_value;
|
||||
}
|
||||
}
|
||||
if let Some(min_value) = self.sampler.min_value() {
|
||||
if self.value > min_value { self.value = min_value; }
|
||||
if self.value > min_value {
|
||||
self.value = min_value;
|
||||
}
|
||||
}
|
||||
if self.value > self.max_value {
|
||||
self.max_value = self.value;
|
||||
}
|
||||
if self.value < self.min_value {
|
||||
self.min_value = self.value;
|
||||
}
|
||||
if self.value > self.max_value { self.max_value = self.value; }
|
||||
if self.value < self.min_value { self.min_value = self.value; }
|
||||
}
|
||||
|
||||
/// Smooth the final value based on the last measured values.
|
||||
@ -589,7 +644,9 @@ impl PanelData {
|
||||
fn normalize_value(&mut self) {
|
||||
let mut size = self.max_value - self.min_value;
|
||||
if let Some(min_size) = self.sampler.min_size() {
|
||||
if size < min_size { size = min_size; }
|
||||
if size < min_size {
|
||||
size = min_size;
|
||||
}
|
||||
}
|
||||
self.norm_value = (self.value - self.min_value) / size;
|
||||
}
|
||||
@ -645,7 +702,7 @@ impl PanelData {
|
||||
let color = match self.value_check {
|
||||
ValueCheck::Correct => &self.config.label_color_ok,
|
||||
ValueCheck::Warning => &self.config.label_color_warn,
|
||||
ValueCheck::Error => &self.config.label_color_err
|
||||
ValueCheck::Error => &self.config.label_color_err,
|
||||
};
|
||||
dom.context.set_fill_style(color);
|
||||
dom.context.fill_text(&display_value, self.config.results_width, y_pos).unwrap();
|
||||
@ -662,7 +719,7 @@ impl PanelData {
|
||||
let color = match self.value_check {
|
||||
ValueCheck::Correct => &self.config.plot_color_ok,
|
||||
ValueCheck::Warning => &self.config.plot_color_warn,
|
||||
ValueCheck::Error => &self.config.plot_color_err
|
||||
ValueCheck::Error => &self.config.plot_color_err,
|
||||
};
|
||||
dom.context.set_fill_style(color);
|
||||
dom.context.fill_rect(0.0, y_pos, self.config.plot_step_size, bar_height);
|
||||
@ -689,22 +746,32 @@ pub struct FrameTime {
|
||||
|
||||
impl FrameTime {
|
||||
/// Constructor
|
||||
pub fn new() -> Self { default() }
|
||||
pub fn new() -> Self {
|
||||
default()
|
||||
}
|
||||
}
|
||||
|
||||
const FRAME_TIME_WARNING_THRESHOLD: f64 = 1000.0 / 55.0;
|
||||
const FRAME_TIME_ERROR_THRESHOLD: f64 = 1000.0 / 25.0;
|
||||
|
||||
impl Sampler for FrameTime {
|
||||
fn label (&self) -> &str { "Frame time (ms)" }
|
||||
fn value (&self) -> f64 { self.value }
|
||||
fn check (&self) -> ValueCheck { self.value_check }
|
||||
fn begin (&mut self, time:f64) { self.begin_time = time; }
|
||||
fn label(&self) -> &str {
|
||||
"Frame time (ms)"
|
||||
}
|
||||
fn value(&self) -> f64 {
|
||||
self.value
|
||||
}
|
||||
fn check(&self) -> ValueCheck {
|
||||
self.value_check
|
||||
}
|
||||
fn begin(&mut self, time: f64) {
|
||||
self.begin_time = time;
|
||||
}
|
||||
fn end(&mut self, time: f64) {
|
||||
let end_time = time;
|
||||
self.value = end_time - self.begin_time;
|
||||
self.value_check = self.check_by_threshold
|
||||
(FRAME_TIME_WARNING_THRESHOLD, FRAME_TIME_ERROR_THRESHOLD);
|
||||
self.value_check =
|
||||
self.check_by_threshold(FRAME_TIME_WARNING_THRESHOLD, FRAME_TIME_ERROR_THRESHOLD);
|
||||
}
|
||||
}
|
||||
|
||||
@ -724,17 +791,27 @@ pub struct Fps {
|
||||
|
||||
impl Fps {
|
||||
/// Constructor.
|
||||
pub fn new() -> Self { default() }
|
||||
pub fn new() -> Self {
|
||||
default()
|
||||
}
|
||||
}
|
||||
|
||||
const FPS_WARNING_THRESHOLD: f64 = 55.0;
|
||||
const FPS_ERROR_THRESHOLD: f64 = 25.0;
|
||||
|
||||
impl Sampler for Fps {
|
||||
fn label (&self) -> &str { "Frames per second" }
|
||||
fn value (&self) -> f64 { self.value }
|
||||
fn check (&self) -> ValueCheck { self.value_check }
|
||||
fn max_value (&self) -> Option<f64> { Some(60.0) }
|
||||
fn label(&self) -> &str {
|
||||
"Frames per second"
|
||||
}
|
||||
fn value(&self) -> f64 {
|
||||
self.value
|
||||
}
|
||||
fn check(&self) -> ValueCheck {
|
||||
self.value_check
|
||||
}
|
||||
fn max_value(&self) -> Option<f64> {
|
||||
Some(60.0)
|
||||
}
|
||||
fn begin(&mut self, time: f64) {
|
||||
if self.begin_time > 0.0 {
|
||||
let end_time = time;
|
||||
@ -760,23 +837,33 @@ pub struct WasmMemory {
|
||||
|
||||
impl WasmMemory {
|
||||
/// Constructor.
|
||||
pub fn new() -> Self { default() }
|
||||
pub fn new() -> Self {
|
||||
default()
|
||||
}
|
||||
}
|
||||
|
||||
const WASM_MEM_WARNING_THRESHOLD: f64 = 50.0;
|
||||
const WASM_MEM_ERROR_THRESHOLD: f64 = 100.0;
|
||||
|
||||
impl Sampler for WasmMemory {
|
||||
fn label (&self) -> &str { "WASM memory usage (Mb)" }
|
||||
fn value (&self) -> f64 { self.value }
|
||||
fn check (&self) -> ValueCheck { self.value_check }
|
||||
fn min_size (&self) -> Option<f64> { Some(100.0) }
|
||||
fn label(&self) -> &str {
|
||||
"WASM memory usage (Mb)"
|
||||
}
|
||||
fn value(&self) -> f64 {
|
||||
self.value
|
||||
}
|
||||
fn check(&self) -> ValueCheck {
|
||||
self.value_check
|
||||
}
|
||||
fn min_size(&self) -> Option<f64> {
|
||||
Some(100.0)
|
||||
}
|
||||
fn end(&mut self, _time: f64) {
|
||||
let memory: Memory = wasm_bindgen::memory().dyn_into().unwrap();
|
||||
let buffer: ArrayBuffer = memory.buffer().dyn_into().unwrap();
|
||||
self.value = (buffer.byte_length() as f64) / (1024.0 * 1024.0);
|
||||
self.value_check = self.check_by_threshold
|
||||
(WASM_MEM_WARNING_THRESHOLD,WASM_MEM_ERROR_THRESHOLD);
|
||||
self.value_check =
|
||||
self.check_by_threshold(WASM_MEM_WARNING_THRESHOLD, WASM_MEM_ERROR_THRESHOLD);
|
||||
}
|
||||
}
|
||||
|
||||
@ -791,7 +878,6 @@ impl Sampler for WasmMemory {
|
||||
macro_rules! stats_sampler {
|
||||
( $label:tt, $name:ident, $stats_method:ident, $t1:expr, $t2:expr, $precision:expr
|
||||
, $value_divisor:expr) => {
|
||||
|
||||
/// Sampler implementation.
|
||||
#[derive(Debug, Default)]
|
||||
pub struct $name {
|
||||
@ -806,13 +892,22 @@ macro_rules! stats_sampler {
|
||||
}
|
||||
|
||||
impl Sampler for $name {
|
||||
fn label (&self) -> &str { $label }
|
||||
fn value (&self) -> f64 { self.stats.$stats_method() as f64 / $value_divisor }
|
||||
fn min_size (&self) -> Option<f64> { Some($t1) }
|
||||
fn precision (&self) -> usize { $precision }
|
||||
fn check (&self) -> ValueCheck { self.check_by_threshold($t1,$t2) }
|
||||
fn label(&self) -> &str {
|
||||
$label
|
||||
}
|
||||
fn value(&self) -> f64 {
|
||||
self.stats.$stats_method() as f64 / $value_divisor
|
||||
}
|
||||
fn min_size(&self) -> Option<f64> {
|
||||
Some($t1)
|
||||
}
|
||||
fn precision(&self) -> usize {
|
||||
$precision
|
||||
}
|
||||
fn check(&self) -> ValueCheck {
|
||||
self.check_by_threshold($t1, $t2)
|
||||
}
|
||||
}
|
||||
|
||||
};
|
||||
}
|
||||
|
||||
@ -827,4 +922,12 @@ stats_sampler!("Sprite system count" , SpriteSystemCount , sprite_system_cou
|
||||
stats_sampler!("Symbol count", SymbolCount, symbol_count, 100.0, 500.0, 0, 1.0);
|
||||
stats_sampler!("Sprite count", SpriteCount, sprite_count, 100_000.0, 500_000.0, 0, 1.0);
|
||||
stats_sampler!("Shader count", ShaderCount, shader_count, 100.0, 500.0, 0, 1.0);
|
||||
stats_sampler!("Shader compile count" , ShaderCompileCount , shader_compile_count , 10.0 , 100.0 , 0 , 1.0);
|
||||
stats_sampler!(
|
||||
"Shader compile count",
|
||||
ShaderCompileCount,
|
||||
shader_compile_count,
|
||||
10.0,
|
||||
100.0,
|
||||
0,
|
||||
1.0
|
||||
);
|
||||
|
@ -13,7 +13,7 @@ use crate::prelude::*;
|
||||
/// Structure containing all the gathered stats.
|
||||
#[derive(Debug, Clone, CloneRef)]
|
||||
pub struct Stats {
|
||||
rc: Rc<RefCell<StatsData>>
|
||||
rc: Rc<RefCell<StatsData>>,
|
||||
}
|
||||
|
||||
impl Default for Stats {
|
||||
|
@ -3,13 +3,13 @@
|
||||
|
||||
pub mod camera;
|
||||
pub mod layout;
|
||||
pub mod object;
|
||||
pub mod navigation;
|
||||
pub mod object;
|
||||
pub mod render;
|
||||
pub mod scene;
|
||||
pub mod shape;
|
||||
pub mod symbol;
|
||||
pub mod style;
|
||||
pub mod symbol;
|
||||
pub mod world;
|
||||
|
||||
|
||||
@ -26,11 +26,11 @@ pub mod traits {
|
||||
|
||||
/// Common types.
|
||||
pub mod types {
|
||||
use super::*;
|
||||
pub use object::Object;
|
||||
pub use scene::Scene;
|
||||
pub use scene::dom::DomScene;
|
||||
pub use super::symbol::*;
|
||||
pub use super::traits::*;
|
||||
use super::*;
|
||||
pub use object::Object;
|
||||
pub use scene::dom::DomScene;
|
||||
pub use scene::Scene;
|
||||
}
|
||||
pub use types::*;
|
||||
|
@ -3,11 +3,11 @@
|
||||
|
||||
use crate::prelude::*;
|
||||
|
||||
use crate::control::callback;
|
||||
use crate::data::dirty;
|
||||
use crate::data::dirty::traits::*;
|
||||
use crate::display;
|
||||
use crate::display::scene::Scene;
|
||||
use crate::data::dirty::traits::*;
|
||||
use crate::control::callback;
|
||||
|
||||
use nalgebra::Perspective3;
|
||||
|
||||
@ -54,11 +54,11 @@ pub enum Projection {
|
||||
/// Perspective projection.
|
||||
Perspective {
|
||||
/// Field of view.
|
||||
fov : f32
|
||||
fov: f32,
|
||||
},
|
||||
|
||||
/// Orthographic projection.
|
||||
Orthographic
|
||||
Orthographic,
|
||||
}
|
||||
|
||||
impl Default for Projection {
|
||||
@ -79,7 +79,7 @@ impl Default for Projection {
|
||||
#[allow(missing_docs)]
|
||||
pub struct Clipping {
|
||||
pub near: f32,
|
||||
pub far : f32
|
||||
pub far: f32,
|
||||
}
|
||||
|
||||
impl Default for Clipping {
|
||||
@ -102,7 +102,7 @@ impl Default for Clipping {
|
||||
#[derive(Clone, CloneRef, Debug)]
|
||||
pub struct Dirty {
|
||||
projection: ProjectionDirty,
|
||||
transform : TransformDirty
|
||||
transform: TransformDirty,
|
||||
}
|
||||
|
||||
impl Dirty {
|
||||
@ -190,8 +190,19 @@ impl Camera2dData {
|
||||
display_object.set_on_updated(f_!(dirty.transform.set()));
|
||||
display_object.mod_position(|p| p.z = 1.0);
|
||||
dirty.projection.set();
|
||||
Self {display_object,screen,zoom,z_zoom_1,projection,clipping,matrix,dirty
|
||||
,zoom_update_registry,screen_update_registry}.init()
|
||||
Self {
|
||||
display_object,
|
||||
screen,
|
||||
zoom,
|
||||
z_zoom_1,
|
||||
projection,
|
||||
clipping,
|
||||
matrix,
|
||||
dirty,
|
||||
zoom_update_registry,
|
||||
screen_update_registry,
|
||||
}
|
||||
.init()
|
||||
}
|
||||
|
||||
fn init(mut self) -> Self {
|
||||
@ -221,7 +232,7 @@ impl Camera2dData {
|
||||
let far = self.clipping.far;
|
||||
*Perspective3::new(aspect, *fov, near, far).as_matrix()
|
||||
}
|
||||
_ => unimplemented!()
|
||||
_ => unimplemented!(),
|
||||
};
|
||||
}
|
||||
|
||||
@ -229,7 +240,7 @@ impl Camera2dData {
|
||||
match &self.projection {
|
||||
Projection::Perspective { .. } =>
|
||||
Perspective3::from_matrix_unchecked(self.matrix.projection).inverse(),
|
||||
_ => unimplemented!()
|
||||
_ => unimplemented!(),
|
||||
}
|
||||
}
|
||||
|
||||
@ -291,7 +302,7 @@ impl Camera2dData {
|
||||
self.z_zoom_1 = z_zoom_1;
|
||||
self.mod_position_keep_zoom(|t| t.z = z_zoom_1 / zoom);
|
||||
}
|
||||
_ => unimplemented!()
|
||||
_ => unimplemented!(),
|
||||
};
|
||||
let dimensions = Vector2::new(width, height);
|
||||
self.screen_update_registry.run_all(&dimensions);
|
||||
@ -350,8 +361,8 @@ impl Camera2dData {
|
||||
/// drawing elements and scaling the view. By default, the `alignment` is set to center, which
|
||||
/// defines the origin center at the center of the screen. When scaling the view, objects placed
|
||||
/// in the center of the view will not move visually. If you set the alignment to bottom-left
|
||||
/// corner, you will get a view which behaves like a window in window-based GUIs. When scaling
|
||||
/// the window, the left-bottom corner will stay in place.
|
||||
/// corner, you will get a view which behaves like a window in window-based GUIs. When scaling the
|
||||
/// window, the left-bottom corner will stay in place.
|
||||
#[derive(Clone, CloneRef, Debug)]
|
||||
pub struct Camera2d {
|
||||
display_object: display::object::Instance,
|
||||
|
@ -20,12 +20,20 @@ pub struct Alignment {
|
||||
/// Horizontal alignments.
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
#[allow(missing_docs)]
|
||||
pub enum Horizontal {Left,Center,Right}
|
||||
pub enum Horizontal {
|
||||
Left,
|
||||
Center,
|
||||
Right,
|
||||
}
|
||||
|
||||
/// Vertical alignments.
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
#[allow(missing_docs)]
|
||||
pub enum Vertical {Top,Center,Bottom}
|
||||
pub enum Vertical {
|
||||
Top,
|
||||
Center,
|
||||
Bottom,
|
||||
}
|
||||
|
||||
|
||||
// === Smart Constructors ===
|
||||
@ -38,22 +46,48 @@ impl Alignment {
|
||||
Self { horizontal, vertical }
|
||||
}
|
||||
|
||||
pub fn center () -> Self { Self::new( Horizontal::Center , Vertical::Center ) }
|
||||
pub fn bottom_left () -> Self { Self::new( Horizontal::Left , Vertical::Bottom ) }
|
||||
pub fn bottom_right () -> Self { Self::new( Horizontal::Right , Vertical::Bottom ) }
|
||||
pub fn bottom_center () -> Self { Self::new( Horizontal::Center , Vertical::Bottom ) }
|
||||
pub fn top_left () -> Self { Self::new( Horizontal::Left , Vertical::Top ) }
|
||||
pub fn top_right () -> Self { Self::new( Horizontal::Right , Vertical::Top ) }
|
||||
pub fn top_center () -> Self { Self::new( Horizontal::Center , Vertical::Top ) }
|
||||
pub fn center_left () -> Self { Self::new( Horizontal::Left , Vertical::Center ) }
|
||||
pub fn center_right () -> Self { Self::new( Horizontal::Right , Vertical::Center ) }
|
||||
pub fn center() -> Self {
|
||||
Self::new(Horizontal::Center, Vertical::Center)
|
||||
}
|
||||
pub fn bottom_left() -> Self {
|
||||
Self::new(Horizontal::Left, Vertical::Bottom)
|
||||
}
|
||||
pub fn bottom_right() -> Self {
|
||||
Self::new(Horizontal::Right, Vertical::Bottom)
|
||||
}
|
||||
pub fn bottom_center() -> Self {
|
||||
Self::new(Horizontal::Center, Vertical::Bottom)
|
||||
}
|
||||
pub fn top_left() -> Self {
|
||||
Self::new(Horizontal::Left, Vertical::Top)
|
||||
}
|
||||
pub fn top_right() -> Self {
|
||||
Self::new(Horizontal::Right, Vertical::Top)
|
||||
}
|
||||
pub fn top_center() -> Self {
|
||||
Self::new(Horizontal::Center, Vertical::Top)
|
||||
}
|
||||
pub fn center_left() -> Self {
|
||||
Self::new(Horizontal::Left, Vertical::Center)
|
||||
}
|
||||
pub fn center_right() -> Self {
|
||||
Self::new(Horizontal::Right, Vertical::Center)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// === Defaults ===
|
||||
|
||||
impl Default for Horizontal { fn default() -> Self { Self::Left } }
|
||||
impl Default for Vertical { fn default() -> Self { Self::Bottom } }
|
||||
impl Default for Horizontal {
|
||||
fn default() -> Self {
|
||||
Self::Left
|
||||
}
|
||||
}
|
||||
impl Default for Vertical {
|
||||
fn default() -> Self {
|
||||
Self::Bottom
|
||||
}
|
||||
}
|
||||
impl Default for Alignment {
|
||||
fn default() -> Self {
|
||||
let horizontal = default();
|
||||
@ -62,6 +96,18 @@ impl Default for Alignment {
|
||||
}
|
||||
}
|
||||
|
||||
impl From<&Horizontal> for Horizontal { fn from(t:&Horizontal) -> Self { *t } }
|
||||
impl From<&Vertical> for Vertical { fn from(t:&Vertical) -> Self { *t } }
|
||||
impl From<&Alignment> for Alignment { fn from(t:&Alignment) -> Self { *t } }
|
||||
impl From<&Horizontal> for Horizontal {
|
||||
fn from(t: &Horizontal) -> Self {
|
||||
*t
|
||||
}
|
||||
}
|
||||
impl From<&Vertical> for Vertical {
|
||||
fn from(t: &Vertical) -> Self {
|
||||
*t
|
||||
}
|
||||
}
|
||||
impl From<&Alignment> for Alignment {
|
||||
fn from(t: &Alignment) -> Self {
|
||||
*t
|
||||
}
|
||||
}
|
||||
|
@ -38,9 +38,15 @@ impl NavigatorModel {
|
||||
let min_zoom = 10.0;
|
||||
let max_zoom = 10000.0;
|
||||
let disable_events = Rc::new(Cell::new(true));
|
||||
let (simulator,resize_callback,_events) = Self::start_navigator_events
|
||||
(scene,camera,min_zoom,max_zoom,Rc::clone(&zoom_speed),Rc::clone(&pan_speed),
|
||||
Rc::clone(&disable_events));
|
||||
let (simulator, resize_callback, _events) = Self::start_navigator_events(
|
||||
scene,
|
||||
camera,
|
||||
min_zoom,
|
||||
max_zoom,
|
||||
Rc::clone(&zoom_speed),
|
||||
Rc::clone(&pan_speed),
|
||||
Rc::clone(&disable_events),
|
||||
);
|
||||
Self { _events, simulator, resize_callback, zoom_speed, pan_speed, disable_events }
|
||||
}
|
||||
|
||||
@ -55,14 +61,14 @@ impl NavigatorModel {
|
||||
simulator
|
||||
}
|
||||
|
||||
fn start_navigator_events
|
||||
( scene : &Scene
|
||||
, camera : &Camera2d
|
||||
, min_zoom : f32
|
||||
, max_zoom : f32
|
||||
, zoom_speed : SharedSwitch<f32>
|
||||
, pan_speed : SharedSwitch<f32>
|
||||
, disable_events : Rc<Cell<bool>>
|
||||
fn start_navigator_events(
|
||||
scene: &Scene,
|
||||
camera: &Camera2d,
|
||||
min_zoom: f32,
|
||||
max_zoom: f32,
|
||||
zoom_speed: SharedSwitch<f32>,
|
||||
pan_speed: SharedSwitch<f32>,
|
||||
disable_events: Rc<Cell<bool>>,
|
||||
) -> (physics::inertia::DynSimulator<Vector3>, callback::Handle, NavigatorEvents) {
|
||||
let simulator = Self::create_simulator(camera);
|
||||
let panning_callback = enclose!((scene,camera,mut simulator,pan_speed) move |pan: PanEvent| {
|
||||
@ -81,7 +87,7 @@ impl NavigatorModel {
|
||||
simulator.set_value(position);
|
||||
simulator.set_target_value(position);
|
||||
simulator.set_velocity(default());
|
||||
})
|
||||
}),
|
||||
);
|
||||
|
||||
let zoom_callback = enclose!((scene,camera,simulator) move |zoom:ZoomEvent| {
|
||||
@ -109,9 +115,18 @@ impl NavigatorModel {
|
||||
position += direction * zoom_factor;
|
||||
simulator.set_target_value(position);
|
||||
});
|
||||
(simulator,resize_callback, NavigatorEvents::new(&scene.mouse.mouse_manager,
|
||||
panning_callback,zoom_callback,
|
||||
zoom_speed,pan_speed,disable_events))
|
||||
(
|
||||
simulator,
|
||||
resize_callback,
|
||||
NavigatorEvents::new(
|
||||
&scene.mouse.mouse_manager,
|
||||
panning_callback,
|
||||
zoom_callback,
|
||||
zoom_speed,
|
||||
pan_speed,
|
||||
disable_events,
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
pub fn enable(&self) {
|
||||
@ -145,7 +160,6 @@ impl Navigator {
|
||||
let model = Rc::new(NavigatorModel::new(scene, camera));
|
||||
Navigator { model }
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
@ -157,8 +171,7 @@ impl Navigator {
|
||||
type SharedSwitch<T> = Rc<Cell<Switch<T>>>;
|
||||
|
||||
/// Normalize a `point` in (0..dimension.x, 0..dimension.y) to (0..1, 0..1).
|
||||
fn normalize_point2
|
||||
(point:Vector2<f32>, dimension:Vector2<f32>) -> Vector2<f32> {
|
||||
fn normalize_point2(point: Vector2<f32>, dimension: Vector2<f32>) -> Vector2<f32> {
|
||||
Vector2::new(point.x / dimension.x, point.y / dimension.y)
|
||||
}
|
||||
|
||||
|
@ -1,12 +1,12 @@
|
||||
use crate::prelude::*;
|
||||
|
||||
use crate::control::callback;
|
||||
use crate::control::io::mouse;
|
||||
use crate::control::io::mouse::MouseManager;
|
||||
use crate::control::callback;
|
||||
use crate::display::navigation::navigator::SharedSwitch;
|
||||
|
||||
use nalgebra::Vector2;
|
||||
use nalgebra::zero;
|
||||
use nalgebra::Vector2;
|
||||
|
||||
|
||||
|
||||
@ -19,7 +19,7 @@ pub trait FnZoomEvent = FnMut(ZoomEvent) + 'static;
|
||||
/// A struct holding zoom event information, such as the focus point and the amount of zoom.
|
||||
pub struct ZoomEvent {
|
||||
pub focus: Vector2<f32>,
|
||||
pub amount : f32
|
||||
pub amount: f32,
|
||||
}
|
||||
|
||||
impl ZoomEvent {
|
||||
@ -39,7 +39,7 @@ pub trait FnPanEvent = FnMut(PanEvent) + 'static;
|
||||
|
||||
/// A struct holding pan event information.
|
||||
pub struct PanEvent {
|
||||
pub movement : Vector2<f32>
|
||||
pub movement: Vector2<f32>,
|
||||
}
|
||||
|
||||
impl PanEvent {
|
||||
@ -57,7 +57,7 @@ impl PanEvent {
|
||||
#[derive(PartialEq, Clone, Copy, Debug)]
|
||||
enum MovementType {
|
||||
Pan,
|
||||
Zoom { focus : Vector2<f32> }
|
||||
Zoom { focus: Vector2<f32> },
|
||||
}
|
||||
|
||||
|
||||
@ -89,16 +89,17 @@ struct NavigatorEventsProperties {
|
||||
|
||||
#[derive(Debug)]
|
||||
struct NavigatorEventsData {
|
||||
properties : RefCell<NavigatorEventsProperties>
|
||||
properties: RefCell<NavigatorEventsProperties>,
|
||||
}
|
||||
|
||||
impl NavigatorEventsData {
|
||||
fn new
|
||||
( pan_callback:Box<dyn FnPanEvent>
|
||||
, zoom_callback:Box<dyn FnZoomEvent>
|
||||
, zoom_speed:SharedSwitch<f32>
|
||||
, pan_speed:SharedSwitch<f32>
|
||||
, disable_events:Rc<Cell<bool>>) -> Rc<Self> {
|
||||
fn new(
|
||||
pan_callback: Box<dyn FnPanEvent>,
|
||||
zoom_callback: Box<dyn FnZoomEvent>,
|
||||
zoom_speed: SharedSwitch<f32>,
|
||||
pan_speed: SharedSwitch<f32>,
|
||||
disable_events: Rc<Cell<bool>>,
|
||||
) -> Rc<Self> {
|
||||
let mouse_position = zero();
|
||||
let last_mouse_position = zero();
|
||||
let movement_type = None;
|
||||
@ -111,7 +112,6 @@ impl NavigatorEventsData {
|
||||
mouse_position,
|
||||
pan_callback,
|
||||
zoom_callback,
|
||||
|
||||
});
|
||||
Rc::new(Self { properties })
|
||||
}
|
||||
@ -189,14 +189,22 @@ pub struct NavigatorEvents {
|
||||
#[derivative(Debug = "ignore")]
|
||||
mouse_leave: Option<callback::Handle>,
|
||||
#[derivative(Debug = "ignore")]
|
||||
wheel_zoom : Option<callback::Handle>
|
||||
wheel_zoom: Option<callback::Handle>,
|
||||
}
|
||||
|
||||
impl NavigatorEvents {
|
||||
pub fn new
|
||||
<P,Z>(mouse_manager:&MouseManager, pan_callback:P, zoom_callback:Z,
|
||||
zoom_speed:SharedSwitch<f32>,pan_speed:SharedSwitch<f32>,disable_events:Rc<Cell<bool>>) -> Self
|
||||
where P : FnPanEvent, Z : FnZoomEvent {
|
||||
pub fn new<P, Z>(
|
||||
mouse_manager: &MouseManager,
|
||||
pan_callback: P,
|
||||
zoom_callback: Z,
|
||||
zoom_speed: SharedSwitch<f32>,
|
||||
pan_speed: SharedSwitch<f32>,
|
||||
disable_events: Rc<Cell<bool>>,
|
||||
) -> Self
|
||||
where
|
||||
P: FnPanEvent,
|
||||
Z: FnZoomEvent,
|
||||
{
|
||||
let mouse_manager = mouse_manager.clone_ref();
|
||||
let pan_callback = Box::new(pan_callback);
|
||||
let zoom_callback = Box::new(zoom_callback);
|
||||
@ -205,17 +213,15 @@ impl NavigatorEvents {
|
||||
let mouse_down = default();
|
||||
let wheel_zoom = default();
|
||||
let mouse_leave = default();
|
||||
let data = NavigatorEventsData::new
|
||||
(pan_callback,zoom_callback,zoom_speed,pan_speed,disable_events);
|
||||
let mut event_handler = Self {
|
||||
data,
|
||||
mouse_manager,
|
||||
mouse_down,
|
||||
mouse_up,
|
||||
mouse_move,
|
||||
mouse_leave,
|
||||
wheel_zoom
|
||||
};
|
||||
let data = NavigatorEventsData::new(
|
||||
pan_callback,
|
||||
zoom_callback,
|
||||
zoom_speed,
|
||||
pan_speed,
|
||||
disable_events,
|
||||
);
|
||||
let mut event_handler =
|
||||
Self { data, mouse_manager, mouse_down, mouse_up, mouse_move, mouse_leave, wheel_zoom };
|
||||
|
||||
event_handler.initialize_mouse_events();
|
||||
event_handler
|
||||
@ -267,14 +273,12 @@ impl NavigatorEvents {
|
||||
event.prevent_default();
|
||||
}
|
||||
match event.button() {
|
||||
mouse::MiddleButton => {
|
||||
data.set_movement_type(Some(MovementType::Pan))
|
||||
},
|
||||
mouse::MiddleButton => data.set_movement_type(Some(MovementType::Pan)),
|
||||
mouse::SecondaryButton => {
|
||||
let focus = event.position_relative_to_event_handler();
|
||||
data.set_movement_type(Some(MovementType::Zoom { focus }))
|
||||
},
|
||||
_ => ()
|
||||
}
|
||||
_ => (),
|
||||
}
|
||||
}
|
||||
});
|
||||
@ -324,7 +328,7 @@ impl NavigatorEvents {
|
||||
let zoom_amount = movement_to_zoom(movement);
|
||||
let zoom_event = ZoomEvent::new(focus, zoom_amount, zoom_speed);
|
||||
data.on_zoom(zoom_event);
|
||||
},
|
||||
}
|
||||
MovementType::Pan => {
|
||||
let pan_event = PanEvent::new(movement);
|
||||
data.on_pan(pan_event);
|
||||
|
@ -6,11 +6,11 @@ use crate::prelude::*;
|
||||
|
||||
use super::transform;
|
||||
|
||||
use crate::data::dirty::traits::*;
|
||||
use crate::data::dirty;
|
||||
use crate::display::scene::Scene;
|
||||
use crate::data::dirty::traits::*;
|
||||
use crate::display::scene::layer::Layer;
|
||||
use crate::display::scene::layer::WeakLayer;
|
||||
use crate::display::scene::Scene;
|
||||
|
||||
use data::opt_vec::OptVec;
|
||||
use nalgebra::Matrix4;
|
||||
@ -31,7 +31,7 @@ use transform::CachedTransform;
|
||||
#[allow(missing_docs)]
|
||||
pub struct ParentBind<Host> {
|
||||
pub parent: WeakInstance<Host>,
|
||||
pub index : usize
|
||||
pub index: usize,
|
||||
}
|
||||
|
||||
impl<Host> ParentBind<Host> {
|
||||
@ -69,20 +69,32 @@ pub struct Callbacks<Host> {
|
||||
|
||||
impl<Host> Callbacks<Host> {
|
||||
fn on_updated(&self, model: &Model<Host>) {
|
||||
if let Some(f) = &*self.on_updated.borrow() { f(model) }
|
||||
if let Some(f) = &*self.on_updated.borrow() {
|
||||
f(model)
|
||||
}
|
||||
}
|
||||
|
||||
fn on_show(&self, host: &Host, layers: &[WeakLayer]) {
|
||||
if let Some(f) = &*self.on_show.borrow() { f(host,layers) }
|
||||
if let Some(f) = &*self.on_show.borrow() {
|
||||
f(host, layers)
|
||||
}
|
||||
}
|
||||
|
||||
fn on_hide(&self, host: &Host) {
|
||||
if let Some(f) = &*self.on_hide.borrow() { f(host) }
|
||||
if let Some(f) = &*self.on_hide.borrow() {
|
||||
f(host)
|
||||
}
|
||||
}
|
||||
|
||||
fn on_scene_layers_changed
|
||||
(&self, host:&Host, old_layers:&[WeakLayer], new_layers:&[WeakLayer]) {
|
||||
if let Some(f) = &*self.on_scene_layers_changed.borrow() { f(host,old_layers,new_layers) }
|
||||
fn on_scene_layers_changed(
|
||||
&self,
|
||||
host: &Host,
|
||||
old_layers: &[WeakLayer],
|
||||
new_layers: &[WeakLayer],
|
||||
) {
|
||||
if let Some(f) = &*self.on_scene_layers_changed.borrow() {
|
||||
f(host, old_layers, new_layers)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -217,8 +229,18 @@ impl<Host> Model<Host> {
|
||||
let host = default();
|
||||
let assigned_layers = default();
|
||||
let layers = default();
|
||||
Self {host,assigned_layers,layers,dirty,callbacks,parent_bind,children,transform
|
||||
,visible,logger}
|
||||
Self {
|
||||
host,
|
||||
assigned_layers,
|
||||
layers,
|
||||
dirty,
|
||||
callbacks,
|
||||
parent_bind,
|
||||
children,
|
||||
transform,
|
||||
visible,
|
||||
logger,
|
||||
}
|
||||
}
|
||||
|
||||
/// Checks whether the object is visible.
|
||||
@ -278,13 +300,13 @@ impl<Host> Model<Host> {
|
||||
impl<Host> Model<Host> {
|
||||
/// Updates object transformations by providing a new origin location. See docs of `update` to
|
||||
/// learn more.
|
||||
fn update_with_origin
|
||||
( &self
|
||||
, host : &Host
|
||||
, parent_origin : Matrix4<f32>
|
||||
, parent_origin_changed : bool
|
||||
, parent_layers_changed : bool
|
||||
, parent_layers : &[WeakLayer]
|
||||
fn update_with_origin(
|
||||
&self,
|
||||
host: &Host,
|
||||
parent_origin: Matrix4<f32>,
|
||||
parent_origin_changed: bool,
|
||||
parent_layers_changed: bool,
|
||||
parent_layers: &[WeakLayer],
|
||||
) {
|
||||
// === Scene Layers Update ===
|
||||
let has_new_parent = self.dirty.parent.check();
|
||||
@ -307,9 +329,13 @@ impl<Host> Model<Host> {
|
||||
parent_layers_changed
|
||||
};
|
||||
|
||||
let new_layers_opt = layers_changed.as_some_from(||
|
||||
if has_assigned_layers {assigned_layers} else {parent_layers}
|
||||
);
|
||||
let new_layers_opt = layers_changed.as_some_from(|| {
|
||||
if has_assigned_layers {
|
||||
assigned_layers
|
||||
} else {
|
||||
parent_layers
|
||||
}
|
||||
});
|
||||
if let Some(new_layers) = new_layers_opt {
|
||||
debug!(self.logger, "Scene layers changed.", || {
|
||||
let old_layers = mem::replace(&mut *self.layers.borrow_mut(), new_layers.to_vec());
|
||||
@ -340,10 +366,15 @@ impl<Host> Model<Host> {
|
||||
if !self.children.borrow().is_empty() {
|
||||
debug!(self.logger, "Updating all children.", || {
|
||||
self.children.borrow().iter().for_each(|weak_child| {
|
||||
weak_child.upgrade().for_each(|child|
|
||||
child.update_with_origin
|
||||
(host,new_origin,true,layers_changed,new_layers)
|
||||
);
|
||||
weak_child.upgrade().for_each(|child| {
|
||||
child.update_with_origin(
|
||||
host,
|
||||
new_origin,
|
||||
true,
|
||||
layers_changed,
|
||||
new_layers,
|
||||
)
|
||||
});
|
||||
});
|
||||
})
|
||||
}
|
||||
@ -352,10 +383,19 @@ impl<Host> Model<Host> {
|
||||
if self.dirty.children.check_all() {
|
||||
debug!(self.logger, "Updating dirty children.", || {
|
||||
self.dirty.children.take().iter().for_each(|ix| {
|
||||
self.children.borrow().safe_index(*ix).and_then(|t|t.upgrade())
|
||||
.for_each(|child|
|
||||
child.update_with_origin
|
||||
(host,new_origin,false,layers_changed,new_layers))
|
||||
self.children
|
||||
.borrow()
|
||||
.safe_index(*ix)
|
||||
.and_then(|t| t.upgrade())
|
||||
.for_each(|child| {
|
||||
child.update_with_origin(
|
||||
host,
|
||||
new_origin,
|
||||
false,
|
||||
layers_changed,
|
||||
new_layers,
|
||||
)
|
||||
})
|
||||
});
|
||||
})
|
||||
}
|
||||
@ -410,8 +450,11 @@ impl<Host> Model<Host> {
|
||||
info!(self.logger, "Showing.");
|
||||
let this_scene_layers = self.assigned_layers.borrow();
|
||||
let this_scene_layers_slice = this_scene_layers.as_slice();
|
||||
let layers = if this_scene_layers_slice.is_empty()
|
||||
{ parent_layers } else { this_scene_layers_slice };
|
||||
let layers = if this_scene_layers_slice.is_empty() {
|
||||
parent_layers
|
||||
} else {
|
||||
this_scene_layers_slice
|
||||
};
|
||||
self.visible.set(true);
|
||||
self.callbacks.on_show(host, layers);
|
||||
self.children.borrow().iter().for_each(|child| {
|
||||
@ -551,7 +594,10 @@ impl<Host> Model<Host> {
|
||||
// ==========
|
||||
|
||||
/// Globally unique identifier of a display object.
|
||||
#[derive(Clone,CloneRef,Copy,Debug,Default,Display,Eq,From,Hash,Into,PartialEq,Ord,PartialOrd)]
|
||||
#[derive(
|
||||
Clone, CloneRef, Copy, Debug, Default, Display, Eq, From, Hash, Into, PartialEq, Ord,
|
||||
PartialOrd
|
||||
)]
|
||||
pub struct Id(usize);
|
||||
|
||||
|
||||
@ -595,7 +641,7 @@ pub struct Id(usize);
|
||||
#[derive(CloneRef)]
|
||||
#[derivative(Clone(bound = ""))]
|
||||
pub struct Instance<Host = Scene> {
|
||||
rc : Rc<Model<Host>>
|
||||
rc: Rc<Model<Host>>,
|
||||
}
|
||||
|
||||
impl<Host> Deref for Instance<Host> {
|
||||
@ -629,7 +675,9 @@ impl<Host> Instance<Host> {
|
||||
|
||||
/// Get the layers where this object is displayed. May be equal to layers it was explicitly
|
||||
/// assigned, or layers inherited from the parent.
|
||||
pub fn _display_layers(&self) -> Vec<WeakLayer> { self.layers.borrow().clone() }
|
||||
pub fn _display_layers(&self) -> Vec<WeakLayer> {
|
||||
self.layers.borrow().clone()
|
||||
}
|
||||
|
||||
/// 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.
|
||||
@ -708,7 +756,11 @@ impl<Host> Instance<Host> {
|
||||
pub fn child_index<T: Object<Host>>(&self, child: &T) -> Option<usize> {
|
||||
let child = child.display_object();
|
||||
child.parent_bind.borrow().as_ref().and_then(|bind| {
|
||||
if bind.parent().as_ref() == Some(self) { Some(bind.index) } else { None }
|
||||
if bind.parent().as_ref() == Some(self) {
|
||||
Some(bind.index)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
@ -759,7 +811,7 @@ impl<Host> Debug for Instance<Host> {
|
||||
#[derivative(Clone(bound = ""))]
|
||||
#[derivative(Debug(bound = ""))]
|
||||
pub struct WeakInstance<Host> {
|
||||
weak : Weak<Model<Host>>
|
||||
weak: Weak<Model<Host>>,
|
||||
}
|
||||
|
||||
impl<Host> WeakInstance<Host> {
|
||||
@ -843,7 +895,9 @@ pub trait ObjectOps<Host=Scene> : Object<Host> {
|
||||
|
||||
/// Get the layers where this object is displayed. May be equal to layers it was explicitly
|
||||
/// assigned, or layers inherited from the parent.
|
||||
fn display_layers(&self) -> Vec<WeakLayer> { self.display_object()._display_layers() }
|
||||
fn display_layers(&self) -> Vec<WeakLayer> {
|
||||
self.display_object()._display_layers()
|
||||
}
|
||||
|
||||
/// Add another display object as a child to this display object. Children will inherit all
|
||||
/// transformations of their parents.
|
||||
@ -1118,19 +1172,25 @@ pub trait ObjectOps<Host=Scene> : Object<Host> {
|
||||
/// with base implementation `From<T> for T`.
|
||||
#[derive(CloneRef)]
|
||||
pub struct Any<Host = Scene> {
|
||||
wrapped : Rc<dyn Object<Host>>
|
||||
wrapped: Rc<dyn Object<Host>>,
|
||||
}
|
||||
|
||||
impl<Host> Clone for Any<Host> {
|
||||
fn clone(&self) -> Self { Self {wrapped:self.wrapped.clone()} }
|
||||
fn clone(&self) -> Self {
|
||||
Self { wrapped: self.wrapped.clone() }
|
||||
}
|
||||
}
|
||||
|
||||
impl<Host> Debug for Any<Host> {
|
||||
fn fmt(&self, f:&mut fmt::Formatter) -> fmt::Result { write!(f, "display::object::Any") }
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
write!(f, "display::object::Any")
|
||||
}
|
||||
}
|
||||
|
||||
impl<Host> Object<Host> for Any<Host> {
|
||||
fn display_object(&self) -> &Instance<Host> { self.wrapped.display_object() }
|
||||
fn display_object(&self) -> &Instance<Host> {
|
||||
self.wrapped.display_object()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -1251,11 +1311,15 @@ mod tests {
|
||||
|
||||
impl Deref for TestedNode {
|
||||
type Target = Instance<()>;
|
||||
fn deref(&self) -> &Self::Target { &self.node }
|
||||
fn deref(&self) -> &Self::Target {
|
||||
&self.node
|
||||
}
|
||||
}
|
||||
|
||||
impl Object<()> for TestedNode {
|
||||
fn display_object(&self) -> &Instance<()> { &self.node }
|
||||
fn display_object(&self) -> &Instance<()> {
|
||||
&self.node
|
||||
}
|
||||
}
|
||||
|
||||
impl TestedNode {
|
||||
@ -1293,8 +1357,12 @@ mod tests {
|
||||
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) }
|
||||
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]
|
||||
@ -1418,7 +1486,6 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn deep_hierarchy_test() {
|
||||
|
||||
// === Init ===
|
||||
|
||||
let world = Instance::<()>::new(Logger::new("world"));
|
||||
|
@ -11,10 +11,19 @@ use crate::prelude::*;
|
||||
/// Defines the order in which particular axis coordinates are processed. Used for example to define
|
||||
/// the rotation order in `DisplayObject`.
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
pub enum AxisOrder {XYZ,XZY,YXZ,YZX,ZXY,ZYX}
|
||||
pub enum AxisOrder {
|
||||
XYZ,
|
||||
XZY,
|
||||
YXZ,
|
||||
YZX,
|
||||
ZXY,
|
||||
ZYX,
|
||||
}
|
||||
|
||||
impl Default for AxisOrder {
|
||||
fn default() -> Self { Self::XYZ }
|
||||
fn default() -> Self {
|
||||
Self::XYZ
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -32,11 +41,13 @@ pub enum TransformOrder {
|
||||
RotateScaleTranslate,
|
||||
RotateTranslateScale,
|
||||
TranslateRotateScale,
|
||||
TranslateScaleRotate
|
||||
TranslateScaleRotate,
|
||||
}
|
||||
|
||||
impl Default for TransformOrder {
|
||||
fn default() -> Self { Self::ScaleRotateTranslate }
|
||||
fn default() -> Self {
|
||||
Self::ScaleRotateTranslate
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -71,7 +82,9 @@ impl Default for Transform {
|
||||
|
||||
impl Transform {
|
||||
/// Creates a new transformation object.
|
||||
pub fn new() -> Self { default() }
|
||||
pub fn new() -> Self {
|
||||
default()
|
||||
}
|
||||
|
||||
/// Computes transformation matrix from the provided scale, rotation, and
|
||||
/// translation components, based on the transformation and rotation orders.
|
||||
|
@ -105,12 +105,12 @@ impl DerefMut for ComposerPass {
|
||||
impl ComposerPass {
|
||||
/// Constructor
|
||||
#[allow(clippy::borrowed_box)]
|
||||
pub fn new
|
||||
( context : &Context
|
||||
, variables : &UniformScope
|
||||
, mut pass : Box<dyn pass::Definition>
|
||||
, width : i32
|
||||
, height : i32
|
||||
pub fn new(
|
||||
context: &Context,
|
||||
variables: &UniformScope,
|
||||
mut pass: Box<dyn pass::Definition>,
|
||||
width: i32,
|
||||
height: i32,
|
||||
) -> Self {
|
||||
let instance = pass::Instance::new(context, variables, width, height);
|
||||
pass.initialize(&instance);
|
||||
|
@ -2,8 +2,8 @@
|
||||
|
||||
use crate::prelude::*;
|
||||
|
||||
use crate::system::gpu::*;
|
||||
use crate::system::gpu::data::texture::class::TextureOps;
|
||||
use crate::system::gpu::*;
|
||||
|
||||
|
||||
|
||||
@ -48,12 +48,7 @@ pub struct Instance {
|
||||
impl Instance {
|
||||
/// Constructor
|
||||
#[allow(clippy::borrowed_box)]
|
||||
pub fn new
|
||||
( context : &Context
|
||||
, variables : &UniformScope
|
||||
, width : i32
|
||||
, height : i32
|
||||
) -> Self {
|
||||
pub fn new(context: &Context, variables: &UniformScope, width: i32, height: i32) -> Self {
|
||||
let variables = variables.clone_ref();
|
||||
let context = context.clone();
|
||||
Self { variables, context, width, height }
|
||||
@ -69,7 +64,9 @@ impl Instance {
|
||||
let format = output.internal_format;
|
||||
let item_type = output.item_type;
|
||||
let params = Some(output.texture_parameters);
|
||||
uniform::get_or_add_gpu_texture_dyn(context,variables,&name,format,item_type,args,params)
|
||||
uniform::get_or_add_gpu_texture_dyn(
|
||||
context, variables, &name, format, item_type, args, params,
|
||||
)
|
||||
}
|
||||
|
||||
/// Create a new framebuffer from the provided textures.
|
||||
@ -86,7 +83,13 @@ impl Instance {
|
||||
let gl_texture = Some(&gl_texture);
|
||||
let level = 0;
|
||||
draw_buffers.push(&attachment_point.into());
|
||||
context.framebuffer_texture_2d(target,attachment_point,texture_target,gl_texture,level);
|
||||
context.framebuffer_texture_2d(
|
||||
target,
|
||||
attachment_point,
|
||||
texture_target,
|
||||
gl_texture,
|
||||
level,
|
||||
);
|
||||
}
|
||||
context.draw_buffers(&draw_buffers);
|
||||
context.bind_framebuffer(target, None);
|
||||
@ -113,8 +116,12 @@ pub struct OutputDefinition {
|
||||
|
||||
impl OutputDefinition {
|
||||
/// Constructor.
|
||||
pub fn new<Name:Str,F:Into<texture::AnyInternalFormat>,T:Into<texture::AnyItemType>>
|
||||
(name:Name, internal_format:F, item_type:T, texture_parameters:texture::Parameters) -> Self {
|
||||
pub fn new<Name: Str, F: Into<texture::AnyInternalFormat>, T: Into<texture::AnyItemType>>(
|
||||
name: Name,
|
||||
internal_format: F,
|
||||
item_type: T,
|
||||
texture_parameters: texture::Parameters,
|
||||
) -> Self {
|
||||
let name = name.into();
|
||||
let internal_format = internal_format.into();
|
||||
let item_type = item_type.into();
|
||||
|
@ -1,16 +1,16 @@
|
||||
//! Root module for render passes definitions.
|
||||
|
||||
pub mod symbols;
|
||||
pub mod pixel_read;
|
||||
pub mod screen;
|
||||
pub mod symbols;
|
||||
|
||||
|
||||
|
||||
/// Common types.
|
||||
pub mod types {
|
||||
use super::*;
|
||||
pub use symbols::*;
|
||||
pub use pixel_read::*;
|
||||
pub use screen::*;
|
||||
pub use symbols::*;
|
||||
}
|
||||
pub use types::*;
|
||||
|
@ -3,8 +3,8 @@
|
||||
use crate::prelude::*;
|
||||
|
||||
use crate::display::render::pass;
|
||||
use crate::system::gpu::*;
|
||||
use crate::system::gpu::data::texture::class::TextureOps;
|
||||
use crate::system::gpu::*;
|
||||
use crate::system::js::*;
|
||||
|
||||
use web_sys::WebGlBuffer;
|
||||
@ -29,12 +29,12 @@ pub struct PixelReadPassData<T:JsTypedArrayItem> {
|
||||
|
||||
impl<T: JsTypedArrayItem> PixelReadPassData<T> {
|
||||
/// Constructor.
|
||||
pub fn new
|
||||
( buffer : WebGlBuffer
|
||||
, framebuffer : WebGlFramebuffer
|
||||
, format : texture::AnyFormat
|
||||
, item_type : texture::AnyItemType
|
||||
, js_array : JsTypedArray<T>
|
||||
pub fn new(
|
||||
buffer: WebGlBuffer,
|
||||
framebuffer: WebGlFramebuffer,
|
||||
format: texture::AnyFormat,
|
||||
item_type: texture::AnyItemType,
|
||||
js_array: JsTypedArray<T>,
|
||||
) -> Self {
|
||||
Self { buffer, framebuffer, format, item_type, js_array }
|
||||
}
|
||||
@ -96,7 +96,7 @@ impl<T:JsTypedArrayItem> PixelReadPass<T> {
|
||||
|
||||
let texture = match variables.get("pass_id").unwrap() {
|
||||
uniform::AnyUniform::Texture(t) => t,
|
||||
_ => panic!("Pass internal error. Unmatched types.")
|
||||
_ => panic!("Pass internal error. Unmatched types."),
|
||||
};
|
||||
let format = texture.get_format();
|
||||
let item_type = texture.get_item_type();
|
||||
@ -108,7 +108,13 @@ impl<T:JsTypedArrayItem> PixelReadPass<T> {
|
||||
let gl_texture = Some(&gl_texture);
|
||||
let level = 0;
|
||||
context.bind_framebuffer(target, Some(&framebuffer));
|
||||
context.framebuffer_texture_2d(target,attachment_point,texture_target,gl_texture,level);
|
||||
context.framebuffer_texture_2d(
|
||||
target,
|
||||
attachment_point,
|
||||
texture_target,
|
||||
gl_texture,
|
||||
level,
|
||||
);
|
||||
|
||||
let data = PixelReadPassData::new(buffer, framebuffer, format, item_type, js_array);
|
||||
self.data = Some(data);
|
||||
@ -125,7 +131,9 @@ impl<T:JsTypedArrayItem> PixelReadPass<T> {
|
||||
let offset = 0;
|
||||
context.bind_framebuffer(Context::FRAMEBUFFER, Some(&data.framebuffer));
|
||||
context.bind_buffer(Context::PIXEL_PACK_BUFFER, Some(&data.buffer));
|
||||
context.read_pixels_with_i32(position.x,position.y,width,height,format,typ,offset).unwrap();
|
||||
context
|
||||
.read_pixels_with_i32(position.x, position.y, width, height, format, typ, offset)
|
||||
.unwrap();
|
||||
let condition = Context::SYNC_GPU_COMMANDS_COMPLETE;
|
||||
let flags = 0;
|
||||
let sync = context.fence_sync(condition, flags).unwrap();
|
||||
|
@ -3,12 +3,12 @@
|
||||
use crate::prelude::*;
|
||||
|
||||
use crate::display::render::pass;
|
||||
use crate::display::scene::layer;
|
||||
use crate::display::scene;
|
||||
use crate::display::scene::layer;
|
||||
use crate::display::scene::Scene;
|
||||
use crate::display::symbol::registry::SymbolRegistry;
|
||||
use crate::system::gpu::*;
|
||||
use crate::display::symbol::MaskComposer;
|
||||
use crate::system::gpu::*;
|
||||
|
||||
|
||||
|
||||
@ -24,11 +24,7 @@ struct Framebuffers {
|
||||
}
|
||||
|
||||
impl Framebuffers {
|
||||
fn new
|
||||
( composed : pass::Framebuffer
|
||||
, mask : pass::Framebuffer
|
||||
, layer : pass::Framebuffer
|
||||
) -> Self {
|
||||
fn new(composed: pass::Framebuffer, mask: pass::Framebuffer, layer: pass::Framebuffer) -> Self {
|
||||
Self { composed, mask, layer }
|
||||
}
|
||||
}
|
||||
@ -46,19 +42,19 @@ pub struct SymbolsRenderPass {
|
||||
|
||||
impl SymbolsRenderPass {
|
||||
/// Constructor.
|
||||
pub fn new
|
||||
( logger : impl AnyLogger
|
||||
, scene : &Scene
|
||||
, symbol_registry : &SymbolRegistry
|
||||
, layers : &scene::HardcodedLayers
|
||||
pub fn new(
|
||||
logger: impl AnyLogger,
|
||||
scene: &Scene,
|
||||
symbol_registry: &SymbolRegistry,
|
||||
layers: &scene::HardcodedLayers,
|
||||
) -> Self {
|
||||
let logger = Logger::new_sub(logger, "SymbolsRenderPass");
|
||||
let symbol_registry = symbol_registry.clone_ref();
|
||||
let layers = layers.clone_ref();
|
||||
let framebuffers = default();
|
||||
let scene = scene.clone_ref();
|
||||
let mask_composer = MaskComposer::new
|
||||
(&scene,"pass_mask_color","pass_layer_color","pass_layer_id");
|
||||
let mask_composer =
|
||||
MaskComposer::new(&scene, "pass_mask_color", "pass_layer_color", "pass_layer_id");
|
||||
Self { logger, symbol_registry, layers, framebuffers, scene, mask_composer }
|
||||
}
|
||||
}
|
||||
@ -106,8 +102,11 @@ impl pass::Definition for SymbolsRenderPass {
|
||||
let mut scissor_stack = default();
|
||||
self.render_layer(instance, &self.layers.root.clone(), &mut scissor_stack, false);
|
||||
if !scissor_stack.is_empty() {
|
||||
warning!(&self.logger,"The scissor stack was not cleaned properly. \
|
||||
This is an internal bug that may lead to visual artifacts. Please report it.");
|
||||
warning!(
|
||||
&self.logger,
|
||||
"The scissor stack was not cleaned properly. \
|
||||
This is an internal bug that may lead to visual artifacts. Please report it."
|
||||
);
|
||||
}
|
||||
instance.context.bind_framebuffer(Context::FRAMEBUFFER, None);
|
||||
}
|
||||
@ -122,7 +121,13 @@ impl SymbolsRenderPass {
|
||||
instance.context.disable(web_sys::WebGl2RenderingContext::SCISSOR_TEST);
|
||||
}
|
||||
|
||||
fn render_layer(&mut self, instance:&pass::Instance, layer:&layer::Layer, scissor_stack:&mut Vec<layer::ScissorBox>, parent_masked:bool) {
|
||||
fn render_layer(
|
||||
&mut self,
|
||||
instance: &pass::Instance,
|
||||
layer: &layer::Layer,
|
||||
scissor_stack: &mut Vec<layer::ScissorBox>,
|
||||
parent_masked: bool,
|
||||
) {
|
||||
let framebuffers = self.framebuffers.as_ref().unwrap();
|
||||
let parent_scissor_box = scissor_stack.first().copied();
|
||||
let layer_scissor_box = layer.scissor_box();
|
||||
@ -131,7 +136,9 @@ impl SymbolsRenderPass {
|
||||
let first_scissor_usage = scissor_box_changed && parent_scissor_box.is_none();
|
||||
if let Some(scissor_box) = scissor_box {
|
||||
if scissor_box_changed {
|
||||
if first_scissor_usage { self.enable_scissor_test(instance) }
|
||||
if first_scissor_usage {
|
||||
self.enable_scissor_test(instance)
|
||||
}
|
||||
scissor_stack.push(scissor_box);
|
||||
let position = scissor_box.position();
|
||||
let size = scissor_box.size();
|
||||
@ -145,7 +152,10 @@ impl SymbolsRenderPass {
|
||||
let nested_masking = is_masked && parent_masked;
|
||||
|
||||
if nested_masking {
|
||||
warning!(&self.logger,"Nested layer masking is not supported yet. Skipping nested masks.");
|
||||
warning!(
|
||||
&self.logger,
|
||||
"Nested layer masking is not supported yet. Skipping nested masks."
|
||||
);
|
||||
} else if let Some(mask) = layer_mask {
|
||||
framebuffers.mask.bind();
|
||||
let arr = vec![0.0, 0.0, 0.0, 0.0];
|
||||
@ -174,7 +184,9 @@ impl SymbolsRenderPass {
|
||||
|
||||
if scissor_box_changed {
|
||||
scissor_stack.pop();
|
||||
if first_scissor_usage { self.disable_scissor_test(instance) }
|
||||
if first_scissor_usage {
|
||||
self.disable_scissor_test(instance)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -13,31 +13,31 @@ use crate::prelude::*;
|
||||
|
||||
use crate::animation;
|
||||
use crate::control::callback;
|
||||
use crate::control::io::mouse::MouseManager;
|
||||
use crate::control::io::mouse;
|
||||
use crate::data::dirty::traits::*;
|
||||
use crate::control::io::mouse::MouseManager;
|
||||
use crate::data::dirty;
|
||||
use crate::data::dirty::traits::*;
|
||||
use crate::debug::stats::Stats;
|
||||
use crate::display;
|
||||
use crate::display::camera::Camera2d;
|
||||
use crate::display::render;
|
||||
use crate::display::scene::dom::DomScene;
|
||||
use crate::display::shape::ShapeSystemInstance;
|
||||
use crate::display::shape::system::ShapeSystemOf;
|
||||
use crate::display::shape::system::StaticShapeSystemInstance;
|
||||
use crate::display::style::data::DataMatch;
|
||||
use crate::display::shape::ShapeSystemInstance;
|
||||
use crate::display::style;
|
||||
use crate::display::style::data::DataMatch;
|
||||
use crate::display::symbol::registry::SymbolRegistry;
|
||||
use crate::display::symbol::Symbol;
|
||||
use crate::display::symbol::SymbolId;
|
||||
use crate::display::symbol::registry::SymbolRegistry;
|
||||
use crate::display;
|
||||
use crate::system::gpu::data::attribute;
|
||||
use crate::system::gpu::data::uniform::Uniform;
|
||||
use crate::system::gpu::data::uniform::UniformScope;
|
||||
use crate::system::gpu::shader::Context;
|
||||
use crate::system::web;
|
||||
use crate::system::web::IgnoreContextMenuHandle;
|
||||
use crate::system::web::NodeInserter;
|
||||
use crate::system::web::StyleSetter;
|
||||
use crate::system::web;
|
||||
|
||||
use enso_frp as frp;
|
||||
use enso_frp::io::js::CurrentJsEvent;
|
||||
@ -55,7 +55,6 @@ pub trait MouseTarget : Debug + 'static {
|
||||
|
||||
|
||||
|
||||
|
||||
// =====================
|
||||
// === ShapeRegistry ===
|
||||
// =====================
|
||||
@ -132,21 +131,17 @@ enum DecodingResult{
|
||||
/// Values had to be truncated.
|
||||
Truncated(u8, u8, u8),
|
||||
/// Values have been encoded successfully.
|
||||
Ok(u8,u8,u8)
|
||||
Ok(u8, u8, u8),
|
||||
}
|
||||
|
||||
/// Mouse target. Contains a path to an object pointed by mouse.
|
||||
#[derive(Debug, Clone, Copy, Eq, PartialEq)]
|
||||
pub enum PointerTarget {
|
||||
Background,
|
||||
Symbol {
|
||||
symbol_id : SymbolId,
|
||||
instance_id : attribute::InstanceIndex,
|
||||
}
|
||||
Symbol { symbol_id: SymbolId, instance_id: attribute::InstanceIndex },
|
||||
}
|
||||
|
||||
impl PointerTarget {
|
||||
|
||||
/// Encode two u32 values into three u8 values.
|
||||
///
|
||||
/// This is the same encoding that is used in the `fragment_runner`. This encoding is lossy and
|
||||
@ -202,23 +197,24 @@ impl PointerTarget {
|
||||
Self::Symbol { symbol_id, instance_id } => {
|
||||
match Self::encode(*symbol_id, (*instance_id) as u32) {
|
||||
DecodingResult::Truncated(pack0, pack1, pack2) => {
|
||||
warning!(logger,"Target values too big to encode: \
|
||||
({symbol_id},{instance_id}).");
|
||||
warning!(
|
||||
logger,
|
||||
"Target values too big to encode: \
|
||||
({symbol_id},{instance_id})."
|
||||
);
|
||||
Vector4::new(pack0.into(), pack1.into(), pack2.into(), 1)
|
||||
},
|
||||
DecodingResult::Ok(pack0,pack1,pack2) => {
|
||||
Vector4::new(pack0.into(),pack1.into(),pack2.into(),1)
|
||||
},
|
||||
}
|
||||
},
|
||||
DecodingResult::Ok(pack0, pack1, pack2) =>
|
||||
Vector4::new(pack0.into(), pack1.into(), pack2.into(), 1),
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn from_internal(v: Vector4<u32>) -> Self {
|
||||
if v.w == 0 {
|
||||
Self::Background
|
||||
}
|
||||
else if v.w == 255 {
|
||||
} else if v.w == 255 {
|
||||
let decoded = Self::decode(v.x, v.y, v.z);
|
||||
let symbol_id = SymbolId::new(decoded.0);
|
||||
let instance_id = attribute::InstanceIndex::new(decoded.1 as usize);
|
||||
@ -257,12 +253,12 @@ mod target_tests {
|
||||
match pack {
|
||||
DecodingResult::Truncated { .. } => {
|
||||
panic!("Values got truncated. This is an invalid test case: {}, {}", value1, value1)
|
||||
},
|
||||
}
|
||||
DecodingResult::Ok(pack0, pack1, pack2) => {
|
||||
let unpack = PointerTarget::decode(pack0.into(), pack1.into(), pack2.into());
|
||||
assert_eq!(unpack.0, value1);
|
||||
assert_eq!(unpack.1, value2);
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -313,17 +309,17 @@ pub struct Mouse {
|
||||
pub handles: Rc<[callback::Handle; 3]>,
|
||||
pub frp: enso_frp::io::Mouse,
|
||||
pub scene_frp: Frp,
|
||||
pub logger : Logger
|
||||
pub logger: Logger,
|
||||
}
|
||||
|
||||
impl Mouse {
|
||||
pub fn new
|
||||
( scene_frp : &Frp
|
||||
, root : &web::dom::WithKnownShape<web::HtmlDivElement>
|
||||
, variables : &UniformScope
|
||||
, current_js_event : &CurrentJsEvent
|
||||
, logger : Logger)
|
||||
-> Self {
|
||||
pub fn new(
|
||||
scene_frp: &Frp,
|
||||
root: &web::dom::WithKnownShape<web::HtmlDivElement>,
|
||||
variables: &UniformScope,
|
||||
current_js_event: &CurrentJsEvent,
|
||||
logger: Logger,
|
||||
) -> Self {
|
||||
let scene_frp = scene_frp.clone_ref();
|
||||
let target = PointerTarget::default();
|
||||
let last_position = Rc::new(Cell::new(Vector2::new(0, 0)));
|
||||
@ -349,15 +345,28 @@ impl Mouse {
|
||||
frp.position.emit(position);
|
||||
}
|
||||
}
|
||||
)));
|
||||
let on_down = mouse_manager.on_down.add(current_js_event.make_event_handler(
|
||||
f!((event:&mouse::OnDown) frp.down.emit(event.button())))
|
||||
),
|
||||
));
|
||||
let on_down = mouse_manager.on_down.add(
|
||||
current_js_event
|
||||
.make_event_handler(f!((event:&mouse::OnDown) frp.down.emit(event.button()))),
|
||||
);
|
||||
let on_up = mouse_manager.on_up.add(current_js_event.make_event_handler(
|
||||
f!((event:&mouse::OnUp) frp.up.emit(event.button())))
|
||||
let on_up = mouse_manager.on_up.add(
|
||||
current_js_event
|
||||
.make_event_handler(f!((event:&mouse::OnUp) frp.up.emit(event.button()))),
|
||||
);
|
||||
let handles = Rc::new([on_move, on_down, on_up]);
|
||||
Self {mouse_manager,last_position,position,hover_ids,target,handles,frp,scene_frp,logger}
|
||||
Self {
|
||||
mouse_manager,
|
||||
last_position,
|
||||
position,
|
||||
hover_ids,
|
||||
target,
|
||||
handles,
|
||||
frp,
|
||||
scene_frp,
|
||||
logger,
|
||||
}
|
||||
}
|
||||
|
||||
/// Re-emits FRP mouse changed position event with the last mouse position value.
|
||||
@ -369,9 +378,9 @@ impl Mouse {
|
||||
/// - Callback above is run. The value of `screen_position` uniform changes and FRP events are
|
||||
/// emitted.
|
||||
/// - FRP events propagate trough the whole system.
|
||||
/// - The rendering engine renders a frame and waits for the pixel read pass to report symbol
|
||||
/// ID under the cursor. This is normally done the next frame but sometimes could take even
|
||||
/// few frames.
|
||||
/// - The rendering engine renders a frame and waits for the pixel read pass to report symbol ID
|
||||
/// under the cursor. This is normally done the next frame but sometimes could take even few
|
||||
/// frames.
|
||||
/// - When the new ID are received, we emit `over` and `out` FRP events for appropriate
|
||||
/// elements.
|
||||
/// - After emitting `over` and `out `events, the `position` event is re-emitted.
|
||||
@ -404,7 +413,8 @@ impl Keyboard {
|
||||
pub fn new(current_event: &CurrentJsEvent) -> Self {
|
||||
let logger = Logger::new("keyboard");
|
||||
let frp = enso_frp::io::keyboard::Keyboard::default();
|
||||
let bindings = Rc::new(enso_frp::io::keyboard::DomBindings::new(&logger,&frp,current_event));
|
||||
let bindings =
|
||||
Rc::new(enso_frp::io::keyboard::DomBindings::new(&logger, &frp, current_event));
|
||||
Self { frp, bindings }
|
||||
}
|
||||
}
|
||||
@ -459,14 +469,13 @@ impl Dom {
|
||||
pub struct DomLayers {
|
||||
/// Back DOM scene layer.
|
||||
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.
|
||||
/// 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,
|
||||
/// The WebGL scene layer.
|
||||
pub canvas: web_sys::HtmlCanvasElement,
|
||||
|
||||
}
|
||||
|
||||
impl DomLayers {
|
||||
@ -570,8 +579,12 @@ impl Renderer {
|
||||
// - http://www.realtimerendering.com/blog/gpus-prefer-premultiplication
|
||||
// - https://www.khronos.org/opengl/wiki/Blending#Colors
|
||||
context.blend_equation_separate(Context::FUNC_ADD, Context::FUNC_ADD);
|
||||
context.blend_func_separate ( Context::ONE , Context::ONE_MINUS_SRC_ALPHA
|
||||
, Context::ONE , Context::ONE_MINUS_SRC_ALPHA );
|
||||
context.blend_func_separate(
|
||||
Context::ONE,
|
||||
Context::ONE_MINUS_SRC_ALPHA,
|
||||
Context::ONE,
|
||||
Context::ONE_MINUS_SRC_ALPHA,
|
||||
);
|
||||
|
||||
Self { logger, dom, context, variables, pipeline, composer }
|
||||
}
|
||||
@ -603,7 +616,6 @@ impl Renderer {
|
||||
|
||||
|
||||
|
||||
|
||||
// ==============
|
||||
// === Layers ===
|
||||
// ==============
|
||||
@ -661,23 +673,39 @@ impl HardcodedLayers {
|
||||
|
||||
let mask = Layer::new_with_cam(logger.sub("mask"), main_cam);
|
||||
node_searcher.set_mask(&node_searcher_mask);
|
||||
root.set_sublayers(
|
||||
&[ &viz
|
||||
, &below_main
|
||||
, &main
|
||||
, &port_selection
|
||||
, &label
|
||||
, &above_nodes
|
||||
, &above_nodes_text
|
||||
, &panel
|
||||
, &panel_text
|
||||
, &node_searcher
|
||||
, &tooltip
|
||||
, &tooltip_text
|
||||
, &cursor
|
||||
root.set_sublayers(&[
|
||||
&viz,
|
||||
&below_main,
|
||||
&main,
|
||||
&port_selection,
|
||||
&label,
|
||||
&above_nodes,
|
||||
&above_nodes_text,
|
||||
&panel,
|
||||
&panel_text,
|
||||
&node_searcher,
|
||||
&tooltip,
|
||||
&tooltip_text,
|
||||
&cursor,
|
||||
]);
|
||||
Self {root,viz,below_main,main,port_selection,label,above_nodes,above_nodes_text,panel
|
||||
,panel_text,node_searcher,node_searcher_mask,tooltip,tooltip_text,cursor,mask}
|
||||
Self {
|
||||
root,
|
||||
viz,
|
||||
below_main,
|
||||
main,
|
||||
port_selection,
|
||||
label,
|
||||
above_nodes,
|
||||
above_nodes_text,
|
||||
panel,
|
||||
panel_text,
|
||||
node_searcher,
|
||||
node_searcher_mask,
|
||||
tooltip,
|
||||
tooltip_text,
|
||||
cursor,
|
||||
mask,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -708,7 +736,14 @@ impl Frp {
|
||||
let shape = shape.clone_ref();
|
||||
let camera_changed = camera_changed_source.clone_ref().into();
|
||||
let frame_time = frame_time_source.clone_ref().into();
|
||||
Self {network,shape,camera_changed,frame_time,camera_changed_source,frame_time_source}
|
||||
Self {
|
||||
network,
|
||||
shape,
|
||||
camera_changed,
|
||||
frame_time,
|
||||
camera_changed_source,
|
||||
frame_time_source,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -769,8 +804,12 @@ pub struct SceneData {
|
||||
|
||||
impl SceneData {
|
||||
/// Create new instance with the provided on-dirty callback.
|
||||
pub fn new<OnMut:Fn()+Clone+'static>
|
||||
(parent_dom:&HtmlElement, logger:Logger, stats:&Stats, on_mut:OnMut) -> Self {
|
||||
pub fn new<OnMut: Fn() + Clone + 'static>(
|
||||
parent_dom: &HtmlElement,
|
||||
logger: Logger,
|
||||
stats: &Stats,
|
||||
on_mut: OnMut,
|
||||
) -> Self {
|
||||
debug!(logger, "Initializing.");
|
||||
|
||||
let dom = Dom::new(&logger);
|
||||
@ -822,9 +861,29 @@ impl SceneData {
|
||||
}
|
||||
|
||||
uniforms.pixel_ratio.set(dom.shape().pixel_ratio);
|
||||
Self {display_object,dom,context,symbols,variables,current_js_event,mouse,keyboard,uniforms
|
||||
,shapes,stats,dirty,logger,renderer,layers,style_sheet,bg_color_var,bg_color_change,frp
|
||||
,extensions,disable_context_menu}
|
||||
Self {
|
||||
display_object,
|
||||
dom,
|
||||
context,
|
||||
symbols,
|
||||
variables,
|
||||
current_js_event,
|
||||
mouse,
|
||||
keyboard,
|
||||
uniforms,
|
||||
shapes,
|
||||
stats,
|
||||
dirty,
|
||||
logger,
|
||||
renderer,
|
||||
layers,
|
||||
style_sheet,
|
||||
bg_color_var,
|
||||
bg_color_change,
|
||||
frp,
|
||||
extensions,
|
||||
disable_context_menu,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn shape(&self) -> &frp::Sampler<Shape> {
|
||||
@ -919,8 +978,11 @@ impl SceneData {
|
||||
}
|
||||
|
||||
/// Transforms screen position to the object (display object) coordinate system.
|
||||
pub fn screen_to_object_space
|
||||
(&self, object:&impl display::Object, screen_pos:Vector2) -> Vector2 {
|
||||
pub fn screen_to_object_space(
|
||||
&self,
|
||||
object: &impl display::Object,
|
||||
screen_pos: Vector2,
|
||||
) -> Vector2 {
|
||||
let origin_world_space = Vector4(0.0, 0.0, 0.0, 1.0);
|
||||
let layer = object.display_layers().first().and_then(|t| t.upgrade());
|
||||
let camera = layer.map_or(self.camera(), |l| l.camera());
|
||||
@ -951,12 +1013,16 @@ impl display::Object for SceneData {
|
||||
|
||||
#[derive(Clone, CloneRef, Debug)]
|
||||
pub struct Scene {
|
||||
no_mut_access : SceneData
|
||||
no_mut_access: SceneData,
|
||||
}
|
||||
|
||||
impl Scene {
|
||||
pub fn new<OnMut:Fn()+Clone+'static>
|
||||
(parent_dom:&HtmlElement, logger:impl AnyLogger, stats:&Stats, on_mut:OnMut) -> Self {
|
||||
pub fn new<OnMut: Fn() + Clone + 'static>(
|
||||
parent_dom: &HtmlElement,
|
||||
logger: impl AnyLogger,
|
||||
stats: &Stats,
|
||||
on_mut: OnMut,
|
||||
) -> Self {
|
||||
let logger = Logger::new_sub(logger, "scene");
|
||||
let no_mut_access = SceneData::new(parent_dom, logger, stats, on_mut);
|
||||
let this = Self { no_mut_access };
|
||||
|
@ -2,12 +2,12 @@
|
||||
|
||||
use crate::prelude::*;
|
||||
|
||||
use crate::display::object::traits::*;
|
||||
use crate::display::camera::Camera2d;
|
||||
use crate::display::camera::camera2d::Projection;
|
||||
use crate::display::symbol::DomSymbol;
|
||||
use crate::display::camera::Camera2d;
|
||||
use crate::display::object::traits::*;
|
||||
use crate::display::symbol::dom::eps;
|
||||
use crate::display::symbol::dom::inverse_y_translation;
|
||||
use crate::display::symbol::DomSymbol;
|
||||
use crate::system::gpu::data::JsBufferView;
|
||||
use crate::system::web;
|
||||
use crate::system::web::NodeInserter;
|
||||
@ -56,8 +56,11 @@ mod js {
|
||||
|
||||
/// Setup Camera perspective projection on DOM.
|
||||
#[allow(unsafe_code)]
|
||||
pub fn setup_camera_perspective
|
||||
(dom:&web::JsValue, near:&web::JsValue, matrix_array:&web::JsValue);
|
||||
pub fn setup_camera_perspective(
|
||||
dom: &web::JsValue,
|
||||
near: &web::JsValue,
|
||||
matrix_array: &web::JsValue,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@ -109,7 +112,7 @@ pub struct DomSceneData {
|
||||
pub dom: HtmlDivElement,
|
||||
/// The child div of the `dom` element with view-projection Css 3D transformations applied.
|
||||
pub view_projection_dom: HtmlDivElement,
|
||||
logger : Logger
|
||||
logger: Logger,
|
||||
}
|
||||
|
||||
impl DomSceneData {
|
||||
@ -197,7 +200,9 @@ impl DomScene {
|
||||
/// Update the objects to match the new camera's point of view. This function should be called
|
||||
/// only after camera position change.
|
||||
pub fn update_view_projection(&self, camera: &Camera2d) {
|
||||
if self.children_number() == 0 { return }
|
||||
if self.children_number() == 0 {
|
||||
return;
|
||||
}
|
||||
|
||||
let trans_cam = camera.transform_matrix().try_inverse();
|
||||
let trans_cam = trans_cam.expect("Camera's matrix is not invertible.");
|
||||
@ -211,7 +216,7 @@ impl DomScene {
|
||||
Projection::Perspective { .. } => {
|
||||
js::setup_perspective(&self.data.dom, &near.into());
|
||||
setup_camera_perspective(&self.data.view_projection_dom, near, &trans_cam);
|
||||
},
|
||||
}
|
||||
Projection::Orthographic => {
|
||||
setup_camera_orthographic(&self.data.view_projection_dom, &trans_cam);
|
||||
}
|
||||
|
@ -3,17 +3,17 @@
|
||||
use crate::data::dirty::traits::*;
|
||||
use crate::prelude::*;
|
||||
|
||||
use crate::data::OptVec;
|
||||
use crate::data::dirty;
|
||||
use crate::data::OptVec;
|
||||
use crate::display;
|
||||
use crate::display::camera::Camera2d;
|
||||
use crate::display::scene::Scene;
|
||||
use crate::display::shape::ShapeSystemInstance;
|
||||
use crate::display::shape::system::DynShapeSystemInstance;
|
||||
use crate::display::shape::system::DynShapeSystemOf;
|
||||
use crate::display::shape::system::KnownShapeSystemId;
|
||||
use crate::display::shape::system::ShapeSystemId;
|
||||
use crate::display::shape::ShapeSystemInstance;
|
||||
use crate::display::symbol::SymbolId;
|
||||
use crate::display;
|
||||
use crate::system::gpu::data::attribute;
|
||||
|
||||
use enso_data::dependency_graph::DependencyGraph;
|
||||
@ -28,9 +28,9 @@ use std::any::TypeId;
|
||||
// =============
|
||||
|
||||
/// Display layers implementation. Layer consist of a [`Camera`] and a set of [`LayerItem`]s. Layers
|
||||
/// are hierarchical and contain sublayers. Items of a layer containing sublayers layers are displayed
|
||||
/// below items of sublayers layers. Layers are allowed to share references to the same camera. and
|
||||
/// the same [`Symbol`]s.
|
||||
/// are hierarchical and contain sublayers. Items of a layer containing sublayers layers are
|
||||
/// displayed below items of sublayers layers. Layers are allowed to share references to the same
|
||||
/// camera. and the same [`Symbol`]s.
|
||||
///
|
||||
///
|
||||
/// # Symbol Management
|
||||
@ -74,10 +74,10 @@ use std::any::TypeId;
|
||||
/// # Symbols Ordering
|
||||
/// There are two ways to define symbol ordering in scene layers, a global, and local (per-layer)
|
||||
/// one. In order to define a global depth-order dependency, you can use the
|
||||
/// `add_elements_order_dependency`, and the `remove_elements_order_dependency` methods respectively.
|
||||
/// In order to define local (per-layer) depth-order dependency, you can use methods of the same
|
||||
/// names in every layer instance. After changing a dependency graph, the layer management marks
|
||||
/// appropriate dirty flags and re-orders symbols on each new frame processed.
|
||||
/// `add_elements_order_dependency`, and the `remove_elements_order_dependency` methods
|
||||
/// respectively. In order to define local (per-layer) depth-order dependency, you can use methods
|
||||
/// of the same names in every layer instance. After changing a dependency graph, the layer
|
||||
/// management marks appropriate dirty flags and re-orders symbols on each new frame processed.
|
||||
///
|
||||
/// During symbol sorting, the global and local dependency graphs are merged together. The defined
|
||||
/// rules are equivalently important, so local rules will not override global ones. In case of
|
||||
@ -122,9 +122,9 @@ use std::any::TypeId;
|
||||
/// # Layer Lifetime Management
|
||||
/// Both [`Group`] and every [`Layer`] instance are strongly interconnected. This is needed for a
|
||||
/// nice API. For example, [`Layer`] allows you to add symbols while removing them from other layers
|
||||
/// automatically. Although the [`SublayersModel`] registers [`WeakLayer`], the weak form is used only
|
||||
/// to break cycles and never points to a dropped [`Layer`], as layers update the information on
|
||||
/// a drop.
|
||||
/// automatically. Although the [`SublayersModel`] registers [`WeakLayer`], the weak form is used
|
||||
/// only to break cycles and never points to a dropped [`Layer`], as layers update the information
|
||||
/// on a drop.
|
||||
///
|
||||
/// # Masking Layers With ScissorBox
|
||||
/// Layers rendering an be limited to a specific set of pixels by using the [`ScissorBox`] object.
|
||||
@ -154,7 +154,7 @@ use std::any::TypeId;
|
||||
/// the nested masks will be skipped and a warning will be emitted to the console.
|
||||
#[derive(Clone, CloneRef)]
|
||||
pub struct Layer {
|
||||
model : Rc<LayerModel>
|
||||
model: Rc<LayerModel>,
|
||||
}
|
||||
|
||||
impl Deref for Layer {
|
||||
@ -197,8 +197,7 @@ impl Layer {
|
||||
}
|
||||
|
||||
/// Instantiate the provided [`DynamicShape`].
|
||||
pub fn instantiate<T>
|
||||
(&self, scene:&Scene, shape:&T) -> LayerDynamicShapeInstance
|
||||
pub fn instantiate<T>(&self, scene: &Scene, shape: &T) -> LayerDynamicShapeInstance
|
||||
where T: display::shape::system::DynamicShape {
|
||||
let (shape_system_info, symbol_id, instance_id) =
|
||||
self.shape_system_registry.instantiate(scene, shape);
|
||||
@ -254,7 +253,7 @@ impl From<&Layer> for LayerId {
|
||||
/// A weak version of [`Layer`].
|
||||
#[derive(Clone, CloneRef)]
|
||||
pub struct WeakLayer {
|
||||
model : Weak<LayerModel>
|
||||
model: Weak<LayerModel>,
|
||||
}
|
||||
|
||||
impl WeakLayer {
|
||||
@ -345,9 +344,23 @@ impl LayerModel {
|
||||
let mask = default();
|
||||
let scissor_box = default();
|
||||
let mem_mark = default();
|
||||
Self {logger,camera,shape_system_registry,shape_system_to_symbol_info_map
|
||||
,symbol_to_shape_system_map,elements,symbols_ordered,depth_order,depth_order_dirty
|
||||
,parents,global_element_depth_order,sublayers,mask,scissor_box,mem_mark}
|
||||
Self {
|
||||
logger,
|
||||
camera,
|
||||
shape_system_registry,
|
||||
shape_system_to_symbol_info_map,
|
||||
symbol_to_shape_system_map,
|
||||
elements,
|
||||
symbols_ordered,
|
||||
depth_order,
|
||||
depth_order_dirty,
|
||||
parents,
|
||||
global_element_depth_order,
|
||||
sublayers,
|
||||
mask,
|
||||
scissor_box,
|
||||
mem_mark,
|
||||
}
|
||||
}
|
||||
|
||||
/// Unique identifier of this layer. It is memory-based, it will be unique even for layers in
|
||||
@ -370,13 +383,16 @@ impl LayerModel {
|
||||
use LayerItem::*;
|
||||
match element {
|
||||
Symbol(id) => Some(id),
|
||||
ShapeSystem(id) => self.shape_system_to_symbol_info_map.borrow().get(&id).map(|t|t.id)
|
||||
ShapeSystem(id) => self.shape_system_to_symbol_info_map.borrow().get(&id).map(|t| t.id),
|
||||
}
|
||||
}
|
||||
|
||||
/// Add depth-order dependency between two [`LayerItem`]s in this layer.
|
||||
pub fn add_elements_order_dependency
|
||||
(&self, below:impl Into<LayerItem>, above:impl Into<LayerItem>) {
|
||||
pub fn add_elements_order_dependency(
|
||||
&self,
|
||||
below: impl Into<LayerItem>,
|
||||
above: impl Into<LayerItem>,
|
||||
) {
|
||||
let below = below.into();
|
||||
let above = above.into();
|
||||
if self.depth_order.borrow_mut().insert_dependency(below, above) {
|
||||
@ -386,12 +402,17 @@ impl LayerModel {
|
||||
|
||||
/// Remove a depth-order dependency between two [`LayerItem`]s in this layer. Returns `true`
|
||||
/// if the dependency was found, and `false` otherwise.
|
||||
pub fn remove_elements_order_dependency
|
||||
(&self, below:impl Into<LayerItem>, above:impl Into<LayerItem>) -> bool {
|
||||
pub fn remove_elements_order_dependency(
|
||||
&self,
|
||||
below: impl Into<LayerItem>,
|
||||
above: impl Into<LayerItem>,
|
||||
) -> bool {
|
||||
let below = below.into();
|
||||
let above = above.into();
|
||||
let found = self.depth_order.borrow_mut().remove_dependency(below, above);
|
||||
if found { self.depth_order_dirty.set(); }
|
||||
if found {
|
||||
self.depth_order_dirty.set();
|
||||
}
|
||||
found
|
||||
}
|
||||
|
||||
@ -402,10 +423,11 @@ impl LayerModel {
|
||||
/// This implementation can be simplified to `S1:KnownShapeSystemId` (not using [`Content`] at
|
||||
/// all), after the compiler gets updated to newer version.
|
||||
pub fn add_shapes_order_dependency<S1, S2>(&self) -> (PhantomData<S1>, PhantomData<S2>)
|
||||
where S1 : HasContent,
|
||||
where
|
||||
S1: HasContent,
|
||||
S2: HasContent,
|
||||
Content<S1>: KnownShapeSystemId,
|
||||
Content<S2> : KnownShapeSystemId {
|
||||
Content<S2>: KnownShapeSystemId, {
|
||||
let s1_id = <Content<S1>>::shape_system_id();
|
||||
let s2_id = <Content<S2>>::shape_system_id();
|
||||
self.add_elements_order_dependency(s1_id, s2_id);
|
||||
@ -420,16 +442,20 @@ impl LayerModel {
|
||||
/// # Future Improvements
|
||||
/// This implementation can be simplified to `S1:KnownShapeSystemId` (not using [`Content`] at
|
||||
/// all), after the compiler gets updated to newer version.
|
||||
pub fn remove_shapes_order_dependency<S1,S2>
|
||||
(&self) -> (bool,PhantomData<S1>,PhantomData<S2>)
|
||||
where S1 : HasContent,
|
||||
pub fn remove_shapes_order_dependency<S1, S2>(
|
||||
&self,
|
||||
) -> (bool, PhantomData<S1>, PhantomData<S2>)
|
||||
where
|
||||
S1: HasContent,
|
||||
S2: HasContent,
|
||||
Content<S1>: KnownShapeSystemId,
|
||||
Content<S2> : KnownShapeSystemId {
|
||||
Content<S2>: KnownShapeSystemId, {
|
||||
let s1_id = <Content<S1>>::shape_system_id();
|
||||
let s2_id = <Content<S2>>::shape_system_id();
|
||||
let found = self.remove_elements_order_dependency(s1_id, s2_id);
|
||||
if found { self.depth_order_dirty.set(); }
|
||||
if found {
|
||||
self.depth_order_dirty.set();
|
||||
}
|
||||
(found, default(), default())
|
||||
}
|
||||
|
||||
@ -450,8 +476,11 @@ impl LayerModel {
|
||||
}
|
||||
|
||||
/// Add the shape to this layer.
|
||||
pub(crate) fn add_shape
|
||||
(&self, shape_system_info:ShapeSystemInfo, symbol_id:impl Into<SymbolId>) {
|
||||
pub(crate) fn add_shape(
|
||||
&self,
|
||||
shape_system_info: ShapeSystemInfo,
|
||||
symbol_id: impl Into<SymbolId>,
|
||||
) {
|
||||
self.add_element(symbol_id.into(), Some(shape_system_info))
|
||||
}
|
||||
|
||||
@ -459,7 +488,9 @@ impl LayerModel {
|
||||
fn add_element(&self, symbol_id: SymbolId, shape_system_info: Option<ShapeSystemInfo>) {
|
||||
self.depth_order_dirty.set();
|
||||
match shape_system_info {
|
||||
None => { self.elements.borrow_mut().insert(LayerItem::Symbol(symbol_id)); }
|
||||
None => {
|
||||
self.elements.borrow_mut().insert(LayerItem::Symbol(symbol_id));
|
||||
}
|
||||
Some(info) => {
|
||||
let symbol_info = ShapeSystemSymbolInfo::new(symbol_id, info.ordering);
|
||||
self.shape_system_to_symbol_info_map.borrow_mut().insert(info.id, symbol_info);
|
||||
@ -476,7 +507,8 @@ impl LayerModel {
|
||||
|
||||
self.elements.borrow_mut().remove(&LayerItem::Symbol(symbol_id));
|
||||
if let Some(shape_system_id) =
|
||||
self.symbol_to_shape_system_map.borrow_mut().remove(&symbol_id) {
|
||||
self.symbol_to_shape_system_map.borrow_mut().remove(&symbol_id)
|
||||
{
|
||||
self.shape_system_to_symbol_info_map.borrow_mut().remove(&shape_system_id);
|
||||
self.elements.borrow_mut().remove(&LayerItem::ShapeSystem(shape_system_id));
|
||||
}
|
||||
@ -487,7 +519,8 @@ impl LayerModel {
|
||||
self.depth_order_dirty.set();
|
||||
self.elements.borrow_mut().remove(&LayerItem::ShapeSystem(shape_system_id));
|
||||
if let Some(symbol_id) =
|
||||
self.shape_system_to_symbol_info_map.borrow_mut().remove(&shape_system_id) {
|
||||
self.shape_system_to_symbol_info_map.borrow_mut().remove(&shape_system_id)
|
||||
{
|
||||
self.symbol_to_shape_system_map.borrow_mut().remove(&symbol_id.id);
|
||||
}
|
||||
}
|
||||
@ -498,8 +531,10 @@ impl LayerModel {
|
||||
}
|
||||
|
||||
/// Consume all dirty flags and update the ordering of elements if needed.
|
||||
pub(crate) fn update_internal
|
||||
(&self, global_element_depth_order:Option<&DependencyGraph<LayerItem>>) {
|
||||
pub(crate) fn update_internal(
|
||||
&self,
|
||||
global_element_depth_order: Option<&DependencyGraph<LayerItem>>,
|
||||
) {
|
||||
if self.depth_order_dirty.check() {
|
||||
self.depth_order_dirty.unset();
|
||||
self.depth_sort(global_element_depth_order);
|
||||
@ -522,9 +557,10 @@ impl LayerModel {
|
||||
/// dependency graph (from [`Group`]), the local one (per layer), and individual shape
|
||||
/// preferences (see the "Compile Time Shapes Ordering Relations" section in docs of [`Group`]
|
||||
/// to learn more).
|
||||
fn combined_depth_order_graph
|
||||
(&self, global_element_depth_order:Option<&DependencyGraph<LayerItem>>)
|
||||
-> DependencyGraph<LayerItem> {
|
||||
fn combined_depth_order_graph(
|
||||
&self,
|
||||
global_element_depth_order: Option<&DependencyGraph<LayerItem>>,
|
||||
) -> DependencyGraph<LayerItem> {
|
||||
let mut graph = if let Some(global_element_depth_order) = global_element_depth_order {
|
||||
let mut graph = global_element_depth_order.clone();
|
||||
graph.extend(self.depth_order.borrow().clone().into_iter());
|
||||
@ -535,11 +571,15 @@ impl LayerModel {
|
||||
for element in &*self.elements.borrow() {
|
||||
if let LayerItem::ShapeSystem(id) = element {
|
||||
if let Some(info) = self.shape_system_to_symbol_info_map.borrow().get(id) {
|
||||
for &id2 in &info.below { graph.insert_dependency(*element,id2.into()); }
|
||||
for &id2 in &info.above { graph.insert_dependency(id2.into(),*element); }
|
||||
for &id2 in &info.below {
|
||||
graph.insert_dependency(*element, id2.into());
|
||||
}
|
||||
for &id2 in &info.above {
|
||||
graph.insert_dependency(id2.into(), *element);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
graph
|
||||
}
|
||||
|
||||
@ -547,20 +587,22 @@ impl LayerModel {
|
||||
let graph = self.combined_depth_order_graph(global_element_depth_order);
|
||||
let elements_sorted = self.elements.borrow().iter().copied().collect_vec();
|
||||
let sorted_elements = graph.into_unchecked_topo_sort(elements_sorted);
|
||||
let sorted_symbols = sorted_elements.into_iter().filter_map(|element| {
|
||||
match element {
|
||||
let sorted_symbols = sorted_elements
|
||||
.into_iter()
|
||||
.filter_map(|element| match element {
|
||||
LayerItem::Symbol(symbol_id) => Some(symbol_id),
|
||||
LayerItem::ShapeSystem(id) => {
|
||||
let out = self.shape_system_to_symbol_info_map.borrow().get(&id).map(|t| t.id);
|
||||
if out.is_none() {
|
||||
warning!(self.logger,
|
||||
warning!(
|
||||
self.logger,
|
||||
"Trying to perform depth-order of non-existing element '{id:?}'."
|
||||
)
|
||||
}
|
||||
out
|
||||
}
|
||||
}
|
||||
}).collect();
|
||||
})
|
||||
.collect();
|
||||
*self.symbols_ordered.borrow_mut() = sorted_symbols;
|
||||
}
|
||||
}
|
||||
@ -648,23 +690,33 @@ impl LayerModel {
|
||||
/// Add depth-order dependency between two [`LayerItem`]s in this layer. Returns `true`
|
||||
/// if the dependency was inserted successfully (was not already present), and `false`
|
||||
/// otherwise. All sublayers will inherit these rules.
|
||||
pub fn add_global_elements_order_dependency
|
||||
(&self, below:impl Into<LayerItem>, above:impl Into<LayerItem>) -> bool {
|
||||
pub fn add_global_elements_order_dependency(
|
||||
&self,
|
||||
below: impl Into<LayerItem>,
|
||||
above: impl Into<LayerItem>,
|
||||
) -> bool {
|
||||
let below = below.into();
|
||||
let above = above.into();
|
||||
let fresh = self.global_element_depth_order.borrow_mut().insert_dependency(below, above);
|
||||
if fresh { self.sublayers.element_depth_order_dirty.set(); }
|
||||
if fresh {
|
||||
self.sublayers.element_depth_order_dirty.set();
|
||||
}
|
||||
fresh
|
||||
}
|
||||
|
||||
/// Remove a depth-order dependency between two [`LayerItem`]s in this layer. Returns `true`
|
||||
/// if the dependency was found, and `false` otherwise.
|
||||
pub fn remove_global_elements_order_dependency
|
||||
(&self, below:impl Into<LayerItem>, above:impl Into<LayerItem>) -> bool {
|
||||
pub fn remove_global_elements_order_dependency(
|
||||
&self,
|
||||
below: impl Into<LayerItem>,
|
||||
above: impl Into<LayerItem>,
|
||||
) -> bool {
|
||||
let below = below.into();
|
||||
let above = above.into();
|
||||
let found = self.global_element_depth_order.borrow_mut().remove_dependency(below, above);
|
||||
if found { self.sublayers.element_depth_order_dirty.set(); }
|
||||
if found {
|
||||
self.sublayers.element_depth_order_dirty.set();
|
||||
}
|
||||
found
|
||||
}
|
||||
|
||||
@ -672,12 +724,14 @@ impl LayerModel {
|
||||
/// This implementation can be simplified to `S1:KnownShapeSystemId` (not using [`Content`] at
|
||||
/// all), after the compiler gets updated to newer version. Returns `true` if the dependency was
|
||||
/// inserted successfully (was not already present), and `false` otherwise.
|
||||
pub fn add_global_shapes_order_dependency<S1,S2>
|
||||
(&self) -> (bool,PhantomData<S1>,PhantomData<S2>) where
|
||||
pub fn add_global_shapes_order_dependency<S1, S2>(
|
||||
&self,
|
||||
) -> (bool, PhantomData<S1>, PhantomData<S2>)
|
||||
where
|
||||
S1: HasContent,
|
||||
S2: HasContent,
|
||||
Content<S1>: KnownShapeSystemId,
|
||||
Content<S2> : KnownShapeSystemId {
|
||||
Content<S2>: KnownShapeSystemId, {
|
||||
let s1_id = <Content<S1>>::shape_system_id();
|
||||
let s2_id = <Content<S2>>::shape_system_id();
|
||||
let fresh = self.add_global_elements_order_dependency(s1_id, s2_id);
|
||||
@ -688,12 +742,14 @@ impl LayerModel {
|
||||
/// This implementation can be simplified to `S1:KnownShapeSystemId` (not using [`Content`] at
|
||||
/// all), after the compiler gets updated to newer version. Returns `true` if the dependency was
|
||||
/// found, and `false` otherwise.
|
||||
pub fn remove_global_shapes_order_dependency<S1,S2>
|
||||
(&self) -> (bool,PhantomData<S1>,PhantomData<S2>) where
|
||||
pub fn remove_global_shapes_order_dependency<S1, S2>(
|
||||
&self,
|
||||
) -> (bool, PhantomData<S1>, PhantomData<S2>)
|
||||
where
|
||||
S1: HasContent,
|
||||
S2: HasContent,
|
||||
Content<S1>: KnownShapeSystemId,
|
||||
Content<S2> : KnownShapeSystemId {
|
||||
Content<S2>: KnownShapeSystemId, {
|
||||
let s1_id = <Content<S1>>::shape_system_id();
|
||||
let s2_id = <Content<S2>>::shape_system_id();
|
||||
let found = self.remove_global_elements_order_dependency(s1_id, s2_id);
|
||||
@ -736,7 +792,7 @@ impl std::borrow::Borrow<LayerModel> for Layer {
|
||||
pub struct LayerDynamicShapeInstance {
|
||||
pub layer: WeakLayer,
|
||||
pub symbol_id: SymbolId,
|
||||
pub instance_id : attribute::InstanceIndex
|
||||
pub instance_id: attribute::InstanceIndex,
|
||||
}
|
||||
|
||||
impl LayerDynamicShapeInstance {
|
||||
@ -846,7 +902,7 @@ newtype_prim! {
|
||||
#[allow(missing_docs)]
|
||||
pub enum LayerItem {
|
||||
Symbol(SymbolId),
|
||||
ShapeSystem (ShapeSystemId)
|
||||
ShapeSystem(ShapeSystemId),
|
||||
}
|
||||
|
||||
impl From<ShapeSystemId> for LayerItem {
|
||||
@ -943,9 +999,10 @@ impl ShapeSystemRegistryData {
|
||||
self.shape_system_map.get_mut(&id).and_then(|t| {
|
||||
let shape_system = t.shape_system.downcast_mut::<T>();
|
||||
let instance_count = &mut t.instance_count;
|
||||
shape_system.map(move |shape_system|
|
||||
ShapeSystemRegistryEntryRefMut {shape_system,instance_count}
|
||||
)
|
||||
shape_system.map(move |shape_system| ShapeSystemRegistryEntryRefMut {
|
||||
shape_system,
|
||||
instance_count,
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
@ -960,12 +1017,13 @@ impl ShapeSystemRegistryData {
|
||||
self.get_mut().unwrap()
|
||||
}
|
||||
|
||||
fn with_get_or_register_mut<T,F,Out>
|
||||
(&mut self, scene:&Scene, f:F) -> Out
|
||||
where F:FnOnce(ShapeSystemRegistryEntryRefMut<T>)->Out, T:ShapeSystemInstance {
|
||||
fn with_get_or_register_mut<T, F, Out>(&mut self, scene: &Scene, f: F) -> Out
|
||||
where
|
||||
F: FnOnce(ShapeSystemRegistryEntryRefMut<T>) -> Out,
|
||||
T: ShapeSystemInstance, {
|
||||
match self.get_mut() {
|
||||
Some(entry) => f(entry),
|
||||
None => f(self.register(scene))
|
||||
None => f(self.register(scene)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -2,10 +2,10 @@
|
||||
|
||||
pub mod def;
|
||||
pub mod shader;
|
||||
pub mod system;
|
||||
pub mod style_watch;
|
||||
pub mod system;
|
||||
|
||||
pub use def::*;
|
||||
pub use shader::*;
|
||||
pub use system::*;
|
||||
pub use style_watch::*;
|
||||
pub use system::*;
|
||||
|
@ -1,8 +1,8 @@
|
||||
//! This module is the root module for all primitive shapes and shape transform definitions.
|
||||
|
||||
pub mod primitive;
|
||||
pub mod class;
|
||||
pub mod modifier;
|
||||
pub mod primitive;
|
||||
pub mod unit;
|
||||
pub mod var;
|
||||
|
||||
|
@ -2,13 +2,13 @@
|
||||
|
||||
use crate::prelude::*;
|
||||
|
||||
use super::unit::*;
|
||||
use super::modifier::*;
|
||||
use super::unit::*;
|
||||
|
||||
use crate::data::color;
|
||||
use crate::display::shape::primitive::def::var::Var;
|
||||
use crate::display::shape::primitive::shader::canvas;
|
||||
use crate::display::shape::primitive::shader::canvas::Canvas;
|
||||
use crate::display::shape::primitive::def::var::Var;
|
||||
use crate::data::color;
|
||||
|
||||
|
||||
|
||||
@ -23,7 +23,7 @@ pub trait Shape = 'static + canvas::Draw;
|
||||
/// as a generic shape type.
|
||||
#[derive(Debug, Clone, CloneRef)]
|
||||
pub struct AnyShape {
|
||||
rc: Rc<dyn canvas::Draw>
|
||||
rc: Rc<dyn canvas::Draw>,
|
||||
}
|
||||
|
||||
impl AsOwned for AnyShape {
|
||||
@ -53,7 +53,7 @@ impl canvas::Draw for AnyShape {
|
||||
#[derive(Debug, Derivative, Shrinkwrap)]
|
||||
#[derivative(Clone(bound = ""))]
|
||||
pub struct ShapeRef<T> {
|
||||
rc:Rc<T>
|
||||
rc: Rc<T>,
|
||||
}
|
||||
|
||||
impl<T> From<&ShapeRef<T>> for ShapeRef<T> {
|
||||
@ -94,7 +94,8 @@ impl<T> ShapeOps for ShapeRef<T> {}
|
||||
impl ShapeOps for AnyShape {}
|
||||
|
||||
/// Methods implemented by every shape.
|
||||
pub trait ShapeOps : Sized where for<'t> &'t Self : IntoOwned<Owned=Self> {
|
||||
pub trait ShapeOps: Sized
|
||||
where for<'t> &'t Self: IntoOwned<Owned = Self> {
|
||||
/// Translate the shape by a given offset.
|
||||
fn translate<V: Into<Var<Vector2<Pixels>>>>(&self, v: V) -> Translate<Self> {
|
||||
Translate(self, v)
|
||||
|
@ -10,8 +10,8 @@ use crate::data::color::*;
|
||||
use crate::display::shape::primitive::def::class::AnyShape;
|
||||
use crate::display::shape::primitive::def::class::ShapeRef;
|
||||
use crate::display::shape::primitive::def::var::Var;
|
||||
use crate::display::shape::primitive::shader::canvas::Canvas;
|
||||
use crate::display::shape::primitive::shader::canvas;
|
||||
use crate::display::shape::primitive::shader::canvas::Canvas;
|
||||
|
||||
|
||||
|
||||
|
@ -10,11 +10,11 @@ use inflector::Inflector;
|
||||
|
||||
use crate::display::shape::primitive::def::class::AnyShape;
|
||||
use crate::display::shape::primitive::def::class::ShapeRef;
|
||||
use crate::display::shape::primitive::shader::canvas::Canvas;
|
||||
use crate::display::shape::primitive::shader::canvas;
|
||||
use crate::display::shape::primitive::shader::canvas::Canvas;
|
||||
use crate::display::shape::Var;
|
||||
use crate::system::gpu::shader::glsl::Glsl;
|
||||
use crate::system::gpu::shader::glsl::traits::*;
|
||||
use crate::system::gpu::shader::glsl::Glsl;
|
||||
|
||||
|
||||
|
||||
@ -449,12 +449,19 @@ impl Rect {
|
||||
}
|
||||
|
||||
/// Sets the radiuses of each of the corners.
|
||||
pub fn corners_radiuses<T1,T2,T3,T4>
|
||||
(&self, top_left:T1, top_right:T2, bottom_left:T3, bottom_right:T4) -> RoundedRectByCorner
|
||||
where T1 : Into<Var<Pixels>> ,
|
||||
pub fn corners_radiuses<T1, T2, T3, T4>(
|
||||
&self,
|
||||
top_left: T1,
|
||||
top_right: T2,
|
||||
bottom_left: T3,
|
||||
bottom_right: T4,
|
||||
) -> RoundedRectByCorner
|
||||
where
|
||||
T1: Into<Var<Pixels>>,
|
||||
T2: Into<Var<Pixels>>,
|
||||
T3: Into<Var<Pixels>>,
|
||||
T4 : Into<Var<Pixels>> {
|
||||
T4: Into<Var<Pixels>>,
|
||||
{
|
||||
RoundedRectByCorner(self.size(), top_left, top_right, bottom_left, bottom_right)
|
||||
}
|
||||
|
||||
|
@ -4,9 +4,9 @@ use super::var::*;
|
||||
|
||||
use crate::types::topology;
|
||||
|
||||
pub use crate::types::topology::Degrees;
|
||||
pub use crate::types::topology::Pixels;
|
||||
pub use crate::types::topology::Radians;
|
||||
pub use crate::types::topology::Degrees;
|
||||
|
||||
|
||||
|
||||
@ -43,11 +43,11 @@ impl PixelDistance for f32 {
|
||||
|
||||
/// Common types.
|
||||
pub mod types {
|
||||
use super::*;
|
||||
pub use super::PixelDistance;
|
||||
use super::*;
|
||||
pub use topology::Degrees;
|
||||
pub use topology::Pixels;
|
||||
pub use topology::Radians;
|
||||
pub use topology::Degrees;
|
||||
}
|
||||
|
||||
pub use types::*;
|
||||
|
@ -22,7 +22,8 @@ use std::ops::*;
|
||||
pub trait VarInitializer<T> = VarInitializerMarker<T> + Into<Glsl>;
|
||||
|
||||
/// Marker trait for `VarInitializer`.
|
||||
#[marker] pub trait VarInitializerMarker<T> {}
|
||||
#[marker]
|
||||
pub trait VarInitializerMarker<T> {}
|
||||
|
||||
|
||||
// === Instances ===
|
||||
@ -42,7 +43,12 @@ impl VarInitializerMarker<Var<color::Rgba>> for color::Rgba {}
|
||||
impl<G> VarInitializerMarker<Var<color::Rgba>> for color::gradient::SdfSampler<G> {}
|
||||
|
||||
impl<T, S1, S2> VarInitializerMarker<Var<Vector2<T>>> for (S1, S2)
|
||||
where T:Scalar, S1:VarInitializerMarkerNested<Var<T>>, S2:VarInitializerMarkerNested<Var<T>> {}
|
||||
where
|
||||
T: Scalar,
|
||||
S1: VarInitializerMarkerNested<Var<T>>,
|
||||
S2: VarInitializerMarkerNested<Var<T>>,
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
|
||||
@ -88,26 +94,34 @@ impl Var<Pixels> {
|
||||
|
||||
impl Var<color::Rgba> {
|
||||
/// Build a color from its components.
|
||||
pub fn rgba(r:impl Into<Var<f32>>, g:impl Into<Var<f32>>, b:impl Into<Var<f32>>, a:impl Into<Var<f32>>)
|
||||
-> Var<color::Rgba> {
|
||||
format!("srgba({},{},{},{})",
|
||||
pub fn rgba(
|
||||
r: impl Into<Var<f32>>,
|
||||
g: impl Into<Var<f32>>,
|
||||
b: impl Into<Var<f32>>,
|
||||
a: impl Into<Var<f32>>,
|
||||
) -> Var<color::Rgba> {
|
||||
format!(
|
||||
"srgba({},{},{},{})",
|
||||
r.into().glsl(),
|
||||
g.into().glsl(),
|
||||
b.into().glsl(),
|
||||
a.into().glsl()
|
||||
).into()
|
||||
)
|
||||
.into()
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, S> From<T> for Var<S>
|
||||
where T : VarInitializer<Var<S>> {
|
||||
where T: VarInitializer<Var<S>>
|
||||
{
|
||||
default fn from(t: T) -> Self {
|
||||
Self::Dynamic(t.into())
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> From<T> for Var<T>
|
||||
where T : VarInitializer<Var<T>> {
|
||||
where T: VarInitializer<Var<T>>
|
||||
{
|
||||
fn from(t: T) -> Self {
|
||||
Self::Static(t)
|
||||
}
|
||||
@ -138,17 +152,19 @@ impls! {[T:Into<Glsl>] From<Var<T>> for Glsl { |t|
|
||||
// ==================
|
||||
|
||||
impl<T> Abs for Var<T>
|
||||
where T:Abs {
|
||||
where T: Abs
|
||||
{
|
||||
fn abs(&self) -> Self {
|
||||
match self {
|
||||
Self::Static(t) => Var::Static(t.abs()),
|
||||
Self::Dynamic (t) => Var::Dynamic(format!("abs({})",t).into())
|
||||
Self::Dynamic(t) => Var::Dynamic(format!("abs({})", t).into()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> Min for Var<T>
|
||||
where T:Min+Into<Glsl> {
|
||||
where T: Min + Into<Glsl>
|
||||
{
|
||||
fn min(a: Self, b: Self) -> Self {
|
||||
match (a, b) {
|
||||
(Var::Static(a), Var::Static(b)) => Var::Static(Min::min(a, b)),
|
||||
@ -156,13 +172,14 @@ where T:Min+Into<Glsl> {
|
||||
let a: Glsl = a.into();
|
||||
let b: Glsl = b.into();
|
||||
Var::Dynamic(format!("min({},{})", a.glsl(), b.glsl()).into())
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> Max for Var<T>
|
||||
where T:Max+Into<Glsl> {
|
||||
where T: Max + Into<Glsl>
|
||||
{
|
||||
fn max(a: Self, b: Self) -> Self {
|
||||
match (a, b) {
|
||||
(Var::Static(a), Var::Static(b)) => Var::Static(Max::max(a, b)),
|
||||
@ -170,7 +187,7 @@ where T:Max+Into<Glsl> {
|
||||
let a: Glsl = a.into();
|
||||
let b: Glsl = b.into();
|
||||
Var::Dynamic(format!("max({},{})", a.glsl(), b.glsl()).into())
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -187,7 +204,7 @@ impl<T:Scalar> Dim1 for Var<Vector2<T>> {
|
||||
fn x(&self) -> Var<T> {
|
||||
match self {
|
||||
Self::Static(t) => Var::Static(t.x.clone()),
|
||||
Self::Dynamic (t) => Var::Dynamic(format!("{}.x",t).into())
|
||||
Self::Dynamic(t) => Var::Dynamic(format!("{}.x", t).into()),
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -196,7 +213,7 @@ impl<T:Scalar> Dim2 for Var<Vector2<T>> {
|
||||
fn y(&self) -> Var<T> {
|
||||
match self {
|
||||
Self::Static(t) => Var::Static(t.y.clone()),
|
||||
Self::Dynamic (t) => Var::Dynamic(format!("{}.y",t).into())
|
||||
Self::Dynamic(t) => Var::Dynamic(format!("{}.y", t).into()),
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -205,7 +222,7 @@ impl<T:Scalar> Dim1 for Var<Vector3<T>> {
|
||||
fn x(&self) -> Var<T> {
|
||||
match self {
|
||||
Self::Static(t) => Var::Static(t.x.clone()),
|
||||
Self::Dynamic (t) => Var::Dynamic(format!("{}.x",t).into())
|
||||
Self::Dynamic(t) => Var::Dynamic(format!("{}.x", t).into()),
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -214,7 +231,7 @@ impl<T:Scalar> Dim2 for Var<Vector3<T>> {
|
||||
fn y(&self) -> Var<T> {
|
||||
match self {
|
||||
Self::Static(t) => Var::Static(t.y.clone()),
|
||||
Self::Dynamic (t) => Var::Dynamic(format!("{}.y",t).into())
|
||||
Self::Dynamic(t) => Var::Dynamic(format!("{}.y", t).into()),
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -223,7 +240,7 @@ impl<T:Scalar> Dim3 for Var<Vector3<T>> {
|
||||
fn z(&self) -> Var<T> {
|
||||
match self {
|
||||
Self::Static(t) => Var::Static(t.z.clone()),
|
||||
Self::Dynamic (t) => Var::Dynamic(format!("{}.z",t).into())
|
||||
Self::Dynamic(t) => Var::Dynamic(format!("{}.z", t).into()),
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -234,7 +251,7 @@ impl PixelDistance for Var<Vector2<f32>> {
|
||||
fn px(&self) -> Self::Output {
|
||||
match self {
|
||||
Self::Static(t) => Var::Static(Vector2(t.x.pixels(), t.y.pixels())),
|
||||
Self::Dynamic (t) => Var::Dynamic(t.clone())
|
||||
Self::Dynamic(t) => Var::Dynamic(t.clone()),
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -244,7 +261,7 @@ impl PixelDistance for Var<Vector3<f32>> {
|
||||
fn px(&self) -> Self::Output {
|
||||
match self {
|
||||
Self::Static(t) => Var::Static(Vector3(t.x.pixels(), t.y.pixels(), t.z.pixels())),
|
||||
Self::Dynamic (t) => Var::Dynamic(t.clone())
|
||||
Self::Dynamic(t) => Var::Dynamic(t.clone()),
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -320,7 +337,7 @@ macro_rules! define_shape_data_operator {
|
||||
}
|
||||
}
|
||||
}}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
macro_rules! define_shape_data_prim_operator {
|
||||
@ -371,7 +388,8 @@ define_shape_data_prim_operator! { Sub sub (-) for f32 where [A:RefInto<Glsl>] }
|
||||
define_shape_data_prim_operator! { Rem rem (%) for f32 where [A:RefInto<Glsl>] }
|
||||
|
||||
impl<T> Neg for Var<T>
|
||||
where T : Neg + RefInto<Glsl> {
|
||||
where T: Neg + RefInto<Glsl>
|
||||
{
|
||||
type Output = Var<<T as Neg>::Output>;
|
||||
fn neg(self) -> Self::Output {
|
||||
match self {
|
||||
@ -382,7 +400,8 @@ where T : Neg + RefInto<Glsl> {
|
||||
}
|
||||
|
||||
impl<'t, T> Neg for &'t Var<T>
|
||||
where &'t T : Neg + Into<Glsl> {
|
||||
where &'t T: Neg + Into<Glsl>
|
||||
{
|
||||
type Output = Var<<&'t T as Neg>::Output>;
|
||||
fn neg(self) -> Self::Output {
|
||||
match self {
|
||||
@ -400,13 +419,14 @@ macro_rules! define_shape_data_string_operator {
|
||||
define_shape_data_string_operator_ref! { $name $fn ($opr) for str }
|
||||
define_shape_data_string_operator_no_ref! { $name $fn ($opr) for String }
|
||||
define_shape_data_string_operator_no_ref! { $name $fn ($opr) for CowString }
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
macro_rules! define_shape_data_string_operator_ref {
|
||||
( $name:ident $fn:ident ($opr:tt) for $target:ident ) => {
|
||||
impl<'t, A> $name<&'t $target> for &'t Var<A>
|
||||
where A : RefInto<Glsl> {
|
||||
where A: RefInto<Glsl>
|
||||
{
|
||||
type Output = Var<A>;
|
||||
fn $fn(self, rhs: &'t $target) -> Self::Output {
|
||||
Var::Dynamic(format!("{}({},{})", stringify!($fn), self.glsl(), rhs).into())
|
||||
@ -414,7 +434,8 @@ macro_rules! define_shape_data_string_operator_ref {
|
||||
}
|
||||
|
||||
impl<'t, A> $name<&'t $target> for Var<A>
|
||||
where A : RefInto<Glsl> {
|
||||
where A: RefInto<Glsl>
|
||||
{
|
||||
type Output = Var<A>;
|
||||
fn $fn(self, rhs: &'t $target) -> Self::Output {
|
||||
Var::Dynamic(format!("{}({},{})", stringify!($fn), self.glsl(), rhs).into())
|
||||
@ -422,7 +443,8 @@ macro_rules! define_shape_data_string_operator_ref {
|
||||
}
|
||||
|
||||
impl<'t, A> $name<&'t Var<A>> for &'t $target
|
||||
where A : Display + RefInto<Glsl> {
|
||||
where A: Display + RefInto<Glsl>
|
||||
{
|
||||
type Output = Var<A>;
|
||||
fn $fn(self, rhs: &'t Var<A>) -> Self::Output {
|
||||
Var::Dynamic(format!("{}({},{})", stringify!($fn), self.glsl(), rhs).into())
|
||||
@ -430,19 +452,21 @@ macro_rules! define_shape_data_string_operator_ref {
|
||||
}
|
||||
|
||||
impl<'t, A> $name<Var<A>> for &'t $target
|
||||
where A : Display + RefInto<Glsl> {
|
||||
where A: Display + RefInto<Glsl>
|
||||
{
|
||||
type Output = Var<A>;
|
||||
fn $fn(self, rhs: Var<A>) -> Self::Output {
|
||||
Var::Dynamic(format!("{}({},{})", stringify!($fn), self.glsl(), rhs).into())
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
macro_rules! define_shape_data_string_operator_no_ref {
|
||||
( $name:ident $fn:ident ($opr:tt) for $target:ident ) => {
|
||||
impl<'t, A> $name<$target> for &'t Var<A>
|
||||
where A : RefInto<Glsl> {
|
||||
where A: RefInto<Glsl>
|
||||
{
|
||||
type Output = Var<A>;
|
||||
fn $fn(self, rhs: $target) -> Self::Output {
|
||||
Var::Dynamic(format!("{}({},{})", stringify!($fn), self.glsl(), rhs).into())
|
||||
@ -450,7 +474,8 @@ macro_rules! define_shape_data_string_operator_no_ref {
|
||||
}
|
||||
|
||||
impl<A> $name<$target> for Var<A>
|
||||
where A : RefInto<Glsl> {
|
||||
where A: RefInto<Glsl>
|
||||
{
|
||||
type Output = Var<A>;
|
||||
fn $fn(self, rhs: $target) -> Self::Output {
|
||||
Var::Dynamic(format!("{}({},{})", stringify!($fn), self.glsl(), rhs).into())
|
||||
@ -458,7 +483,8 @@ macro_rules! define_shape_data_string_operator_no_ref {
|
||||
}
|
||||
|
||||
impl<'t, A> $name<&'t Var<A>> for $target
|
||||
where A : Display + RefInto<Glsl> {
|
||||
where A: Display + RefInto<Glsl>
|
||||
{
|
||||
type Output = Var<A>;
|
||||
fn $fn(self, rhs: &'t Var<A>) -> Self::Output {
|
||||
Var::Dynamic(format!("{}({},{})", stringify!($fn), self.glsl(), rhs).into())
|
||||
@ -466,13 +492,14 @@ macro_rules! define_shape_data_string_operator_no_ref {
|
||||
}
|
||||
|
||||
impl<A> $name<Var<A>> for $target
|
||||
where A : Display + RefInto<Glsl> {
|
||||
where A: Display + RefInto<Glsl>
|
||||
{
|
||||
type Output = Var<A>;
|
||||
fn $fn(self, rhs: Var<A>) -> Self::Output {
|
||||
Var::Dynamic(format!("{}({},{})", stringify!($fn), self.glsl(), rhs).into())
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
define_shape_data_string_operator! { Add add (+) }
|
||||
@ -516,46 +543,50 @@ impl Var<f32> {
|
||||
// ===============================
|
||||
|
||||
impl<T> Sin for Var<T>
|
||||
where T: Sin<Output=T> {
|
||||
where T: Sin<Output = T>
|
||||
{
|
||||
type Output = Var<T>;
|
||||
fn sin(&self) -> Self {
|
||||
match self {
|
||||
Self::Static(t) => Var::Static(t.sin()),
|
||||
Self::Dynamic (t) => Var::Dynamic(format!("sin({})",t).into())
|
||||
Self::Dynamic(t) => Var::Dynamic(format!("sin({})", t).into()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> Asin for Var<T>
|
||||
where T: Asin<Output=T> {
|
||||
where T: Asin<Output = T>
|
||||
{
|
||||
type Output = Var<T>;
|
||||
fn asin(&self) -> Self {
|
||||
match self {
|
||||
Self::Static(t) => Var::Static(t.asin()),
|
||||
Self::Dynamic (t) => Var::Dynamic(format!("asin({})",t).into())
|
||||
Self::Dynamic(t) => Var::Dynamic(format!("asin({})", t).into()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
impl<T> Cos for Var<T>
|
||||
where T: Cos<Output=T> {
|
||||
where T: Cos<Output = T>
|
||||
{
|
||||
type Output = Var<T>;
|
||||
fn cos(&self) -> Self {
|
||||
match self {
|
||||
Self::Static(t) => Var::Static(t.cos()),
|
||||
Self::Dynamic (t) => Var::Dynamic(format!("cos({})",t).into())
|
||||
Self::Dynamic(t) => Var::Dynamic(format!("cos({})", t).into()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> Acos for Var<T>
|
||||
where T: Acos<Output=T> {
|
||||
where T: Acos<Output = T>
|
||||
{
|
||||
type Output = Var<T>;
|
||||
fn acos(&self) -> Self {
|
||||
match self {
|
||||
Self::Static(t) => Var::Static(t.acos()),
|
||||
Self::Dynamic (t) => Var::Dynamic(format!("acos({})",t).into())
|
||||
Self::Dynamic(t) => Var::Dynamic(format!("acos({})", t).into()),
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -567,12 +598,13 @@ where T: Acos<Output=T> {
|
||||
// ===================
|
||||
|
||||
impl<T> Sqrt for Var<T>
|
||||
where T: Sqrt<Output=T> {
|
||||
where T: Sqrt<Output = T>
|
||||
{
|
||||
type Output = Var<T>;
|
||||
fn sqrt(&self) -> Self {
|
||||
match self {
|
||||
Self::Static(t) => Var::Static(t.sqrt()),
|
||||
Self::Dynamic (t) => Var::Dynamic(format!("sqrt({})",t).into())
|
||||
Self::Dynamic(t) => Var::Dynamic(format!("sqrt({})", t).into()),
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -584,11 +616,12 @@ where T: Sqrt<Output=T> {
|
||||
// =============
|
||||
|
||||
impl<T> Clamp for Var<T>
|
||||
where T: Clamp<Output=T>+Into<Glsl> {
|
||||
where T: Clamp<Output = T> + Into<Glsl>
|
||||
{
|
||||
type Output = Var<T>;
|
||||
fn clamp(self, lower: Var<T>, upper: Var<T>) -> Var<T> {
|
||||
use Var::Static;
|
||||
use Var::Dynamic;
|
||||
use Var::Static;
|
||||
|
||||
match (self, lower, upper) {
|
||||
(Static(value), Static(lower), Static(upper)) => Static(value.clamp(lower, upper)),
|
||||
@ -609,12 +642,13 @@ where T: Clamp<Output=T>+Into<Glsl> {
|
||||
// ==============
|
||||
|
||||
impl<T> Signum for Var<T>
|
||||
where T: Signum<Output=T> {
|
||||
where T: Signum<Output = T>
|
||||
{
|
||||
type Output = Var<T>;
|
||||
fn signum(self) -> Self {
|
||||
match self {
|
||||
Self::Static(t) => Var::Static(t.signum()),
|
||||
Self::Dynamic (t) => Var::Dynamic(format!("sign({})",t).into())
|
||||
Self::Dynamic(t) => Var::Dynamic(format!("sign({})", t).into()),
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -693,15 +727,14 @@ impl Var<color::Rgba> {
|
||||
/// Return the color with the given alpha value.
|
||||
pub fn with_alpha(self, alpha: &Var<f32>) -> Self {
|
||||
match (self, alpha) {
|
||||
(Var::Static (t), Var::Static(alpha)) => {
|
||||
Var::Static(color::Rgba::new(t.data.red,t.data.green,t.data.blue,*alpha))
|
||||
},
|
||||
(Var::Static(t), Var::Static(alpha)) =>
|
||||
Var::Static(color::Rgba::new(t.data.red, t.data.green, t.data.blue, *alpha)),
|
||||
(t, alpha) => {
|
||||
let t = t.glsl();
|
||||
let alpha = alpha.glsl();
|
||||
let var = format!("srgba({0}.raw.x,{0}.raw.y,{0}.raw.z,{1})", t, alpha);
|
||||
Var::Dynamic(var.into())
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -710,15 +743,16 @@ impl Var<color::Rgba> {
|
||||
pub fn multiply_alpha(self, alpha: &Var<f32>) -> Self {
|
||||
match (self, alpha) {
|
||||
(Var::Static(t), Var::Static(alpha)) => {
|
||||
let var = color::Rgba::new(t.data.red,t.data.green,t.data.blue,*alpha*t.data.alpha);
|
||||
let var =
|
||||
color::Rgba::new(t.data.red, t.data.green, t.data.blue, *alpha * t.data.alpha);
|
||||
Var::Static(var)
|
||||
},
|
||||
}
|
||||
(t, alpha) => {
|
||||
let t = t.glsl();
|
||||
let alpha = alpha.glsl();
|
||||
let var = format!("srgba({0}.raw.x,{0}.raw.y,{0}.raw.z,{0}.raw.w*{1})", t, alpha);
|
||||
Var::Dynamic(var.into())
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -726,7 +760,7 @@ impl Var<color::Rgba> {
|
||||
pub fn into_linear(self) -> Var<color::LinearRgba> {
|
||||
match self {
|
||||
Var::Static(c) => Var::Static(c.into()),
|
||||
Var::Dynamic(c) => Var::Dynamic(format!("rgba({0})",c.glsl()).into())
|
||||
Var::Dynamic(c) => Var::Dynamic(format!("rgba({0})", c.glsl()).into()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -3,11 +3,11 @@
|
||||
|
||||
use crate::prelude::*;
|
||||
|
||||
use super::canvas;
|
||||
use super::canvas::Canvas;
|
||||
use crate::display::shape::primitive::def::primitive;
|
||||
use crate::display::shape::primitive::shader::overload;
|
||||
use crate::display::symbol::shader::builder::CodeTemplate;
|
||||
use super::canvas;
|
||||
use super::canvas::Canvas;
|
||||
|
||||
|
||||
|
||||
@ -42,7 +42,8 @@ impl Builder {
|
||||
let shape_header = header("Shape Definition");
|
||||
canvas.add_current_function_code_line(iformat!("return {shape_ref.getter()};"));
|
||||
canvas.submit_shape_constructor("run");
|
||||
let defs = iformat!("{defs_header}\n\n{sdf_defs}\n\n\n\n{shape_header}\n\n{canvas.to_glsl()}");
|
||||
let defs =
|
||||
iformat!("{defs_header}\n\n{sdf_defs}\n\n\n\n{shape_header}\n\n{canvas.to_glsl()}");
|
||||
|
||||
let redirections = overload::builtin_redirections();
|
||||
let math = overload::allow_overloading(MATH);
|
||||
@ -51,8 +52,14 @@ impl Builder {
|
||||
let shape = overload::allow_overloading(SHAPE);
|
||||
|
||||
let defs = overload::allow_overloading(&defs);
|
||||
let code = format!("{}\n\n{}\n\n{}\n\n{}\n\n{}\n\n{}",redirections,math,color,debug,shape,defs);
|
||||
let main = format!("bool pointer_events_enabled = {};\n{}",pointer_events_enabled,FRAGMENT_RUNNER);
|
||||
let code = format!(
|
||||
"{}\n\n{}\n\n{}\n\n{}\n\n{}\n\n{}",
|
||||
redirections, math, color, debug, shape, defs
|
||||
);
|
||||
let main = format!(
|
||||
"bool pointer_events_enabled = {};\n{}",
|
||||
pointer_events_enabled, FRAGMENT_RUNNER
|
||||
);
|
||||
|
||||
CodeTemplate::new(code, main, "")
|
||||
}
|
||||
|
@ -1,10 +1,10 @@
|
||||
//! Canvas for drawing vector graphics. See the documentation of `Canvas` to learn more.
|
||||
|
||||
use crate::prelude::*;
|
||||
use crate::data::color;
|
||||
use crate::display::shape::primitive::def::var::Var;
|
||||
use crate::prelude::*;
|
||||
use crate::system::gpu::shader::glsl::Glsl;
|
||||
use crate::system::gpu::types::*;
|
||||
use crate::data::color;
|
||||
|
||||
|
||||
|
||||
@ -15,7 +15,7 @@ use crate::data::color;
|
||||
/// Immutable reference to a shape defined on `Canvas` with a fast clone.
|
||||
#[derive(Clone, Debug, Shrinkwrap)]
|
||||
pub struct Shape {
|
||||
rc: Rc<ShapeData>
|
||||
rc: Rc<ShapeData>,
|
||||
}
|
||||
|
||||
impl Shape {
|
||||
@ -104,8 +104,7 @@ impl Canvas {
|
||||
impl Canvas {
|
||||
/// Checks if shape with the given id was already defined. If so, a cached `ShapeCanvas` is
|
||||
/// returned. Otherwise the provided constructor is run and the result is cached.
|
||||
pub fn if_not_defined<F:FnOnce(&mut Self) -> ShapeData>
|
||||
(&mut self, id:usize, f:F) -> Shape {
|
||||
pub fn if_not_defined<F: FnOnce(&mut Self) -> ShapeData>(&mut self, id: usize, f: F) -> Shape {
|
||||
match self.defined_shapes.get(&id) {
|
||||
Some(shape) => shape.clone(),
|
||||
None => {
|
||||
@ -140,8 +139,10 @@ impl Canvas {
|
||||
|
||||
/// Get the final GLSL code.
|
||||
pub fn to_glsl(&self) -> String {
|
||||
assert!(self.current_function_lines.is_empty(),
|
||||
"Internal error. Not all canvas GLSL code lines were converted to functions.");
|
||||
assert!(
|
||||
self.current_function_lines.is_empty(),
|
||||
"Internal error. Not all canvas GLSL code lines were converted to functions."
|
||||
);
|
||||
self.functions.join("\n\n")
|
||||
}
|
||||
}
|
||||
@ -213,8 +214,12 @@ impl Canvas {
|
||||
}
|
||||
|
||||
/// Translate the current canvas origin.
|
||||
pub fn translate<V:Into<Var<Vector2<Pixels>>>>
|
||||
(&mut self, num:usize, s1:Shape, v:V) -> Shape {
|
||||
pub fn translate<V: Into<Var<Vector2<Pixels>>>>(
|
||||
&mut self,
|
||||
num: usize,
|
||||
s1: Shape,
|
||||
v: V,
|
||||
) -> Shape {
|
||||
self.if_not_defined(num, |this| {
|
||||
let v = v.into().glsl();
|
||||
let trans = iformat!("position = translate(position,{v});");
|
||||
@ -227,8 +232,7 @@ impl Canvas {
|
||||
}
|
||||
|
||||
/// Rotate the current canvas origin.
|
||||
pub fn rotation<A:Into<Var<Radians>>>
|
||||
(&mut self, num:usize, s1:Shape, angle:A) -> Shape {
|
||||
pub fn rotation<A: Into<Var<Radians>>>(&mut self, num: usize, s1: Shape, angle: A) -> Shape {
|
||||
self.if_not_defined(num, |this| {
|
||||
let angle: Glsl = angle.into().glsl();
|
||||
let trans = iformat!("position = rotate(position,{angle});");
|
||||
@ -241,8 +245,7 @@ impl Canvas {
|
||||
}
|
||||
|
||||
/// Scale the current canvas origin.
|
||||
pub fn scale<T:Into<Var<f32>>>
|
||||
(&mut self, num:usize, s1:Shape, value:T) -> Shape {
|
||||
pub fn scale<T: Into<Var<f32>>>(&mut self, num: usize, s1: Shape, value: T) -> Shape {
|
||||
self.if_not_defined(num, |this| {
|
||||
let value: Glsl = value.into().glsl();
|
||||
let trans = iformat!("position = scale(position,{value});");
|
||||
@ -255,8 +258,12 @@ impl Canvas {
|
||||
}
|
||||
|
||||
/// Fill the shape with the provided color.
|
||||
pub fn fill<Color:Into<Var<color::Rgba>>>
|
||||
(&mut self, num:usize, s:Shape, color:Color) -> Shape {
|
||||
pub fn fill<Color: Into<Var<color::Rgba>>>(
|
||||
&mut self,
|
||||
num: usize,
|
||||
s: Shape,
|
||||
color: Color,
|
||||
) -> Shape {
|
||||
self.if_not_defined(num, |this| {
|
||||
let color: Glsl = color.into().glsl();
|
||||
this.add_current_function_code_line(iformat!("Shape shape = {s.getter()};"));
|
||||
@ -269,8 +276,7 @@ impl Canvas {
|
||||
}
|
||||
|
||||
/// Make the borders of the shape crisp. Please note that it removes any form of antialiasing.
|
||||
pub fn pixel_snap
|
||||
(&mut self, num:usize, s:Shape) -> Shape {
|
||||
pub fn pixel_snap(&mut self, num: usize, s: Shape) -> Shape {
|
||||
self.if_not_defined(num, |this| {
|
||||
let expr = iformat!("return pixel_snap({s.getter()});");
|
||||
let mut shape = this.new_shape_from_expr(num, &expr);
|
||||
@ -280,8 +286,7 @@ impl Canvas {
|
||||
}
|
||||
|
||||
/// Grow the shape by the given value.
|
||||
pub fn grow<T:Into<Var<f32>>>
|
||||
(&mut self, num:usize, s:Shape, value:T) -> Shape {
|
||||
pub fn grow<T: Into<Var<f32>>>(&mut self, num: usize, s: Shape, value: T) -> Shape {
|
||||
self.if_not_defined(num, |this| {
|
||||
let value: Glsl = value.into().glsl();
|
||||
let expr = iformat!("return grow({s.getter()},{value});");
|
||||
@ -292,15 +297,18 @@ impl Canvas {
|
||||
}
|
||||
|
||||
/// Shrink the shape by the given value.
|
||||
pub fn shrink<T:Into<Var<f32>>>
|
||||
(&mut self, num:usize, s:Shape, value:T) -> Shape {
|
||||
pub fn shrink<T: Into<Var<f32>>>(&mut self, num: usize, s: Shape, value: T) -> Shape {
|
||||
let value = value.into();
|
||||
self.grow(num, s, -value)
|
||||
}
|
||||
|
||||
/// Repeat the shape with the given tile size.
|
||||
pub fn repeat<T:Into<Var<Vector2<Pixels>>>>
|
||||
(&mut self, num:usize, s:Shape, tile_size:T) -> Shape {
|
||||
pub fn repeat<T: Into<Var<Vector2<Pixels>>>>(
|
||||
&mut self,
|
||||
num: usize,
|
||||
s: Shape,
|
||||
tile_size: T,
|
||||
) -> Shape {
|
||||
self.if_not_defined(num, |this| {
|
||||
let value: Glsl = tile_size.into().glsl();
|
||||
let repeat = iformat!("position = repeat(position,{value});");
|
||||
|
@ -4,9 +4,9 @@ use crate::prelude::*;
|
||||
|
||||
use crate::control::callback;
|
||||
use crate::data::color;
|
||||
use crate::display::style::Path;
|
||||
use crate::display::style::data::DataMatch;
|
||||
use crate::display::style;
|
||||
use crate::display::style::data::DataMatch;
|
||||
use crate::display::style::Path;
|
||||
|
||||
use enso_frp as frp;
|
||||
|
||||
@ -56,8 +56,10 @@ impl StyleWatchFrp {
|
||||
Self { network, sheet, vars, handles, callback }
|
||||
}
|
||||
|
||||
fn get_internal
|
||||
(&self, path:impl Into<Path>) -> (frp::Source<Option<style::Data>>,Option<style::Data>) {
|
||||
fn get_internal(
|
||||
&self,
|
||||
path: impl Into<Path>,
|
||||
) -> (frp::Source<Option<style::Data>>, Option<style::Data>) {
|
||||
let network = &self.network;
|
||||
frp::extend! { network
|
||||
source <- source::<Option<style::Data>>();
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user