├── .github ├── ISSUE_TEMPLATE │ ├── bug_report.md │ └── feature-request.md ├── pull_request_template.md └── workflows │ └── orbtk.yml ├── .gitignore ├── .vscode ├── launch.json └── tasks.json ├── CHANGELOG.md ├── Cargo.toml ├── LICENSE ├── Node.toml ├── README.md ├── clippy.toml ├── orbtk ├── Cargo.toml ├── assets │ └── orbtk_space.png ├── examples │ ├── README.md │ ├── assets │ │ ├── calculator │ │ │ └── calculator_dark.ron │ │ ├── popup │ │ │ ├── default_dark.ron │ │ │ └── popup_de_DE.ron │ │ └── showcase │ │ │ ├── orbtk_logo.png │ │ │ └── showcase_de_DE.ron │ ├── calculator.rs │ ├── login.rs │ ├── message.rs │ ├── minimal.rs │ ├── msg_handler.rs │ ├── multi_window.rs │ ├── overlay.rs │ ├── popup.rs │ ├── showcase.rs │ └── stack.rs ├── images │ ├── layout_constraints.png │ ├── layout_constraints.svg │ ├── orbtk_logo_dark.png │ ├── orbtk_planet.svg │ └── orbtk_space.png ├── screenshots │ ├── calculator.png │ ├── calculator_dark_macos.png │ ├── calculator_light_macos.png │ ├── calculator_redox.png │ ├── canvas.jpg │ ├── canvas.png │ ├── login.png │ ├── message.png │ ├── minimal.png │ ├── msg_handler.png │ ├── multi_window.jpg │ ├── multi_window.png │ ├── overlay.jpg │ ├── overlay.png │ ├── popup.png │ ├── showcase.png │ ├── showcase_button.png │ ├── showcase_button_macos.png │ ├── showcase_image.png │ ├── showcase_interactive.png │ ├── showcase_items.png │ ├── showcase_layouts.png │ ├── showcase_localization.png │ ├── showcase_navigation.png │ └── stack.png └── src │ ├── application.rs │ ├── lib.rs │ └── prelude.rs ├── orbtk_core ├── Cargo.toml ├── README.md └── src │ ├── application │ ├── context_provider.rs │ ├── mod.rs │ ├── overlay.rs │ └── window_adapter.rs │ ├── event │ ├── drop.rs │ ├── editable.rs │ ├── event_adapter.rs │ ├── event_handler.rs │ ├── event_queue.rs │ ├── focus.rs │ ├── key.rs │ ├── mod.rs │ ├── mouse.rs │ ├── system.rs │ ├── text_input.rs │ └── window.rs │ ├── layout │ ├── absolute.rs │ ├── fixed_size.rs │ ├── grid.rs │ ├── mod.rs │ ├── padding.rs │ ├── popup.rs │ └── stack.rs │ ├── lib.rs │ ├── localization │ ├── mod.rs │ └── ron_localization │ │ ├── dictionary.rs │ │ └── mod.rs │ ├── macros.rs │ ├── prelude.rs │ ├── properties │ ├── layout │ │ ├── block.rs │ │ ├── mod.rs │ │ └── scroll_viewer_mode.rs │ ├── mod.rs │ └── widget │ │ ├── focus_state.rs │ │ ├── keyboard_state.rs │ │ ├── mod.rs │ │ ├── render_pipeline.rs │ │ ├── selected_entities.rs │ │ ├── selected_indices.rs │ │ └── text_selection.rs │ ├── render_object │ ├── cursor.rs │ ├── default.rs │ ├── font_icon.rs │ ├── image.rs │ ├── mod.rs │ ├── pipeline.rs │ ├── popup.rs │ ├── rectangle.rs │ └── text.rs │ ├── services │ ├── clipboard.rs │ ├── mod.rs │ └── settings.rs │ ├── systems │ ├── cleanup_system.rs │ ├── event_state_system.rs │ ├── init_system.rs │ ├── layout_system.rs │ ├── mod.rs │ ├── post_layout_state_system.rs │ └── render_system.rs │ ├── theming │ ├── config │ │ ├── mod.rs │ │ ├── style_config.rs │ │ └── theme_config.rs │ ├── mod.rs │ ├── selector.rs │ ├── style.rs │ ├── theme.rs │ └── theme_state.rs │ ├── tree │ └── mod.rs │ └── widget_base │ ├── build_context.rs │ ├── context.rs │ ├── message_adapter.rs │ ├── mod.rs │ ├── registry.rs │ ├── state.rs │ ├── states_context.rs │ ├── template.rs │ └── widget_container.rs ├── orbtk_orbclient ├── Cargo.toml ├── README.md └── src │ ├── event.rs │ ├── lib.rs │ ├── native │ └── mod.rs │ ├── orbclient │ ├── mod.rs │ ├── states.rs │ ├── window.rs │ └── window_builder.rs │ ├── prelude.rs │ ├── web │ ├── mod.rs │ ├── states.rs │ ├── window.rs │ └── window_builder.rs │ └── window_adapter.rs ├── orbtk_tinyskia ├── Cargo.toml ├── README.md └── src │ ├── common.rs │ ├── lib.rs │ ├── prelude.rs │ ├── render_target.rs │ └── tinyskia │ ├── font.rs │ ├── image.rs │ └── mod.rs ├── orbtk_widgets ├── Cargo.toml ├── README.md ├── assets │ ├── fonts │ │ ├── material │ │ │ ├── MATERIAL_ICONS_LICENSE │ │ │ ├── MaterialIcons-Baseline.woff2 │ │ │ ├── MaterialIcons-Outlined.woff2 │ │ │ ├── MaterialIcons-Round.woff2 │ │ │ ├── MaterialIcons-Sharp.woff2 │ │ │ ├── MaterialIcons-TwoTone.woff2 │ │ │ ├── MaterialIcons.ttf │ │ │ ├── README.md │ │ │ ├── material_icons_font.ron │ │ │ └── package.json │ │ ├── mdl2 │ │ │ ├── mdl2.otf │ │ │ └── mdl2_assets_font.ron │ │ ├── roboto │ │ │ ├── ROBOTO_LICENSE │ │ │ ├── Roboto-Medium.ttf │ │ │ └── Roboto-Regular.ttf │ │ └── selawik │ │ │ ├── Selawik-Bold.otf │ │ │ ├── Selawik-Light.otf │ │ │ └── Selawik-Regular.otf │ └── themes │ │ ├── fluent │ │ ├── theme_fluent.ron │ │ ├── theme_fluent_colors_dark.ron │ │ ├── theme_fluent_colors_light.ron │ │ └── theme_fluent_fonts.ron │ │ ├── orbtk │ │ ├── theme_default.ron │ │ ├── theme_default_colors_dark.ron │ │ ├── theme_default_colors_light.ron │ │ └── theme_default_fonts.ron │ │ └── redox │ │ ├── theme_redox.ron │ │ ├── theme_redox_colors.ron │ │ └── theme_redox_fonts.ron └── src │ ├── behaviors │ ├── mod.rs │ ├── mouse_behavior.rs │ ├── selection_behavior.rs │ └── text_behavior.rs │ ├── button.rs │ ├── canvas.rs │ ├── check_box.rs │ ├── combo_box.rs │ ├── container.rs │ ├── cursor.rs │ ├── font_icon_block.rs │ ├── grid.rs │ ├── image_widget.rs │ ├── items_widget.rs │ ├── lib.rs │ ├── list_view.rs │ ├── master_detail.rs │ ├── numeric_box.rs │ ├── pager.rs │ ├── password_box.rs │ ├── popup.rs │ ├── prelude.rs │ ├── progress_bar.rs │ ├── scroll_bar.rs │ ├── scroll_indicator.rs │ ├── scroll_viewer.rs │ ├── slider.rs │ ├── stack.rs │ ├── switch.rs │ ├── tab_widget.rs │ ├── text_block.rs │ ├── text_box.rs │ ├── themes │ ├── mod.rs │ ├── theme_fluent │ │ ├── fluent_fonts.rs │ │ ├── mdl2_assets_font.rs │ │ └── mod.rs │ ├── theme_orbtk │ │ ├── colors.rs │ │ ├── material_icons_font.rs │ │ ├── mod.rs │ │ └── orbtk_fonts.rs │ └── theme_redox.rs │ ├── toggle_button.rs │ └── window.rs ├── policies └── code-of-conduct.md ├── proc_macros ├── Cargo.toml ├── README.md └── src │ └── lib.rs └── utils ├── Cargo.toml ├── README.md ├── build.rs ├── colors.txt └── src ├── alignment.rs ├── angle.rs ├── border.rs ├── brush.rs ├── color.rs ├── constraint.rs ├── dirty_size.rs ├── expression.rs ├── f32_cmp.rs ├── f64_cmp.rs ├── filter.rs ├── gradients.rs ├── lib.rs ├── number.rs ├── orientation.rs ├── point.rs ├── prelude.rs ├── rectangle.rs ├── relative_direction.rs ├── selection_mode.rs ├── size.rs ├── spacer.rs ├── string16.rs ├── text_alignment.rs ├── text_baseline.rs ├── thickness.rs ├── value.rs └── visibility.rs /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report 3 | about: Create a report to help us improve 4 | title: '' 5 | labels: bug 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Describe the bug** 11 | A clear and concise description of what the bug is. 12 | 13 | **To Reproduce** 14 | Steps to reproduce the behavior: 15 | 1. Go to '...' 16 | 2. Click on '....' 17 | 3. Scroll down to '....' 18 | 4. See error 19 | 20 | **Expected behavior** 21 | A clear and concise description of what you expected to happen. 22 | 23 | **Screenshots** 24 | If applicable, add screenshots to help explain your problem. 25 | 26 | **Desktop (please complete the following information):** 27 | - OS: [e.g. iOS] 28 | - Browser [e.g. chrome, safari] 29 | - Version [e.g. 22] 30 | 31 | **Smartphone (please complete the following information):** 32 | - Device: [e.g. iPhone6] 33 | - OS: [e.g. iOS8.1] 34 | - Browser [e.g. stock browser, safari] 35 | - Version [e.g. 22] 36 | 37 | **Additional context** 38 | Add any other context about the problem here. 39 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature-request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Feature request 3 | about: " Suggest an idea for this project " 4 | title: '' 5 | labels: '' 6 | assignees: '' 7 | 8 | --- 9 | 10 | # Context 11 | 12 | In which context or scenario will your feature/widget be used? 13 | 14 | # Problem description & Solution 15 | 16 | Describe your problem and possible solution here 17 | 18 | # Examples and MockUps 19 | 20 | Can you give a concrete example of how you'd use the feature? A relevant code sample? 21 | Can you draw a picture of what you're imagining? :-) 22 | -------------------------------------------------------------------------------- /.github/pull_request_template.md: -------------------------------------------------------------------------------- 1 | # Context: 2 | A description of the changes proposed in the pull request. 3 | 4 | Reference to a related issue in the repository. 5 | @mentions of the person or team responsible for reviewing proposed changes. 6 | 7 | ## Contribution checklist: 8 | - [ ] Add [documentation](https://doc.rust-lang.org/1.7.0/book/documentation.html) to all public structs, traits and functions. 9 | - [ ] Add unit tests if possible 10 | - [ ] Describe the major change(s) in the CHANGELOG.MD 11 | - [ ] Run `cargo fmt` to make the formatting consistent across the codebase 12 | - [ ] Run `cargo clippy` to check with the linter 13 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Compiled files 2 | *.o 3 | *.so 4 | *.rlib 5 | *.dll 6 | 7 | # Executables 8 | *.exe 9 | 10 | # Generated by Cargo 11 | Cargo.lock 12 | /target/ 13 | /crates/*/target/ 14 | 15 | .idea/ 16 | *.iml 17 | #.vscode 18 | 19 | # Brainstorming files 20 | brain_*.rs 21 | 22 | .DS_Store 23 | Thumbs.db 24 | 25 | # Generated by cargo node 26 | static/ 27 | 28 | book 29 | orbclient -------------------------------------------------------------------------------- /.vscode/tasks.json: -------------------------------------------------------------------------------- 1 | { 2 | // See https://go.microsoft.com/fwlink/?LinkId=733558 3 | // for the documentation about the tasks.json format 4 | "version": "2.0.0", 5 | "tasks": [ 6 | { 7 | "label": "build", 8 | "command": "cargo", 9 | "type": "shell", 10 | "args": [ 11 | "build", 12 | "--examples=minimal", 13 | "--features", 14 | "log" 15 | ], 16 | "presentation": { 17 | "reveal": "always", 18 | "panel": "new" 19 | }, 20 | "group": { 21 | "kind": "build", 22 | "isDefault": true 23 | }, 24 | "problemMatcher": [] 25 | }, 26 | { 27 | "label": "build release", 28 | "command": "cargo", 29 | "type": "shell", 30 | "args": [ 31 | "build", 32 | "--examples=minimal", 33 | "--release" 34 | ], 35 | "presentation": { 36 | "reveal": "always", 37 | "panel": "new" 38 | }, 39 | "group": { 40 | "kind": "build", 41 | "isDefault": true 42 | }, 43 | "problemMatcher": [] 44 | }, 45 | { 46 | "label": "build preview", 47 | "command": "cargo", 48 | "type": "shell", 49 | "args": [ 50 | "build", 51 | "--examples=minimal", 52 | "--features", 53 | "preview" 54 | ], 55 | "presentation": { 56 | "reveal": "always", 57 | "panel": "new" 58 | }, 59 | "group": { 60 | "kind": "build", 61 | "isDefault": true 62 | }, 63 | "problemMatcher": [] 64 | }, 65 | { 66 | "label": "build debug", 67 | "command": "cargo", 68 | "type": "shell", 69 | "args": [ 70 | "build", 71 | "--examples=minimal", 72 | "--features", 73 | "debug" 74 | ], 75 | "presentation": { 76 | "reveal": "always", 77 | "panel": "new" 78 | }, 79 | "group": { 80 | "kind": "build", 81 | "isDefault": true 82 | } 83 | }, 84 | { 85 | "label": "run-web", 86 | "command": "cargo", 87 | "type": "shell", 88 | "args": [ 89 | "web", 90 | "start", 91 | "--target=wasm32-unknown-unknown", 92 | "--auto-reload", 93 | "--example", 94 | "widgets" 95 | ], 96 | "presentation": { 97 | "reveal": "always", 98 | "panel": "new" 99 | }, 100 | "group": { 101 | "kind": "build", 102 | "isDefault": true 103 | } 104 | }, 105 | ] 106 | } -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [profile.dev] 2 | opt-level = 1 3 | 4 | [workspace] 5 | members = [ 6 | "orbtk", 7 | "orbtk_core", 8 | "orbtk_widgets", 9 | "orbtk_orbclient", 10 | "orbtk_tinyskia", 11 | "proc_macros", 12 | "utils", 13 | ] -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015 Jeremy Soller 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | 23 | -------------------------------------------------------------------------------- /Node.toml: -------------------------------------------------------------------------------- 1 | [[apps]] 2 | name = "showcase" 3 | width = 468 4 | height = 730 5 | assets = "assets/showcase" 6 | [[apps.fonts]] 7 | font_family = "MaterialIcons-Regular" 8 | src = "crates/theme_default/assets/fonts/MaterialIcons.ttf" 9 | [[apps.fonts]] 10 | font_family = "Roboto-Regular" 11 | src = "crates/theme_default/assets/fonts/Roboto-Regular.ttf" 12 | [[apps.fonts]] 13 | font_family = "Roboto-Medium" 14 | src = "crates/theme_default/assets/fonts/Roboto-Medium.ttf" 15 | 16 | [[apps]] 17 | name = "calculator" 18 | width = 212 19 | height = 360 20 | 21 | [[apps]] 22 | name = "canvas" 23 | width = 468 24 | height = 730 25 | [[apps.fonts]] 26 | font_family = "MaterialIcons-Regular" 27 | src = "crates/theme_default/assets/fonts/MaterialIcons.ttf" 28 | [[apps.fonts]] 29 | font_family = "Roboto-Regular" 30 | src = "crates/theme_default/assets/fonts/Roboto-Regular.ttf" 31 | [[apps.fonts]] 32 | font_family = "Roboto-Medium" 33 | src = "crates/theme_default/assets/fonts/Roboto-Medium.ttf" 34 | -------------------------------------------------------------------------------- /clippy.toml: -------------------------------------------------------------------------------- 1 | too-many-arguments-threshold = 10 2 | type-complexity-threshold = 400 -------------------------------------------------------------------------------- /orbtk/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | authors = [ 3 | "Florian Blasius <flovanpt@posteo.de>", 4 | "Jeremy Soller <jackpot51@gmail.com>" 5 | ] 6 | description = "The Orbital Widget Toolkit" 7 | documentation = "https://docs.rs/orbtk" 8 | edition = "2021" 9 | keywords = [ 10 | "orbital", 11 | "redox", 12 | "ui", 13 | ] 14 | license = "MIT" 15 | name = "orbtk" 16 | readme = "README.md" 17 | repository = "https://github.com/redox-os/orbtk" 18 | version = "0.3.1-alpha5" 19 | 20 | [dependencies] 21 | orbtk_proc_macros = { version = "0.3.1-alpha5", path = "../proc_macros" } 22 | orbtk_core = { version = "0.3.1-alpha5", path = "../orbtk_core", default-features = false } 23 | orbtk_tinyskia = { version = "0.3.1-alpha5", path = "../orbtk_tinyskia", default-features = false } 24 | orbtk_orbclient = { version = "0.3.1-alpha5", path = "../orbtk_orbclient", default-features = false } 25 | orbtk_utils = { version = "0.3.1-alpha5", path = "../utils" } 26 | orbtk_widgets = { version = "0.3.1-alpha5", path="../orbtk_widgets", default-features = false } 27 | 28 | [dependencies.dces] 29 | #version = "0.3.1" 30 | git = "https://gitlab.redox-os.org/redox-os/dces-rust.git" 31 | branch = "master" 32 | #branch = "develop" 33 | 34 | [dev-dependencies] 35 | euc = "0.5.0" 36 | vek = { version = "0.15.7", default-features = false, features = ["rgb", "rgba"] } 37 | serde = "1.0.106" 38 | serde_derive = "1.0.106" 39 | 40 | [features] 41 | log = ["orbtk_orbclient/log"] 42 | debug = ["orbtk_core/debug"] 43 | bundled = ["orbtk_orbclient/bundled"] 44 | 45 | [lib] 46 | name = "orbtk" 47 | path = "src/lib.rs" 48 | -------------------------------------------------------------------------------- /orbtk/assets/orbtk_space.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/redox-os/orbtk/eba9e77821551076bbf1d9f7ab44d788150e3446/orbtk/assets/orbtk_space.png -------------------------------------------------------------------------------- /orbtk/examples/README.md: -------------------------------------------------------------------------------- 1 | # OrbTk examples 2 | 3 | [](https://github.com/redox-os/orbtk/actions) 4 |  5 | 6 | ## Basic reference examples 7 | 8 | * `minimal`: minimal example 9 | 10 |  11 | 12 | Howto start your OrbTk journey. 13 | 14 | * `showcase`: major reference app 15 | 16 | <p float="left"> 17 | <img alt="showcase" width="720" src="https://raw.githubusercontent.com/redox-os/orbtk/develop/orbtk/screenshots/showcase.png"> 18 | </p> 19 | 20 | It provides a structured overview of an OrbTk app. We try to 21 | incorporate example implementation that make use of the supported 22 | widgets inside the orbtk-widget crate. 23 | 24 | ## Specialized examples 25 | 26 | * calculator: a calculator example 27 | 28 |  29 | 30 | * canvas: use third party render library in canvas 31 | 32 |  33 | 34 | * login: PasswordBox showcase implementing a login form 35 | 36 | <img alt="login" width="360" src="https://raw.githubusercontent.com/redox-os/orbtk/develop/orbtk/screenshots/login.png"> 37 | 38 | * message: MessageAdapter example 39 | 40 | <img alt="message" width="360" src="https://raw.githubusercontent.com/redox-os/orbtk/develop/orbtk/screenshots/message.png"> 41 | 42 | 1. Assign and implement a thread inside the state of a MainView. 43 | 2. Generate a time based loop that sleeps given amount out 44 | seconds. 45 | 3. When the timer expires, increment a counter and issue a message 46 | inside the state. A message will be triggered and send via the 47 | message_adapter. 48 | 4. The receiver side will evaluate (match) the 49 | message type. The associated function block will increment a 50 | counter and update a text property inside the MainView. Thus, the View will update that counter every given time period. 51 | 52 | * msg_handler: sender-receiver example 53 | 54 | <img alt="msg_handler" width="480" src="https://raw.githubusercontent.com/redox-os/orbtk/develop/orbtk/screenshots/msg_handler.png"> 55 | 56 | * multi_window: multi window example 57 | 58 |  59 | 60 | * overlay: draw widgets on the top of the render stack 61 | 62 |  63 | 64 | * popup: show how to open and use a popup 65 | 66 | <img alt="popup" width="480" src="https://raw.githubusercontent.com/redox-os/orbtk/develop/orbtk/screenshots/popup.png"> 67 | 68 | * stack: stack layout example 69 | 70 | <img alt="stack" width="360" src="https://raw.githubusercontent.com/redox-os/orbtk/develop/orbtk/screenshots/stack.png"> 71 | 72 | ## License 73 | 74 | Licensed under MIT license ([LICENSE](../LICENSE)). 75 | -------------------------------------------------------------------------------- /orbtk/examples/assets/calculator/calculator_dark.ron: -------------------------------------------------------------------------------- 1 | Theme ( 2 | styles: { 3 | "input": ( 4 | // based on body style the of default_theme 5 | base: "body", 6 | properties: { 7 | "foreground": "#9dafbf" 8 | } 9 | ), 10 | "result": ( 11 | // based on body style the of default_theme 12 | base: "body", 13 | properties: { 14 | "font_size": "$FONT_SIZE_36", 15 | } 16 | ), 17 | "header_area": ( 18 | properties: { 19 | "background": "#444e55" 20 | } 21 | ), 22 | "content_area": ( 23 | properties: { 24 | "background": "#3b434a" 25 | } 26 | ), 27 | "button_calculator": ( 28 | // based on button style the of default_theme 29 | base: "button", 30 | properties: { 31 | "min_width": 48, 32 | "height": 48, 33 | "border_radius": 1, 34 | "spacing": 0, 35 | }, 36 | ), 37 | "button_calculator_primary": ( 38 | // based on button_primary style the of default_theme 39 | base: "button_primary", 40 | properties: { 41 | "min_width": 48, 42 | "height": 48, 43 | "border_radius": 1, 44 | "spacing": 0, 45 | }, 46 | ), 47 | } 48 | ) -------------------------------------------------------------------------------- /orbtk/examples/assets/popup/default_dark.ron: -------------------------------------------------------------------------------- 1 | // Popup: extended default theme. 2 | Theme ( 3 | styles: { 4 | "popup_combo_box": ( 5 | base: "combo_box", 6 | properties: { 7 | "offset": 3 8 | }, 9 | ), 10 | "container_form": ( 11 | base: "container", 12 | properties: { 13 | "background": "#0000FF", 14 | "border_brush": "$CONTAINER_BORDER", 15 | "border_radius": 3, 16 | "border_width": 2, 17 | "height": 200, 18 | "padding": 16, 19 | "width": 200 20 | }, 21 | ), 22 | "popup_form": ( 23 | base: "popup", 24 | properties: { 25 | "background": "#FFFFFF", 26 | "border_brush": "#000000", 27 | "border_radius": 12, 28 | "border_width": 5, 29 | "margin": 8, 30 | "padding": 0 31 | }, 32 | ), 33 | "popup_text_block": ( 34 | base: "popup", 35 | properties: { 36 | "foreground": "#000000", 37 | "h_align": "center", 38 | "v_align": "top" 39 | }, 40 | ), 41 | "popup_numeric_box": ( 42 | base: "numeric_box", 43 | properties: { 44 | "width": 100 45 | }, 46 | ), 47 | "target_text_block": ( 48 | base: "small_text", 49 | properties: { 50 | "h_align": "center", 51 | "v_align": "center" 52 | }, 53 | ), 54 | } 55 | ) 56 | -------------------------------------------------------------------------------- /orbtk/examples/assets/popup/popup_de_DE.ron: -------------------------------------------------------------------------------- 1 | Dictionary ( 2 | words: { 3 | "Click me to show the popup": "Klicken zum Anzeigen des Popup", 4 | "Click me to hide the popup": "Klicken zum Verbergen des Popup", 5 | "Offset:": "Versatz:", 6 | "Placement:": "Plazierung:", 7 | } 8 | ) 9 | -------------------------------------------------------------------------------- /orbtk/examples/assets/showcase/orbtk_logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/redox-os/orbtk/eba9e77821551076bbf1d9f7ab44d788150e3446/orbtk/examples/assets/showcase/orbtk_logo.png -------------------------------------------------------------------------------- /orbtk/examples/assets/showcase/showcase_de_DE.ron: -------------------------------------------------------------------------------- 1 | Dictionary ( 2 | words: { 3 | // ButtonView 4 | "Text only": "nur Text", 5 | "disabled": "deaktiviert", 6 | // ItemsView 7 | "Item 1": "Eintrag 1", 8 | "Item 2": "Eintrag 2", 9 | "Item 3": "Eintrag 3", 10 | "Item 4": "Eintrag 4", 11 | "Item 5": "Eintrag 5", 12 | // LocalizationView 13 | "Hello": "Hallo", 14 | "world": "Welt", 15 | "I": "ich", 16 | "love": "liebe", 17 | "localization": "Lokalisierung", 18 | "German": "Deutsch", 19 | "English": "Englisch", 20 | "Localization": "Lokalisierung", 21 | // NavigationView 22 | "back": "zurück", 23 | "Content inside the master pane": "Inhalt im Hautpfenster", 24 | "Content inside the detail pane": "Inhalt im Detailfenster", 25 | "Detail Pane": "Detailfenster", 26 | "Master Pane": "Hautpfenster", 27 | "Page 1": "Seite 1", 28 | "Page 2": "Seite 2", 29 | "Page 3": "Seite 3", 30 | "Resize the window: Pane brake is set to 800 pixel": "Fenstergröße ändern: Die Splitgrenze ist bei 800 Pixel", 31 | "Navigation": "Navigation", 32 | "show detail pane": "zeige Detailfenster", 33 | } 34 | ) 35 | -------------------------------------------------------------------------------- /orbtk/examples/message.rs: -------------------------------------------------------------------------------- 1 | use orbtk::prelude::*; 2 | 3 | use std::{thread, time}; 4 | 5 | enum Message { 6 | Increment, 7 | } 8 | 9 | // constants 10 | pub static ID_MESSAGE: &str = "message"; 11 | pub static ID_MESSAGE_STACK: &str = "message_stack"; 12 | pub static ID_MESSAGE_TEXTBLOCK_COUNTER: &str = "message_counter"; 13 | pub static ID_MESSAGE_TEXTBLOCK_LABEL: &str = "message_counter_label"; 14 | 15 | #[derive(Default, AsAny)] 16 | struct MainState { 17 | count: i32, 18 | _my_thread: Option<thread::JoinHandle<()>>, 19 | } 20 | 21 | impl State for MainState { 22 | fn init(&mut self, _registry: &mut Registry, ctx: &mut Context) { 23 | let entity = ctx.widget().entity(); 24 | let message_adapter = ctx.message_adapter(); 25 | 26 | // increments a counter and send the result as message to `MainView`. 27 | self._my_thread = Some(thread::spawn(move || { 28 | let duration = time::Duration::from_secs(1); 29 | loop { 30 | thread::sleep(duration); 31 | message_adapter.send_message(Message::Increment, entity); 32 | } 33 | })); 34 | } 35 | fn messages( 36 | &mut self, 37 | mut messages: MessageReader, 38 | _registry: &mut Registry, 39 | ctx: &mut Context, 40 | ) { 41 | for message in messages.read::<Message>() { 42 | match message { 43 | Message::Increment => { 44 | self.count += 1; 45 | MainView::text_set(&mut ctx.widget(), self.count.to_string()); 46 | } 47 | } 48 | } 49 | } 50 | } 51 | 52 | widget!(MainView<MainState> { text: String }); 53 | 54 | impl Template for MainView { 55 | fn template(self, id: Entity, ctx: &mut BuildContext) -> Self { 56 | self.id(ID_MESSAGE).name(ID_MESSAGE).text("0").child( 57 | Stack::new() 58 | .id(ID_MESSAGE_STACK) 59 | .name(ID_MESSAGE_STACK) 60 | .margin(8) 61 | .spacing(4) 62 | .child( 63 | TextBlock::new() 64 | .id(ID_MESSAGE_TEXTBLOCK_LABEL) 65 | .name(ID_MESSAGE_TEXTBLOCK_LABEL) 66 | .text("Message counter example") 67 | .style("header") 68 | .build(ctx), 69 | ) 70 | .child( 71 | TextBlock::new() 72 | .id(ID_MESSAGE_TEXTBLOCK_COUNTER) 73 | .name(ID_MESSAGE_TEXTBLOCK_COUNTER) 74 | .style("body") 75 | .margin((0, 8, 0, 0)) 76 | .text(id) 77 | .build(ctx), 78 | ) 79 | .build(ctx), 80 | ) 81 | } 82 | } 83 | 84 | fn main() { 85 | // use this only if you want to run it as web application. 86 | orbtk::initialize(); 87 | 88 | Application::new() 89 | .window(|ctx| { 90 | Window::new() 91 | .title("OrbTk - message example") 92 | .position((100.0, 100.0)) 93 | .size(420.0, 730.0) 94 | .child(MainView::new().build(ctx)) 95 | .build(ctx) 96 | }) 97 | .run(); 98 | } 99 | -------------------------------------------------------------------------------- /orbtk/examples/minimal.rs: -------------------------------------------------------------------------------- 1 | // ANCHOR: All 2 | // ANCHOR: Use 3 | use orbtk::prelude::*; 4 | // ANCHOR_END: Use 5 | 6 | // ANCHOR: Main 7 | fn main() { 8 | // ANCHOR_END: Main 9 | // ANCHOR: Initialize 10 | // use this only if you want to run it as web application. 11 | orbtk::initialize(); 12 | // ANCHOR_END: Initialize 13 | 14 | // ANCHOR: Application 15 | Application::new() 16 | // ANCHOR_END: Application 17 | // ANCHOR: Window 18 | .window(|ctx| { 19 | Window::new() 20 | .title("OrbTk - Minimal") 21 | .position((100.0, 100.0)) 22 | .size(420.0, 140.0) 23 | // ANCHOR_END: Window 24 | // ANCHOR: Child 25 | .child( 26 | TextBlock::new() 27 | // ANCHOR: Properties 28 | .font_size(28) 29 | .h_align("center") 30 | .text("Hey OrbTk!") 31 | .v_align("center") 32 | // ANCHOR_END: Properties 33 | .build(ctx), 34 | ) 35 | // ANCHOR: Build 36 | .build(ctx) 37 | // ANCHOR_END: Build 38 | // ANCHOR_END: Child 39 | }) 40 | // ANCHOR: Run 41 | .run(); 42 | // ANCHOR_END: Run 43 | } 44 | // ANCHOR_END: All 45 | -------------------------------------------------------------------------------- /orbtk/examples/overlay.rs: -------------------------------------------------------------------------------- 1 | use orbtk::prelude::*; 2 | 3 | widget!(MainView); 4 | 5 | impl Template for MainView { 6 | fn template(self, _: Entity, ctx: &mut BuildContext) -> Self { 7 | let container = Container::new() 8 | .background("#dfebf5") 9 | .width(200.0) 10 | .height(200.0) 11 | .child( 12 | TextBlock::new() 13 | .foreground("#3b434a") 14 | .text("Overlay") 15 | .v_align("center") 16 | .h_align("center") 17 | .build(ctx), 18 | ) 19 | .build(ctx); 20 | 21 | ctx.append_child_to_overlay(container).unwrap(); 22 | self.name("MainView").child( 23 | Container::new() 24 | .background("#e1bc21") 25 | .child( 26 | TextBlock::new() 27 | .text("MainView") 28 | .style("text_block_header") 29 | .v_align("center") 30 | .h_align("center") 31 | .build(ctx), 32 | ) 33 | .build(ctx), 34 | ) 35 | } 36 | } 37 | 38 | fn main() { 39 | // use this only if you want to run it as web application. 40 | orbtk::initialize(); 41 | 42 | Application::new() 43 | .window(|ctx| { 44 | Window::new() 45 | .title("OrbTk - overlay example") 46 | .position((100.0, 100.0)) 47 | .size(420.0, 730.0) 48 | .child(MainView::new().build(ctx)) 49 | .build(ctx) 50 | }) 51 | .run(); 52 | } 53 | -------------------------------------------------------------------------------- /orbtk/images/layout_constraints.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/redox-os/orbtk/eba9e77821551076bbf1d9f7ab44d788150e3446/orbtk/images/layout_constraints.png -------------------------------------------------------------------------------- /orbtk/images/orbtk_logo_dark.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/redox-os/orbtk/eba9e77821551076bbf1d9f7ab44d788150e3446/orbtk/images/orbtk_logo_dark.png -------------------------------------------------------------------------------- /orbtk/images/orbtk_space.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/redox-os/orbtk/eba9e77821551076bbf1d9f7ab44d788150e3446/orbtk/images/orbtk_space.png -------------------------------------------------------------------------------- /orbtk/screenshots/calculator.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/redox-os/orbtk/eba9e77821551076bbf1d9f7ab44d788150e3446/orbtk/screenshots/calculator.png -------------------------------------------------------------------------------- /orbtk/screenshots/calculator_dark_macos.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/redox-os/orbtk/eba9e77821551076bbf1d9f7ab44d788150e3446/orbtk/screenshots/calculator_dark_macos.png -------------------------------------------------------------------------------- /orbtk/screenshots/calculator_light_macos.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/redox-os/orbtk/eba9e77821551076bbf1d9f7ab44d788150e3446/orbtk/screenshots/calculator_light_macos.png -------------------------------------------------------------------------------- /orbtk/screenshots/calculator_redox.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/redox-os/orbtk/eba9e77821551076bbf1d9f7ab44d788150e3446/orbtk/screenshots/calculator_redox.png -------------------------------------------------------------------------------- /orbtk/screenshots/canvas.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/redox-os/orbtk/eba9e77821551076bbf1d9f7ab44d788150e3446/orbtk/screenshots/canvas.jpg -------------------------------------------------------------------------------- /orbtk/screenshots/canvas.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/redox-os/orbtk/eba9e77821551076bbf1d9f7ab44d788150e3446/orbtk/screenshots/canvas.png -------------------------------------------------------------------------------- /orbtk/screenshots/login.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/redox-os/orbtk/eba9e77821551076bbf1d9f7ab44d788150e3446/orbtk/screenshots/login.png -------------------------------------------------------------------------------- /orbtk/screenshots/message.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/redox-os/orbtk/eba9e77821551076bbf1d9f7ab44d788150e3446/orbtk/screenshots/message.png -------------------------------------------------------------------------------- /orbtk/screenshots/minimal.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/redox-os/orbtk/eba9e77821551076bbf1d9f7ab44d788150e3446/orbtk/screenshots/minimal.png -------------------------------------------------------------------------------- /orbtk/screenshots/msg_handler.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/redox-os/orbtk/eba9e77821551076bbf1d9f7ab44d788150e3446/orbtk/screenshots/msg_handler.png -------------------------------------------------------------------------------- /orbtk/screenshots/multi_window.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/redox-os/orbtk/eba9e77821551076bbf1d9f7ab44d788150e3446/orbtk/screenshots/multi_window.jpg -------------------------------------------------------------------------------- /orbtk/screenshots/multi_window.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/redox-os/orbtk/eba9e77821551076bbf1d9f7ab44d788150e3446/orbtk/screenshots/multi_window.png -------------------------------------------------------------------------------- /orbtk/screenshots/overlay.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/redox-os/orbtk/eba9e77821551076bbf1d9f7ab44d788150e3446/orbtk/screenshots/overlay.jpg -------------------------------------------------------------------------------- /orbtk/screenshots/overlay.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/redox-os/orbtk/eba9e77821551076bbf1d9f7ab44d788150e3446/orbtk/screenshots/overlay.png -------------------------------------------------------------------------------- /orbtk/screenshots/popup.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/redox-os/orbtk/eba9e77821551076bbf1d9f7ab44d788150e3446/orbtk/screenshots/popup.png -------------------------------------------------------------------------------- /orbtk/screenshots/showcase.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/redox-os/orbtk/eba9e77821551076bbf1d9f7ab44d788150e3446/orbtk/screenshots/showcase.png -------------------------------------------------------------------------------- /orbtk/screenshots/showcase_button.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/redox-os/orbtk/eba9e77821551076bbf1d9f7ab44d788150e3446/orbtk/screenshots/showcase_button.png -------------------------------------------------------------------------------- /orbtk/screenshots/showcase_button_macos.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/redox-os/orbtk/eba9e77821551076bbf1d9f7ab44d788150e3446/orbtk/screenshots/showcase_button_macos.png -------------------------------------------------------------------------------- /orbtk/screenshots/showcase_image.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/redox-os/orbtk/eba9e77821551076bbf1d9f7ab44d788150e3446/orbtk/screenshots/showcase_image.png -------------------------------------------------------------------------------- /orbtk/screenshots/showcase_interactive.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/redox-os/orbtk/eba9e77821551076bbf1d9f7ab44d788150e3446/orbtk/screenshots/showcase_interactive.png -------------------------------------------------------------------------------- /orbtk/screenshots/showcase_items.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/redox-os/orbtk/eba9e77821551076bbf1d9f7ab44d788150e3446/orbtk/screenshots/showcase_items.png -------------------------------------------------------------------------------- /orbtk/screenshots/showcase_layouts.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/redox-os/orbtk/eba9e77821551076bbf1d9f7ab44d788150e3446/orbtk/screenshots/showcase_layouts.png -------------------------------------------------------------------------------- /orbtk/screenshots/showcase_localization.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/redox-os/orbtk/eba9e77821551076bbf1d9f7ab44d788150e3446/orbtk/screenshots/showcase_localization.png -------------------------------------------------------------------------------- /orbtk/screenshots/showcase_navigation.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/redox-os/orbtk/eba9e77821551076bbf1d9f7ab44d788150e3446/orbtk/screenshots/showcase_navigation.png -------------------------------------------------------------------------------- /orbtk/screenshots/stack.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/redox-os/orbtk/eba9e77821551076bbf1d9f7ab44d788150e3446/orbtk/screenshots/stack.png -------------------------------------------------------------------------------- /orbtk/src/application.rs: -------------------------------------------------------------------------------- 1 | //! This module contains the base elements of an OrbTk application (Application, WindowBuilder and Window). 2 | 3 | use std::sync::mpsc; 4 | 5 | use dces::prelude::Entity; 6 | 7 | use crate::{ 8 | core::{application::WindowAdapter, localization::*, *}, 9 | shell::{Shell, ShellRequest}, 10 | }; 11 | 12 | /// The `Application` represents the entry point of an OrbTk based application. 13 | pub struct Application { 14 | // shells: Vec<Shell<WindowAdapter>>, 15 | request_sender: mpsc::Sender<ShellRequest<WindowAdapter>>, 16 | shell: Shell<WindowAdapter>, 17 | name: Box<str>, 18 | theme: Rc<Theme>, 19 | localization: Option<Rc<RefCell<Box<dyn Localization>>>>, 20 | } 21 | 22 | impl Default for Application { 23 | fn default() -> Self { 24 | Application::from_name("orbtk_application") 25 | } 26 | } 27 | 28 | impl Application { 29 | /// Creates a new application. 30 | pub fn new() -> Self { 31 | Self::default() 32 | } 33 | 34 | /// Sets the default theme for the application. Could be changed per window. 35 | pub fn theme(mut self, theme: Theme) -> Self { 36 | self.theme = Rc::new(theme); 37 | self 38 | } 39 | 40 | pub fn localization<L>(mut self, localization: L) -> Self 41 | where 42 | L: Localization + 'static, 43 | { 44 | self.localization = Some(Rc::new(RefCell::new(Box::new(localization)))); 45 | self 46 | } 47 | 48 | /// Create a new application with the given name. 49 | pub fn from_name(name: impl Into<Box<str>>) -> Self { 50 | let (sender, receiver) = mpsc::channel(); 51 | 52 | Application { 53 | request_sender: sender, 54 | name: name.into(), 55 | shell: Shell::new(receiver), 56 | theme: Rc::new(crate::widgets::themes::theme_orbtk::theme_default()), 57 | localization: None, 58 | } 59 | } 60 | 61 | /// Creates a new window and add it to the application. 62 | pub fn window<F: Fn(&mut BuildContext) -> Entity + 'static>(mut self, create_fn: F) -> Self { 63 | let (adapter, settings, receiver) = create_window( 64 | self.name.clone(), 65 | &self.theme, 66 | self.request_sender.clone(), 67 | create_fn, 68 | self.localization.clone(), 69 | ); 70 | 71 | self.shell 72 | .create_window_from_settings(settings, adapter) 73 | .request_receiver(receiver) 74 | .build(); 75 | 76 | self 77 | } 78 | 79 | /// Starts the application and run it until quit is requested. 80 | pub fn run(mut self) { 81 | self.shell.run(); 82 | } 83 | } 84 | -------------------------------------------------------------------------------- /orbtk/src/lib.rs: -------------------------------------------------------------------------------- 1 | #![crate_name = "orbtk"] 2 | #![crate_type = "lib"] 3 | 4 | //! # OrbTk - The Orbital Toolkit 5 | //! 6 | //! ![Welcome to the OrbTk planet.][orbtk_planet] 7 | //! 8 | //! The `OrbTk` crate implements a cross-platform (G)UI toolkit for 9 | //! building scalable user interfaces. The codebase is natively build 10 | //! with the `Rust` programming language. 11 | //! 12 | //! `OrbTk` provides a [functional reactive-like][functional_reative] API. It depends on 13 | //! the rust [`DCES`][dces] crate, that provides an Entity Component 14 | //! System. Interaction with `DCES` is managed via the `Entity 15 | //! Component Manager`(ECM), a wrapper API, that transparently mapps `OrbTk` widgets 16 | //! to `ECM` entities and `OrbTk` properties to `ECM` components. 17 | //! 18 | //! The main goals of `OrbTk` are speed, ease of use, and cross-platform compatibility. 19 | //! 20 | //! Happy 🦀 hacking! ✨ 21 | //! 22 | //! [dces]: https://docs.rs/dces 23 | //! [functional_reative]: https://en.wikipedia.org/wiki/Functional_reactive_programming 24 | //! [orbtk_planet]: https://raw.githubusercontent.com/rzerres/orbtk/wip_documentation/orbtk/images/orbtk_planet.svg 25 | // //! [orbtk_planet]: https://raw.githubusercontent.com/redox-os/orbtk/develop/orbtk/images/orbtk_planet.svg 26 | 27 | //#![feature(extern_doc)] 28 | //#[doc(include="../README.md")] 29 | 30 | /// Tries to make your OrbTk experience more convenient. 31 | /// It will automatically import traits and types into scope, that you likely need in your app. 32 | pub use orbtk_orbclient::prelude::initialize; 33 | 34 | /// Handles core implenentations (OrbTk building blocks). 35 | pub mod core { 36 | pub use orbtk_core::application; 37 | pub use orbtk_core::localization; 38 | pub use orbtk_core::macros; 39 | pub use orbtk_core::prelude::*; 40 | pub use orbtk_core::theming; 41 | pub use orbtk_core::tree; 42 | } 43 | 44 | /// Handles procedural macros. 45 | pub mod proc_macros { 46 | pub use orbtk_proc_macros::*; 47 | } 48 | 49 | /// Handles renderer implementations. 50 | pub mod render { 51 | pub use orbtk_tinyskia::*; 52 | } 53 | 54 | /// Handles shell interaction implementations. 55 | pub mod shell { 56 | pub use orbtk_orbclient::prelude::*; 57 | } 58 | 59 | /// Handles helper utilities and global methods. 60 | pub mod utils { 61 | pub use orbtk_utils::*; 62 | } 63 | 64 | /// Handle widget implementations. 65 | pub mod widgets { 66 | pub use orbtk_widgets::*; 67 | } 68 | 69 | /// Pre-selects commonly used OrbTk crates and put them into scope. 70 | pub mod prelude; 71 | 72 | mod application; 73 | 74 | pub use self::application::*; 75 | -------------------------------------------------------------------------------- /orbtk/src/prelude.rs: -------------------------------------------------------------------------------- 1 | /// This module pre-selects commonly used OrbTk crates and put them into scope. 2 | pub use std::{ 3 | any::{Any, TypeId}, 4 | cell::RefCell, 5 | collections::HashMap, 6 | fmt::Debug, 7 | rc::Rc, 8 | }; 9 | 10 | pub use dces::prelude::*; 11 | 12 | pub use crate::{ 13 | core::macros::*, core::*, proc_macros::*, render::prelude::*, utils::prelude::*, 14 | widgets::prelude::*, Application, 15 | }; 16 | -------------------------------------------------------------------------------- /orbtk_core/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | authors = ["Florian Blasius <flovanpt@posteo.de>"] 3 | description = "Core crate that provides base api and elements for OrbTk like widgets basis." 4 | edition = "2021" 5 | keywords = ["ui", "api"] 6 | license = "MIT" 7 | name = "orbtk_core" 8 | readme = "README.md" 9 | repository = "https://github.com/redox-os/orbtk" 10 | version = "0.3.1-alpha5" 11 | 12 | [features] 13 | debug = [] 14 | 15 | [lib] 16 | doctest = false 17 | 18 | [dependencies] 19 | dirs-next = { version = "2.0" } 20 | derive_more = { version = "0.99", default-features = false, features = ["constructor"] } 21 | memchr = {version = "2"} 22 | orbtk_proc_macros = { path = "../proc_macros", version = "0.3.1-alpha5" } 23 | orbtk_utils = { path = "../utils", version = "0.3.1-alpha5" } 24 | orbtk_tinyskia = { path = "../orbtk_tinyskia", version = "0.3.1-alpha5", default-features = false } 25 | orbtk_orbclient = { path = "../orbtk_orbclient", version = "0.3.1-alpha5", default-features = false } 26 | raw-window-handle = "0.4" 27 | ron = { version = "0.8" } 28 | rust_decimal = { version = "1.15" } 29 | serde = { version = "1.0" } 30 | serde_derive = { version = "1.0" } 31 | smallvec = { version = "1", default-features = false } 32 | 33 | [dependencies.dces] 34 | #version = "0.3.1" 35 | git = "https://gitlab.redox-os.org/redox-os/dces-rust.git" 36 | branch = "master" 37 | #branch = "develop" 38 | 39 | [target.'cfg(not(target_arch = "wasm32"))'.dependencies] 40 | threadpool = { version = "1.8" } 41 | 42 | [target.wasm32-unknown-unknown.dependencies] 43 | stdweb = { version = "0.4" } 44 | -------------------------------------------------------------------------------- /orbtk_core/README.md: -------------------------------------------------------------------------------- 1 | # orbtk_core 2 | 3 | Provides base traits and structs for application, widgets and properties. It's part of [OrbTk](https://gitlab.redox-os.org/redox-os/orbtk) - The Rust UI-Toolkit. 4 | 5 | [](https://github.com/redox-os/orbtk/actions) 6 | [](../../LICENSE) 7 | 8 | ## Dependencies 9 | 10 | * [dces](https://gitlab.redox-os.org/redox-os/dces-rust) (MIT): Entity Component System 11 | * [ron](https://github.com/ron-rs/ron) (Apache 2.0, MIT): Save settings to ron file 12 | * [serde](https://github.com/serde-rs/serde) (Apache 2.0, MIT): serialize, deserialize setting objects 13 | * [dirs](https://github.com/xdg-rs/dirs.git) (Apache 2.0, MIT): platform-specific, user-accessible locations 14 | * [raw-window-handle](https://github.com/rust-windowing/raw-window-handle) (MIT): access to a window's platform-specific raw window handle 15 | 16 | ## License 17 | 18 | Licensed under MIT license ([LICENSE](../../LICENSE)). 19 | -------------------------------------------------------------------------------- /orbtk_core/src/application/context_provider.rs: -------------------------------------------------------------------------------- 1 | use std::{ 2 | cell::{Cell, RefCell}, 3 | collections::BTreeMap, 4 | rc::Rc, 5 | sync::mpsc, 6 | }; 7 | 8 | use dces::prelude::*; 9 | 10 | use super::WindowAdapter; 11 | 12 | use crate::{ 13 | event::*, 14 | layout::*, 15 | localization::Localization, 16 | render_object::*, 17 | shell::{ShellRequest, WindowRequest}, 18 | utils::Point, 19 | widget_base::*, 20 | }; 21 | 22 | /// Temporary solution to share dependencies and needs to be refactored. 23 | #[derive(Clone)] 24 | pub struct ContextProvider { 25 | /// Reference counted cells of render objects. 26 | pub render_objects: Rc<RefCell<BTreeMap<Entity, Box<dyn RenderObject>>>>, 27 | 28 | /// Reference counted cells of layouts objects. 29 | pub layouts: Rc<RefCell<BTreeMap<Entity, Box<dyn Layout>>>>, 30 | 31 | /// Reference counted cells of handler_map objects. 32 | pub handler_map: Rc<RefCell<EventHandlerMap>>, 33 | 34 | /// Reference counted cells of handler_states. 35 | pub states: Rc<RefCell<BTreeMap<Entity, Box<dyn State>>>>, 36 | 37 | /// Event adapter objects. 38 | pub event_adapter: EventAdapter, 39 | 40 | /// Message adapter objects. 41 | pub message_adapter: MessageAdapter, 42 | 43 | /// Reference counted cells of mouse_positions defined as `points` 44 | pub mouse_position: Rc<Cell<Point>>, 45 | 46 | /// A window_sender object, used for multiparty session-typed communication. 47 | pub window_sender: mpsc::Sender<WindowRequest>, 48 | 49 | /// A shell_sender object, used for multiparty session-typed communication. 50 | pub shell_sender: mpsc::Sender<ShellRequest<WindowAdapter>>, 51 | 52 | /// Holds the application name. 53 | pub application_name: String, 54 | 55 | /// Reference counted cell to track the `first_run` 56 | pub first_run: Rc<Cell<bool>>, 57 | 58 | /// Holds a raw window handler object. 59 | pub raw_window_handle: Option<raw_window_handle::RawWindowHandle>, 60 | 61 | // TODO: make it thread safe 62 | /// Reference counted cells that hold the supported localization identifiers. 63 | pub localization: Option<Rc<RefCell<Box<dyn Localization>>>>, 64 | } 65 | 66 | impl ContextProvider { 67 | /// Creates a new context provider. 68 | pub fn new( 69 | window_sender: mpsc::Sender<WindowRequest>, 70 | shell_sender: mpsc::Sender<ShellRequest<WindowAdapter>>, 71 | application_name: impl Into<String>, 72 | localization: Option<Rc<RefCell<Box<dyn Localization>>>>, 73 | ) -> Self { 74 | ContextProvider { 75 | render_objects: Rc::new(RefCell::new(BTreeMap::new())), 76 | layouts: Rc::new(RefCell::new(BTreeMap::new())), 77 | handler_map: Rc::new(RefCell::new(EventHandlerMap::new())), 78 | states: Rc::new(RefCell::new(BTreeMap::new())), 79 | event_adapter: EventAdapter::new(window_sender.clone()), 80 | message_adapter: MessageAdapter::new(window_sender.clone()), 81 | mouse_position: Rc::new(Cell::new(Point::new(0.0, 0.0))), 82 | window_sender, 83 | shell_sender, 84 | application_name: application_name.into(), 85 | first_run: Rc::new(Cell::new(true)), 86 | raw_window_handle: None, 87 | localization, 88 | } 89 | } 90 | } 91 | -------------------------------------------------------------------------------- /orbtk_core/src/application/mod.rs: -------------------------------------------------------------------------------- 1 | //! This module contains the base elements of an OrbTk application (Application, WindowBuilder and Window). 2 | 3 | pub use self::context_provider::*; 4 | pub use self::overlay::*; 5 | pub use self::window_adapter::*; 6 | 7 | mod context_provider; 8 | mod overlay; 9 | mod window_adapter; 10 | -------------------------------------------------------------------------------- /orbtk_core/src/application/overlay.rs: -------------------------------------------------------------------------------- 1 | pub use std::{ 2 | any::{Any, TypeId}, 3 | cell::RefCell, 4 | collections::{HashMap, HashSet}, 5 | fmt::Debug, 6 | rc::Rc, 7 | }; 8 | 9 | use dces::prelude::*; 10 | 11 | use crate::{ 12 | event::*, 13 | layout::{AbsoluteLayout, Layout}, 14 | proc_macros::WidgetCtx, 15 | properties::*, 16 | theming::Selector, 17 | utils::*, 18 | widget, 19 | widget_base::*, 20 | }; 21 | 22 | widget!( 23 | /// The `Overlay` is used to draw its children on the top of all 24 | /// other widgets in the tree. 25 | Overlay 26 | ); 27 | 28 | impl Template for Overlay { 29 | fn template(self, _: Entity, _: &mut BuildContext) -> Self { 30 | self.name("Overlay") 31 | } 32 | 33 | fn layout(&self) -> Box<dyn Layout> { 34 | Box::new(AbsoluteLayout::new()) 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /orbtk_core/src/event/drop.rs: -------------------------------------------------------------------------------- 1 | use crate::{ 2 | prelude::*, 3 | proc_macros::{Event, IntoHandler}, 4 | }; 5 | 6 | /// This event occurs on a drop file event on the window. 7 | #[derive(Event, Clone)] 8 | pub struct DropFileEvent { 9 | pub file_name: String, 10 | pub position: Point, 11 | } 12 | 13 | /// This event occurs on a drop file event on the window. 14 | #[derive(Event, Clone)] 15 | pub struct DropTextEvent { 16 | pub text: String, 17 | pub position: Point, 18 | } 19 | 20 | pub type DropFn = dyn Fn(&mut StatesContext, String, Point) -> bool + 'static; 21 | 22 | /// Structure to handle drops inside the file handles. 23 | #[derive(IntoHandler)] 24 | pub struct DropFileHandler { 25 | /// A reference counted handler object. 26 | pub handler: Rc<DropFn>, 27 | } 28 | 29 | impl EventHandler for DropFileHandler { 30 | fn handle_event(&self, states: &mut StatesContext, event: &EventBox) -> bool { 31 | if let Ok(event) = event.downcast_ref::<DropFileEvent>() { 32 | return (self.handler)(states, event.file_name.clone(), event.position); 33 | } 34 | 35 | false 36 | } 37 | 38 | fn handles_event(&self, event: &EventBox) -> bool { 39 | event.is_type::<DropFileEvent>() 40 | } 41 | } 42 | 43 | /// Structure to handle droppings inside the text handles. 44 | #[derive(IntoHandler)] 45 | pub struct DropTextHandler { 46 | /// A reference counted handler object. 47 | pub handler: Rc<DropFn>, 48 | } 49 | 50 | impl EventHandler for DropTextHandler { 51 | fn handle_event(&self, states: &mut StatesContext, event: &EventBox) -> bool { 52 | if let Ok(event) = event.downcast_ref::<DropTextEvent>() { 53 | return (self.handler)(states, event.text.clone(), event.position); 54 | } 55 | 56 | false 57 | } 58 | 59 | fn handles_event(&self, event: &EventBox) -> bool { 60 | event.is_type::<DropTextEvent>() 61 | } 62 | } 63 | 64 | /// Implement this trait if you want that your widget can handle drop (file | text) events 65 | pub trait DropHandler: Sized + Widget { 66 | /// Inserts a handler for drop file events. 67 | fn on_drop_file<H: Fn(&mut StatesContext, String, Point) -> bool + 'static>( 68 | self, 69 | handler: H, 70 | ) -> Self { 71 | self.insert_handler(DropFileHandler { 72 | handler: Rc::new(handler), 73 | }) 74 | } 75 | 76 | /// Inserts a handler for drop text events. 77 | fn on_drop_text<H: Fn(&mut StatesContext, String, Point) -> bool + 'static>( 78 | self, 79 | handler: H, 80 | ) -> Self { 81 | self.insert_handler(DropTextHandler { 82 | handler: Rc::new(handler), 83 | }) 84 | } 85 | } 86 | -------------------------------------------------------------------------------- /orbtk_core/src/event/event_handler.rs: -------------------------------------------------------------------------------- 1 | use crate::{event::EventBox, widget_base::StatesContext}; 2 | 3 | /// This trait is used to define an event handler. 4 | pub trait EventHandler { 5 | /// Handles an `event` by the given `widget`. If it returns `true` the event will not be forwarded. 6 | fn handle_event(&self, state_context: &mut StatesContext, event: &EventBox) -> bool; 7 | 8 | /// Check if the handler could handle the given event box. 9 | fn handles_event(&self, event: &EventBox) -> bool; 10 | } 11 | -------------------------------------------------------------------------------- /orbtk_core/src/event/focus.rs: -------------------------------------------------------------------------------- 1 | use dces::prelude::Entity; 2 | 3 | use crate::{ 4 | prelude::*, 5 | proc_macros::{Event, IntoHandler}, 6 | }; 7 | 8 | /// Used to request keyboard focus on the window. 9 | #[derive(Event, Clone)] 10 | pub enum FocusEvent { 11 | RequestFocus(Entity), 12 | RemoveFocus(Entity), 13 | } 14 | 15 | pub type FocusHandlerFn = dyn Fn(&mut StatesContext, FocusEvent) -> bool + 'static; 16 | 17 | /// Structure for the focus handling of an event 18 | #[derive(IntoHandler)] 19 | pub struct FocusEventHandler { 20 | /// A reference counted handler 21 | pub handler: Rc<FocusHandlerFn>, 22 | } 23 | 24 | impl EventHandler for FocusEventHandler { 25 | fn handle_event(&self, states: &mut StatesContext, event: &EventBox) -> bool { 26 | if let Ok(event) = event.downcast_ref::<FocusEvent>() { 27 | return (self.handler)(states, event.clone()); 28 | } 29 | 30 | false 31 | } 32 | 33 | fn handles_event(&self, event: &EventBox) -> bool { 34 | event.is_type::<FocusEvent>() 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /orbtk_core/src/event/key.rs: -------------------------------------------------------------------------------- 1 | use std::rc::Rc; 2 | 3 | use crate::{ 4 | prelude::*, 5 | proc_macros::*, 6 | shell::{Key, KeyEvent}, 7 | }; 8 | 9 | use super::{EventBox, EventHandler}; 10 | 11 | /// A structure to handle a keyboard `key-down` events 12 | #[derive(Event)] 13 | pub struct KeyDownEvent { 14 | /// the keyboard event 15 | pub event: KeyEvent, 16 | } 17 | 18 | /// A structure to handle a keyboard `key-up` events 19 | #[derive(Event)] 20 | pub struct KeyUpEvent { 21 | /// the keyboard event 22 | pub event: KeyEvent, 23 | } 24 | 25 | pub type KeyHandler = dyn Fn(&mut StatesContext, KeyEvent) -> bool + 'static; 26 | 27 | /// Used to handle key down events. Could be attached to a widget. 28 | #[derive(IntoHandler)] 29 | pub struct KeyDownEventHandler { 30 | handler: Rc<KeyHandler>, 31 | } 32 | 33 | impl EventHandler for KeyDownEventHandler { 34 | fn handle_event(&self, state_context: &mut StatesContext, event: &EventBox) -> bool { 35 | event 36 | .downcast_ref::<KeyDownEvent>() 37 | .ok() 38 | .map_or(false, |event| { 39 | (self.handler)(state_context, event.event.clone()) 40 | }) 41 | } 42 | 43 | fn handles_event(&self, event: &EventBox) -> bool { 44 | event.is_type::<KeyDownEvent>() 45 | } 46 | } 47 | 48 | pub trait KeyDownHandler: Sized + Widget { 49 | /// Inserts a handler. 50 | fn on_key_down<H: Fn(&mut StatesContext, KeyEvent) -> bool + 'static>( 51 | self, 52 | handler: H, 53 | ) -> Self { 54 | self.insert_handler(KeyDownEventHandler { 55 | handler: Rc::new(handler), 56 | }) 57 | } 58 | 59 | /// Handles events triggered by a specific key. 60 | fn on_key_down_key<H: Fn() -> bool + 'static>(self, key: Key, handler: H) -> Self { 61 | self.on_key_down( 62 | move |_, event| { 63 | if event.key == key { 64 | handler() 65 | } else { 66 | false 67 | } 68 | }, 69 | ) 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /orbtk_core/src/event/mod.rs: -------------------------------------------------------------------------------- 1 | //! This module contains all resources to call and handle events. 2 | 3 | use std::{any::Any, collections::BTreeMap, rc::Rc}; 4 | 5 | use dces::entity::Entity; 6 | 7 | use crate::widget_base::StatesContext; 8 | 9 | pub use self::drop::*; 10 | pub use self::editable::*; 11 | pub use self::event_adapter::*; 12 | pub use self::event_handler::*; 13 | pub use self::event_queue::*; 14 | pub use self::focus::*; 15 | pub use self::key::*; 16 | pub use self::mouse::*; 17 | pub use self::system::*; 18 | pub use self::text_input::*; 19 | pub use self::window::*; 20 | 21 | mod drop; 22 | mod editable; 23 | mod event_adapter; 24 | mod event_handler; 25 | mod event_queue; 26 | mod focus; 27 | mod key; 28 | mod mouse; 29 | mod system; 30 | mod text_input; 31 | mod window; 32 | 33 | /// Defines the strategy of an event how it moves through the tree. 34 | #[derive(Debug, Clone, Eq, PartialEq)] 35 | pub enum EventStrategy { 36 | // /// From root to leaf. 37 | // TopDown, 38 | /// From leaf to root. 39 | BottomUp, 40 | 41 | /// Occurs direct. 42 | Direct, 43 | } 44 | 45 | /// Used to define an event. 46 | pub trait Event: Any { 47 | fn strategy(&self) -> EventStrategy { 48 | EventStrategy::BottomUp 49 | } 50 | } 51 | 52 | /// Assign the `EventHandlerMap` type. 53 | pub type EventHandlerMap = BTreeMap<Entity, Vec<Rc<dyn EventHandler>>>; 54 | 55 | /// Assign the `TriggerHandler` type. 56 | pub type TriggerHandler = dyn Fn(&mut StatesContext, Entity) + 'static; 57 | -------------------------------------------------------------------------------- /orbtk_core/src/event/system.rs: -------------------------------------------------------------------------------- 1 | use crate::{event::Event, proc_macros::Event}; 2 | 3 | /// An enumeration of valid system events 4 | #[derive(Event)] 5 | pub enum SystemEvent { 6 | Quit, 7 | } 8 | -------------------------------------------------------------------------------- /orbtk_core/src/event/text_input.rs: -------------------------------------------------------------------------------- 1 | use std::rc::Rc; 2 | 3 | use crate::{prelude::*, proc_macros::*}; 4 | 5 | /// The text input occurs if the keyboard registers a text input. 6 | #[derive(Clone, Default, Debug, Event)] 7 | pub struct TextInputEvent { 8 | pub text: String, 9 | } 10 | 11 | /// Callback closure to handle text input events. 12 | pub type TextHandler = dyn Fn(&mut StatesContext, &str) -> bool + 'static; 13 | 14 | /// Internal struct to manage text input event handlers. 15 | #[derive(IntoHandler)] 16 | pub struct TextInputEventHandler { 17 | handler: Rc<TextHandler>, 18 | } 19 | 20 | impl EventHandler for TextInputEventHandler { 21 | fn handle_event(&self, state_context: &mut StatesContext, event: &EventBox) -> bool { 22 | event 23 | .downcast_ref::<TextInputEvent>() 24 | .ok() 25 | .map_or(false, |event| { 26 | (self.handler)(state_context, event.text.as_str()) 27 | }) 28 | } 29 | 30 | fn handles_event(&self, event: &EventBox) -> bool { 31 | event.is_type::<TextInputEvent>() 32 | } 33 | } 34 | 35 | /// Implement this trait for widgets that should handle text input events. 36 | /// 37 | /// # Examples 38 | /// 39 | /// ```rust 40 | /// widget!(MyWidget: TextInputHandler {}); 41 | /// 42 | /// MyWidget::new() 43 | /// .on_text_input(|_ctx, text| { 44 | /// println!("{}", text); 45 | /// true 46 | /// }).build(ctx) 47 | /// ``` 48 | pub trait TextInputHandler: Sized + Widget { 49 | /// Callback that is called when a text input event reaches the widget. 50 | /// 51 | /// If the callback returns `true` the event is marked as handled and will not available to 52 | /// to other widgets. 53 | fn on_text_input<H: Fn(&mut StatesContext, &str) -> bool + 'static>(self, handler: H) -> Self { 54 | self.insert_handler(TextInputEventHandler { 55 | handler: Rc::new(handler), 56 | }) 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /orbtk_core/src/event/window.rs: -------------------------------------------------------------------------------- 1 | use std::rc::Rc; 2 | 3 | use super::*; 4 | 5 | use crate::{proc_macros::*, widget_base::*}; 6 | 7 | /// The enumeration of valid window events. 8 | #[derive(Clone, Event)] 9 | pub enum WindowEvent { 10 | Resize { width: f64, height: f64 }, 11 | ActiveChanged(bool), 12 | None, 13 | } 14 | 15 | pub type WindowHandlerFn = dyn Fn(&mut StatesContext, WindowEvent) -> bool + 'static; 16 | 17 | /// The structure handling windows events. 18 | #[derive(IntoHandler)] 19 | pub struct WindowEventHandler { 20 | /// A reference counted handler. 21 | pub handler: Rc<WindowHandlerFn>, 22 | } 23 | 24 | impl EventHandler for WindowEventHandler { 25 | fn handle_event(&self, states: &mut StatesContext, event: &EventBox) -> bool { 26 | if let Ok(event) = event.downcast_ref::<WindowEvent>() { 27 | return (self.handler)(states, event.clone()); 28 | } 29 | 30 | false 31 | } 32 | 33 | fn handles_event(&self, event: &EventBox) -> bool { 34 | event.is_type::<WindowEvent>() 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /orbtk_core/src/layout/mod.rs: -------------------------------------------------------------------------------- 1 | //! This module contains the layout types of an OrbTk application (Absolute, Fixed, Grid, Padding, Popup, Stack). 2 | use std::{any::Any, collections::BTreeMap}; 3 | 4 | use dces::prelude::*; 5 | 6 | use crate::{render::RenderContext2D, theming::*, tree::Tree, utils::*}; 7 | 8 | pub use self::absolute::*; 9 | pub use self::fixed_size::*; 10 | pub use self::grid::*; 11 | pub use self::padding::*; 12 | pub use self::popup::*; 13 | pub use self::stack::*; 14 | 15 | mod absolute; 16 | mod fixed_size; 17 | mod grid; 18 | mod padding; 19 | mod popup; 20 | mod stack; 21 | 22 | /// The layout process will order the children of a given widget in a dynamic iteration. 23 | /// It will respect constraint values between its elements. The following image illustrates 24 | /// the relationship between known layout elements: 25 | /// 26 | /// ![cheat-sheet: layout constraints][layout_constraints] 27 | /// 28 | /// [layout_constraints]: https://raw.githubusercontent.com/rzerres/orbtk/wip_documentation/orbtk/images/layout_constraints.png 29 | // /// [layout_constraints.svg]: https://raw.githubusercontent.com/redox-os/orbtk/develop/orbtk/images/layout_constraints.svg 30 | 31 | pub trait Layout: Any { 32 | // Measure all children before the arrangement. 33 | fn measure( 34 | &self, 35 | render_context_2_d: &mut RenderContext2D, 36 | entity: Entity, 37 | ecm: &mut EntityComponentManager<Tree>, 38 | layouts: &BTreeMap<Entity, Box<dyn Layout>>, 39 | theme: &Theme, 40 | ) -> DirtySize; 41 | 42 | /// Arranges and sizes the children. 43 | fn arrange( 44 | &self, 45 | render_context_2_d: &mut RenderContext2D, 46 | parent_size: (f64, f64), 47 | entity: Entity, 48 | ecm: &mut EntityComponentManager<Tree>, 49 | layouts: &BTreeMap<Entity, Box<dyn Layout>>, 50 | theme: &Theme, 51 | ) -> (f64, f64); 52 | } 53 | 54 | fn component<C: Component + Clone>( 55 | ecm: &mut EntityComponentManager<Tree>, 56 | entity: Entity, 57 | component: &str, 58 | ) -> C { 59 | ecm.component_store() 60 | .get::<C>(component, entity) 61 | .unwrap() 62 | .clone() 63 | } 64 | 65 | fn try_component<C: Component + Clone>( 66 | ecm: &mut EntityComponentManager<Tree>, 67 | entity: Entity, 68 | component: &str, 69 | ) -> Option<C> { 70 | if let Ok(c) = ecm.component_store().get::<C>(component, entity) { 71 | return Some(c.clone()); 72 | } 73 | 74 | None 75 | } 76 | 77 | fn component_or_default<C: Component + Clone + Default>( 78 | ecm: &mut EntityComponentManager<Tree>, 79 | entity: Entity, 80 | component: &str, 81 | ) -> C { 82 | ecm.component_store() 83 | .get::<C>(component, entity) 84 | .map(Clone::clone) 85 | .unwrap_or_default() 86 | } 87 | 88 | fn component_try_mut<'a, C: Component>( 89 | ecm: &'a mut EntityComponentManager<Tree>, 90 | entity: Entity, 91 | component: &str, 92 | ) -> Option<&'a mut C> { 93 | ecm.component_store_mut() 94 | .get_mut::<C>(component, entity) 95 | .ok() 96 | } 97 | -------------------------------------------------------------------------------- /orbtk_core/src/lib.rs: -------------------------------------------------------------------------------- 1 | /*! 2 | API crate that provides base api and elements for OrbTk like widgets basis. 3 | */ 4 | 5 | #[macro_use] 6 | extern crate derive_more; 7 | 8 | pub(crate) use orbtk_orbclient::prelude as shell; 9 | pub(crate) use orbtk_proc_macros as proc_macros; 10 | pub(crate) use orbtk_tinyskia::prelude as render; 11 | pub(crate) use orbtk_utils::prelude as utils; 12 | 13 | pub mod application; 14 | pub mod localization; 15 | pub mod theming; 16 | #[macro_use] 17 | pub mod event; 18 | pub mod layout; 19 | pub mod prelude; 20 | pub mod properties; 21 | pub mod render_object; 22 | pub mod services; 23 | pub mod systems; 24 | pub mod tree; 25 | pub mod widget_base; 26 | 27 | #[macro_use] 28 | pub mod macros; 29 | -------------------------------------------------------------------------------- /orbtk_core/src/localization/mod.rs: -------------------------------------------------------------------------------- 1 | //! This module contains the localization methods, that handle runtime based adaption of involved OrbTk entities. 2 | 3 | mod ron_localization; 4 | 5 | pub use self::ron_localization::*; 6 | 7 | pub trait Localization { 8 | /// Gets the current language by language key e.g. `en_US`, 9 | /// `de_DE` or `fr_FR`. 10 | fn language(&self) -> &String; 11 | 12 | /// Sets the current language identified by its language key (e.g. `en_US`, `de_DE`). 13 | fn set_language(&mut self, key: &str); 14 | 15 | /// Gets the translated text for the given key. If there is no 16 | /// given translation, the `key` will be returned as the result. 17 | fn text(&self, key: String) -> String; 18 | } 19 | -------------------------------------------------------------------------------- /orbtk_core/src/localization/ron_localization/dictionary.rs: -------------------------------------------------------------------------------- 1 | use std::collections::HashMap; 2 | 3 | use ron::de::from_str; 4 | use serde_derive::Deserialize; 5 | 6 | /// Internal struct used by the `RonLocalization` to parse as language file. 7 | #[derive(Debug, Clone, Deserialize)] 8 | pub struct Dictionary { 9 | pub words: HashMap<String, String>, 10 | } 11 | 12 | impl From<&str> for Dictionary { 13 | fn from(s: &str) -> Self { 14 | from_str(s).unwrap() 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /orbtk_core/src/localization/ron_localization/mod.rs: -------------------------------------------------------------------------------- 1 | use std::collections::HashMap; 2 | 3 | use crate::localization::Localization; 4 | 5 | use dictionary::Dictionary; 6 | 7 | mod dictionary; 8 | 9 | /// Used to build a new `RonLocalization` and configure language file 10 | /// path and initial language. 11 | #[derive(Debug, Default, Clone)] 12 | pub struct RonLocalizationBuilder { 13 | language: String, 14 | dictionaries: HashMap<String, Dictionary>, 15 | } 16 | 17 | impl RonLocalizationBuilder { 18 | /// Adds a new dictionary. 19 | pub fn dictionary(mut self, key: impl Into<String>, dictionary: &str) -> Self { 20 | self.dictionaries 21 | .insert(key.into(), Dictionary::from(dictionary)); 22 | self 23 | } 24 | 25 | /// Sets the initial language. 26 | pub fn language(mut self, language: impl Into<String>) -> Self { 27 | self.language = language.into(); 28 | self 29 | } 30 | 31 | /// Builds a new ron localization service. 32 | pub fn build(self) -> RonLocalization { 33 | RonLocalization { 34 | language: self.language, 35 | dictionaries: self.dictionaries, 36 | } 37 | } 38 | } 39 | 40 | /// `RonLocalization` represents the default implementation of a 41 | /// localization service based on `ron`. 42 | /// 43 | /// # Example 44 | /// 45 | /// ```rust 46 | /// pub const EN_US: &str = include_str!("../assets/dictionary_en_US.ron"); 47 | /// 48 | /// let localization = RonLocalization::create().language("en_US").dictionary("en_US", EN_US).build(); 49 | /// if let Some(text) = localization.text("hello") { 50 | /// println!("{}", text); 51 | /// } 52 | /// ``` 53 | #[derive(Debug, Default, Clone)] 54 | pub struct RonLocalization { 55 | language: String, 56 | dictionaries: HashMap<String, Dictionary>, 57 | } 58 | 59 | impl RonLocalization { 60 | /// Creates a new `RonLocalizationBuilder` to configure the localization service. 61 | pub fn create() -> RonLocalizationBuilder { 62 | RonLocalizationBuilder::default() 63 | } 64 | } 65 | 66 | impl Localization for RonLocalization { 67 | fn language(&self) -> &String { 68 | &self.language 69 | } 70 | 71 | fn set_language(&mut self, key: &str) { 72 | self.language = key.to_string(); 73 | } 74 | 75 | fn text(&self, key: String) -> String { 76 | if let Some(dictionary) = self.dictionaries.get(&self.language) { 77 | if let Some(word) = dictionary.words.get(&key) { 78 | return word.clone(); 79 | } 80 | } 81 | 82 | key 83 | } 84 | } 85 | 86 | #[cfg(test)] 87 | mod tests { 88 | use super::*; 89 | 90 | #[test] 91 | fn test_text() { 92 | let de_de = r#" 93 | Dictionary( 94 | words: { 95 | "hello": "Hallo", 96 | "world": "Welt", 97 | } 98 | ) 99 | "#; 100 | 101 | let localization = RonLocalization::create() 102 | .language("de_DE") 103 | .dictionary("de_DE", de_de) 104 | .build(); 105 | 106 | assert_eq!(localization.text("hello".to_string()), "Hallo".to_string()); 107 | assert_eq!(localization.text("world".to_string()), "Welt".to_string()); 108 | assert_eq!(localization.text("test".to_string()), "test".to_string()); 109 | } 110 | } 111 | -------------------------------------------------------------------------------- /orbtk_core/src/prelude.rs: -------------------------------------------------------------------------------- 1 | //! This module pre-selects commonly used OrbTk crates and put them into scope. 2 | 3 | // std 4 | pub use std::rc::Rc; 5 | 6 | // crates modules 7 | pub use crate::application::*; 8 | pub use crate::event::*; 9 | pub use crate::layout::*; 10 | pub use crate::localization::*; 11 | pub use crate::macros::*; 12 | pub use crate::properties::*; 13 | pub use crate::render_object::*; 14 | pub use crate::services::*; 15 | pub use crate::systems::*; 16 | pub use crate::theming::*; 17 | pub use crate::tree::*; 18 | pub use crate::widget_base::*; 19 | 20 | pub use crate::{into_property_source, trigger_event, widget}; 21 | -------------------------------------------------------------------------------- /orbtk_core/src/properties/layout/mod.rs: -------------------------------------------------------------------------------- 1 | // Layout specific properties. 2 | 3 | pub use self::block::*; 4 | pub use self::scroll_viewer_mode::*; 5 | 6 | mod block; 7 | mod scroll_viewer_mode; 8 | -------------------------------------------------------------------------------- /orbtk_core/src/properties/layout/scroll_viewer_mode.rs: -------------------------------------------------------------------------------- 1 | /// The `ScrollMode` defines the mode of a scroll direction. 2 | #[derive(Clone, Copy, Debug, Eq, PartialEq)] 3 | pub enum ScrollMode { 4 | /// Scrolling will process by `ScrollViewer` logic 5 | Auto, 6 | 7 | /// Scrolling could be handled from outside. It will not be 8 | /// process by `ScrollViewer` logic. 9 | Custom, 10 | 11 | /// Scrolling will be disabled. 12 | Disabled, 13 | } 14 | 15 | impl Default for ScrollMode { 16 | fn default() -> Self { 17 | ScrollMode::Auto 18 | } 19 | } 20 | 21 | impl From<&str> for ScrollMode { 22 | fn from(s: &str) -> ScrollMode { 23 | match s { 24 | "Custom" | "custom" => ScrollMode::Custom, 25 | "Disabled" | "disabled" => ScrollMode::Disabled, 26 | _ => ScrollMode::Auto, 27 | } 28 | } 29 | } 30 | 31 | /// `ScrollViewerMode` describes the vertical and horizontal scroll 32 | /// behavior of the `ScrollViewer`. 33 | #[derive(Clone, Copy, Debug, Eq, PartialEq)] 34 | pub struct ScrollViewerMode { 35 | /// Vertical scroll mode. 36 | pub vertical: ScrollMode, 37 | 38 | /// Horizontal scroll mode. 39 | pub horizontal: ScrollMode, 40 | } 41 | 42 | // --- Conversions --- 43 | 44 | impl From<(&str, &str)> for ScrollViewerMode { 45 | fn from(s: (&str, &str)) -> ScrollViewerMode { 46 | ScrollViewerMode { 47 | horizontal: ScrollMode::from(s.0), 48 | vertical: ScrollMode::from(s.1), 49 | } 50 | } 51 | } 52 | 53 | impl Default for ScrollViewerMode { 54 | fn default() -> ScrollViewerMode { 55 | ScrollViewerMode { 56 | vertical: ScrollMode::Auto, 57 | horizontal: ScrollMode::Auto, 58 | } 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /orbtk_core/src/properties/widget/focus_state.rs: -------------------------------------------------------------------------------- 1 | use crate::{theming::Selector, widget_base::Context}; 2 | 3 | use dces::prelude::Entity; 4 | 5 | /// Contains the state information of the current focused element. 6 | /// 7 | /// Provides methods to request and remove focus. 8 | #[derive(Debug, Default, Clone, Eq, PartialEq)] 9 | pub struct FocusState { 10 | focused_entity: Option<Entity>, 11 | } 12 | 13 | impl FocusState { 14 | /// Request focus for the given entity. 15 | pub fn request_focus(&mut self, entity: impl Into<Entity>, ctx: &mut Context) { 16 | let entity = entity.into(); 17 | 18 | if (self.focused_entity.is_some() && self.focused_entity.unwrap() == entity) 19 | || !*ctx.get_widget(entity).get::<bool>("enabled") 20 | { 21 | return; 22 | } 23 | 24 | if let Some(old_focused_element) = self.focused_entity { 25 | let mut old_focused_element = ctx.get_widget(old_focused_element); 26 | 27 | old_focused_element.set("focused", false); 28 | old_focused_element 29 | .get_mut::<Selector>("selector") 30 | .remove_all_similar_states("focused"); 31 | old_focused_element.update(false); 32 | } 33 | 34 | self.focused_entity = Some(entity); 35 | 36 | if ctx.get_widget(entity).has::<bool>("focused") { 37 | let mut focused_element = ctx.get_widget(entity); 38 | 39 | focused_element.set("focused", true); 40 | focused_element 41 | .get_mut::<Selector>("selector") 42 | .push_state("focused"); 43 | focused_element.update(false); 44 | } 45 | } 46 | 47 | /// Remove the focus of the given entity. If the given entity is 48 | /// not the focused entity nothing will happen. 49 | pub fn remove_focus(&mut self, entity: impl Into<Entity>, ctx: &mut Context) { 50 | let entity = entity.into(); 51 | 52 | if let Some(old_focused_element) = self.focused_entity { 53 | if old_focused_element != entity { 54 | return; 55 | } 56 | let mut old_focused_element = ctx.get_widget(old_focused_element); 57 | old_focused_element.set("focused", false); 58 | old_focused_element 59 | .get_mut::<Selector>("selector") 60 | .remove_all_similar_states("focused"); 61 | old_focused_element.update(false); 62 | } 63 | 64 | self.focused_entity = None; 65 | } 66 | 67 | /// Returns `true` if the given entity is focused. 68 | pub fn has_focus(&self, entity: impl Into<Entity>) -> bool { 69 | self.focused_entity.is_some() && self.focused_entity.unwrap() == entity.into() 70 | } 71 | 72 | /// Returns a reference to the current focused entity. 73 | pub fn focused_entity(&self) -> &Option<Entity> { 74 | &self.focused_entity 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /orbtk_core/src/properties/widget/mod.rs: -------------------------------------------------------------------------------- 1 | // Widget related properties. 2 | pub use self::focus_state::*; 3 | pub use self::keyboard_state::*; 4 | pub use self::render_pipeline::*; 5 | pub use self::selected_entities::*; 6 | pub use self::selected_indices::*; 7 | pub use self::text_selection::*; 8 | 9 | mod focus_state; 10 | mod keyboard_state; 11 | mod render_pipeline; 12 | mod selected_entities; 13 | mod selected_indices; 14 | mod text_selection; 15 | -------------------------------------------------------------------------------- /orbtk_core/src/properties/widget/render_pipeline.rs: -------------------------------------------------------------------------------- 1 | use std::any::Any; 2 | 3 | use crate::render; 4 | 5 | #[derive(Clone, PartialEq)] 6 | struct EmptyRenderPipeline; 7 | 8 | impl render::PipelineTrait for EmptyRenderPipeline { 9 | fn box_eq(&self, other: &dyn Any) -> bool { 10 | other.downcast_ref::<Self>().map_or(false, |a| self == a) 11 | } 12 | fn as_any(&self) -> &dyn Any { 13 | self 14 | } 15 | 16 | fn clone_box(&self) -> Box<dyn render::PipelineTrait> { 17 | Box::new(self.clone()) 18 | } 19 | } 20 | 21 | impl render::RenderPipeline for EmptyRenderPipeline { 22 | fn draw(&self, _: &mut render::RenderTarget) {} 23 | } 24 | 25 | /// RenderPipeline object. 26 | #[derive(Clone, Debug)] 27 | pub struct DefaultRenderPipeline(pub Box<dyn render::PipelineTrait>); 28 | 29 | impl PartialEq for DefaultRenderPipeline { 30 | fn eq(&self, _other: &Self) -> bool { 31 | // todo this is workaround for property checking 32 | false 33 | } 34 | } 35 | 36 | impl Default for DefaultRenderPipeline { 37 | fn default() -> Self { 38 | DefaultRenderPipeline(Box::new(EmptyRenderPipeline)) 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /orbtk_core/src/properties/widget/selected_entities.rs: -------------------------------------------------------------------------------- 1 | use std::collections::HashSet; 2 | 3 | use dces::prelude::*; 4 | 5 | /// `SelectedEntities` describes a list of selected entities. 6 | #[derive(Debug, Default, Clone, Eq, PartialEq)] 7 | pub struct SelectedEntities(pub HashSet<Entity>); 8 | 9 | impl From<HashSet<Entity>> for SelectedEntities { 10 | fn from(i: HashSet<Entity>) -> Self { 11 | SelectedEntities(i) 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /orbtk_core/src/properties/widget/selected_indices.rs: -------------------------------------------------------------------------------- 1 | use std::collections::HashSet; 2 | 3 | /// `SelectedIndices` describes a list of selected indices. 4 | #[derive(Debug, Default, Clone, Eq, PartialEq)] 5 | pub struct SelectedIndices(pub HashSet<usize>); 6 | 7 | impl From<HashSet<usize>> for SelectedIndices { 8 | fn from(i: HashSet<usize>) -> Self { 9 | SelectedIndices(i) 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /orbtk_core/src/properties/widget/text_selection.rs: -------------------------------------------------------------------------------- 1 | /// Mark the selection inside a text object. 2 | #[derive(Debug, Default, Copy, Clone, Eq, PartialEq)] 3 | pub struct TextSelection { 4 | start: usize, 5 | end: usize, 6 | } 7 | 8 | impl TextSelection { 9 | /// Creates a new text selection with an given start and end. 10 | pub fn new(start: usize, end: usize) -> Self { 11 | TextSelection { start, end } 12 | } 13 | 14 | /// Gets the start index. 15 | pub fn start(&self) -> usize { 16 | self.start 17 | } 18 | 19 | /// Sets the start index. 20 | pub fn set_start(&mut self, start: usize) { 21 | self.start = start; 22 | } 23 | 24 | /// Gets the end index. 25 | pub fn end(&self) -> usize { 26 | self.end 27 | } 28 | 29 | /// Sets the end index. 30 | pub fn set_end(&mut self, end: usize) { 31 | self.end = end; 32 | } 33 | 34 | /// Sets start and end to the same value. 35 | pub fn set(&mut self, value: usize) { 36 | self.start = value; 37 | self.end = value; 38 | } 39 | 40 | /// Gets the length of the selection. 41 | pub fn len(&self) -> usize { 42 | (self.start as i32 - self.end as i32).unsigned_abs() as usize 43 | } 44 | 45 | /// Check if the selection is empty (start == end). 46 | pub fn is_empty(&self) -> bool { 47 | self.len() == 0 48 | } 49 | } 50 | 51 | impl From<(usize, usize)> for TextSelection { 52 | fn from(t: (usize, usize)) -> Self { 53 | TextSelection::new(t.0, t.1) 54 | } 55 | } 56 | 57 | #[cfg(test)] 58 | mod tests { 59 | use super::*; 60 | 61 | #[test] 62 | fn test_into() { 63 | let offset: TextSelection = (14, 16).into(); 64 | assert_eq!(offset.start(), 14); 65 | assert_eq!(offset.end(), 16); 66 | } 67 | 68 | #[test] 69 | fn test_len() { 70 | let offset: TextSelection = (14, 16).into(); 71 | assert_eq!(offset.len(), 2); 72 | 73 | let offset: TextSelection = (16, 14).into(); 74 | assert_eq!(offset.len(), 2); 75 | } 76 | 77 | #[test] 78 | fn test_set() { 79 | let mut offset: TextSelection = (14, 16).into(); 80 | offset.set(5); 81 | 82 | assert_eq!(offset.start(), 5); 83 | assert_eq!(offset.end(), 5); 84 | } 85 | } 86 | -------------------------------------------------------------------------------- /orbtk_core/src/render_object/cursor.rs: -------------------------------------------------------------------------------- 1 | use crate::{ 2 | proc_macros::IntoRenderObject, 3 | render_object::*, 4 | utils::{Brush, Point, Rectangle, Thickness}, 5 | }; 6 | 7 | /// The `CursorRenderObject` is used to render the `Cursor` widget. 8 | /// 9 | /// [`Cursor`]: ../../widgets/struct.Cursor.html 10 | #[derive(Debug, IntoRenderObject)] 11 | pub struct CursorRenderObject; 12 | 13 | impl RenderObject for CursorRenderObject { 14 | fn render_self(&self, ctx: &mut Context, global_position: &Point) { 15 | let ( 16 | bounds, 17 | background, 18 | border_width, 19 | border_brush, 20 | background_opacity, 21 | cursor_x, 22 | selection_width, 23 | selection_x, 24 | offset, 25 | ) = { 26 | let widget = ctx.widget(); 27 | ( 28 | *widget.get::<Rectangle>("bounds"), 29 | widget.get::<Brush>("background").clone(), 30 | *widget.get::<Thickness>("border_width"), 31 | widget.clone_or_default::<Brush>("border_brush"), 32 | *widget.get::<f32>("background_opacity"), 33 | *widget.get::<f64>("cursor_x"), 34 | *widget.get::<f64>("selection_width"), 35 | *widget.get::<f64>("selection_x"), 36 | *widget.get::<f64>("offset"), 37 | ) 38 | }; 39 | 40 | let border_width = border_width.right(); 41 | 42 | // background 43 | ctx.render_context_2_d().set_alpha(background_opacity); 44 | ctx.render_context_2_d().set_fill_style(background); 45 | ctx.render_context_2_d().fill_rect( 46 | global_position.x() + bounds.x() + offset + selection_x - border_width / 2., 47 | global_position.y() + bounds.y(), 48 | selection_width, 49 | bounds.height(), 50 | ); 51 | ctx.render_context_2_d().set_alpha(1.); 52 | 53 | // border 54 | ctx.render_context_2_d().set_fill_style(border_brush); 55 | ctx.render_context_2_d().fill_rect( 56 | global_position.x() + bounds.x() + offset + cursor_x - border_width / 2., 57 | global_position.y() + bounds.y(), 58 | border_width, 59 | bounds.height(), 60 | ); 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /orbtk_core/src/render_object/default.rs: -------------------------------------------------------------------------------- 1 | use crate::{proc_macros::IntoRenderObject, render_object::*}; 2 | 3 | /// The `DefaultRenderObject` holds default objects inside 4 | /// a render object. 5 | #[derive(Debug, IntoRenderObject)] 6 | pub struct DefaultRenderObject; 7 | 8 | impl RenderObject for DefaultRenderObject {} 9 | -------------------------------------------------------------------------------- /orbtk_core/src/render_object/font_icon.rs: -------------------------------------------------------------------------------- 1 | use crate::{ 2 | proc_macros::IntoRenderObject, 3 | render_object::*, 4 | utils::{Brush, Point, Rectangle}, 5 | }; 6 | 7 | /// The `FontIconRenderObject` holds the font icons inside 8 | /// a render object. 9 | #[derive(Debug, IntoRenderObject)] 10 | pub struct FontIconRenderObject; 11 | 12 | impl RenderObject for FontIconRenderObject { 13 | fn render_self(&self, ctx: &mut Context, global_position: &Point) { 14 | let (bounds, icon, icon_brush, icon_font, icon_size) = { 15 | let widget = ctx.widget(); 16 | ( 17 | *widget.get::<Rectangle>("bounds"), 18 | widget.clone::<String>("icon"), 19 | widget.get::<Brush>("icon_brush").clone(), 20 | widget.get::<String>("icon_font").clone(), 21 | *widget.get::<f64>("icon_size"), 22 | ) 23 | }; 24 | 25 | if bounds.width() == 0.0 26 | || bounds.height() == 0.0 27 | || icon_brush.is_transparent() 28 | || icon_size == 0.0 29 | || icon.is_empty() 30 | { 31 | return; 32 | } 33 | 34 | if !icon.is_empty() { 35 | ctx.render_context_2_d().begin_path(); 36 | ctx.render_context_2_d().set_font_family(icon_font); 37 | ctx.render_context_2_d().set_font_size(icon_size); 38 | ctx.render_context_2_d().set_fill_style(icon_brush); 39 | 40 | ctx.render_context_2_d().fill_text( 41 | &icon, 42 | global_position.x() + bounds.x(), 43 | global_position.y() + bounds.y(), 44 | ); 45 | ctx.render_context_2_d().close_path(); 46 | } 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /orbtk_core/src/render_object/image.rs: -------------------------------------------------------------------------------- 1 | use crate::{proc_macros::IntoRenderObject, render::Image, render_object::*}; 2 | 3 | /// Used to render an image. 4 | #[derive(Debug, IntoRenderObject)] 5 | pub struct ImageRenderObject; 6 | 7 | impl RenderObject for ImageRenderObject { 8 | fn render_self(&self, ctx: &mut Context, global_position: &Point) { 9 | let (bounds, mut image) = { 10 | let widget = ctx.widget(); 11 | ( 12 | widget.clone::<Rectangle>("bounds"), 13 | widget.try_clone::<Image>("image"), 14 | ) 15 | }; 16 | 17 | if let Some(image) = &mut image { 18 | ctx.render_context_2_d().draw_image( 19 | image, 20 | bounds.x() + global_position.x(), 21 | bounds.y() + global_position.y(), 22 | ); 23 | } 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /orbtk_core/src/render_object/pipeline.rs: -------------------------------------------------------------------------------- 1 | use crate::{proc_macros::IntoRenderObject, render_object::*}; 2 | 3 | /// Structure that defines a pipeline for a render object. 4 | #[derive(Debug, IntoRenderObject)] 5 | pub struct PipelineRenderObject; 6 | 7 | impl RenderObject for PipelineRenderObject { 8 | fn render_self(&self, ctx: &mut Context, _: &Point) { 9 | let bounds = *ctx.widget().get::<Rectangle>("bounds"); 10 | let pipeline = ctx 11 | .widget() 12 | .get::<DefaultRenderPipeline>("render_pipeline") 13 | .0 14 | .clone(); 15 | 16 | ctx.render_context_2_d().draw_pipeline( 17 | bounds.x(), 18 | bounds.y(), 19 | bounds.width(), 20 | bounds.height(), 21 | pipeline, 22 | ); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /orbtk_core/src/render_object/text.rs: -------------------------------------------------------------------------------- 1 | use crate::{ 2 | proc_macros::IntoRenderObject, 3 | render_object::*, 4 | utils::{Brush, Point, Rectangle}, 5 | }; 6 | use memchr::memchr_iter; 7 | use std::iter; 8 | 9 | /// Used to render a text. 10 | #[derive(Debug, IntoRenderObject)] 11 | pub struct TextRenderObject; 12 | 13 | impl RenderObject for TextRenderObject { 14 | fn render_self(&self, ctx: &mut Context, global_position: &Point) { 15 | let (bounds, text, foreground, font, font_size, offset) = { 16 | let widget = ctx.widget(); 17 | let text = text(&widget); 18 | let offset = *widget.get::<f64>("offset"); 19 | 20 | let txt = { 21 | if !text.is_empty() { 22 | text 23 | } else { 24 | widget.clone_or_default::<String>("water_mark") 25 | } 26 | }; 27 | ( 28 | *widget.get::<Rectangle>("bounds"), 29 | txt, 30 | widget.get::<Brush>("foreground").clone(), 31 | widget.get::<String>("font").clone(), 32 | *widget.get::<f64>("font_size"), 33 | offset, 34 | ) 35 | }; 36 | 37 | if bounds.width() == 0.0 38 | || bounds.height() == 0.0 39 | || foreground.is_transparent() 40 | || font_size == 0.0 41 | || text.is_empty() 42 | { 43 | return; 44 | } 45 | if text.is_empty() { 46 | return; 47 | } 48 | 49 | ctx.render_context_2_d().begin_path(); 50 | ctx.render_context_2_d().set_font_family(font); 51 | ctx.render_context_2_d().set_font_size(font_size); 52 | ctx.render_context_2_d().set_fill_style(foreground); 53 | 54 | let mut y_disp = 0.0; 55 | let mut last_ofs = 0; 56 | for i in memchr_iter(b'\n', text.as_bytes()).chain(iter::once(text.len())) { 57 | ctx.render_context_2_d().fill_text( 58 | &text[last_ofs..i], 59 | global_position.x() + bounds.x() + offset, 60 | global_position.y() + bounds.y() + y_disp, 61 | ); 62 | y_disp += font_size * 1.15; // TODO: Make the space between lines customizable 63 | last_ofs = i + 1; // + 1 to skip the end of line character 64 | } 65 | 66 | ctx.render_context_2_d().close_path(); 67 | } 68 | } 69 | 70 | fn text(widget: &WidgetContainer) -> String { 71 | if let Some(localizable) = widget.try_get::<bool>("localizable") { 72 | if *localizable { 73 | if let Some(localized_text) = widget.try_get::<String>("localized_text") { 74 | if !localized_text.is_empty() { 75 | return localized_text.clone(); 76 | } 77 | } 78 | } 79 | } 80 | 81 | if let Some(text) = widget.try_get::<String>("text") { 82 | return text.clone(); 83 | } 84 | 85 | String::default() 86 | } 87 | -------------------------------------------------------------------------------- /orbtk_core/src/services/clipboard.rs: -------------------------------------------------------------------------------- 1 | /// Clipboard leads you read and store a value. 2 | /// 3 | /// To access the value of systems clipboard it must be used in 4 | /// combination with a window shell. 5 | /// 6 | /// # Examples 7 | /// ``` 8 | /// impl State for MyState { 9 | /// fn update(&mut self, registry: &mut Registry, _: &mut Context) { 10 | /// let mut clipboard = registry.get_mut;:<Clipboard>("clipboard"); 11 | /// println!("{:?}", clipboard.get()); 12 | /// clipboard.set("paste"); 13 | /// } 14 | /// } 15 | /// ``` 16 | #[derive(Clone, Default, Debug)] 17 | pub struct Clipboard { 18 | value: Option<String>, 19 | } 20 | 21 | impl Clipboard { 22 | /// Creates a new clipboard with default values. 23 | pub fn new() -> Self { 24 | Clipboard::default() 25 | } 26 | 27 | /// Return the latest value of the clipboard. 28 | /// If there is no value present on the clipboard it will return `None`. 29 | pub fn get(&self) -> Option<String> { 30 | self.value.clone() 31 | } 32 | 33 | /// Sets the value of the clipboard. 34 | pub fn set(&mut self, value: impl Into<String>) { 35 | self.value = Some(value.into()); 36 | } 37 | } 38 | 39 | #[cfg(test)] 40 | mod tests { 41 | use super::*; 42 | 43 | #[test] 44 | /// A quick test to ensure that the items are properly set. 45 | fn get_set() { 46 | let mut clipboard = Clipboard::new(); 47 | let test = String::from("test"); 48 | clipboard.set(test.clone()); 49 | assert_eq!(test, clipboard.get().unwrap()); 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /orbtk_core/src/services/mod.rs: -------------------------------------------------------------------------------- 1 | //! This module contains global services. 2 | //! 3 | pub use self::clipboard::*; 4 | pub use self::settings::*; 5 | 6 | mod clipboard; 7 | mod settings; 8 | -------------------------------------------------------------------------------- /orbtk_core/src/systems/cleanup_system.rs: -------------------------------------------------------------------------------- 1 | use std::{cell::RefCell, rc::Rc}; 2 | 3 | use dces::prelude::*; 4 | 5 | use crate::{prelude::*, render::RenderContext2D, theming::Theme, tree::Tree}; 6 | 7 | /// Handles the inner cleanup while window is closing. 8 | #[derive(Constructor)] 9 | pub struct CleanupSystem { 10 | context_provider: ContextProvider, 11 | registry: Rc<RefCell<Registry>>, 12 | } 13 | 14 | impl System<Tree, RenderContext2D> for CleanupSystem { 15 | fn run_with_context( 16 | &self, 17 | ecm: &mut EntityComponentManager<Tree>, 18 | render_context: &mut RenderContext2D, 19 | ) { 20 | // let mut shell = self.shell.borrow_mut(); 21 | let root = ecm.entity_store().root(); 22 | let theme = ecm 23 | .component_store() 24 | .get::<Rc<Theme>>("theme", root) 25 | .unwrap() 26 | .clone(); 27 | 28 | let mut dirty_index = 0; 29 | 30 | loop { 31 | if dirty_index 32 | >= ecm 33 | .component_store() 34 | .get::<Vec<Entity>>("dirty_widgets", root) 35 | .unwrap() 36 | .len() 37 | { 38 | break; 39 | } 40 | 41 | let skip = false; 42 | 43 | let widget = *ecm 44 | .component_store() 45 | .get::<Vec<Entity>>("dirty_widgets", root) 46 | .unwrap() 47 | .get(dirty_index) 48 | .unwrap(); 49 | 50 | let mut keys = vec![]; 51 | 52 | if !skip { 53 | let registry = &mut self.registry.borrow_mut(); 54 | 55 | let mut ctx = Context::new( 56 | (widget, ecm), 57 | &theme, 58 | &self.context_provider, 59 | render_context, 60 | ); 61 | 62 | if let Some(state) = self.context_provider.states.borrow_mut().get_mut(&widget) { 63 | state.cleanup(registry, &mut ctx); 64 | } 65 | 66 | keys.append(&mut ctx.new_states_keys()); 67 | } 68 | 69 | dirty_index += 1; 70 | } 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /orbtk_core/src/systems/init_system.rs: -------------------------------------------------------------------------------- 1 | use std::{cell::RefCell, rc::Rc}; 2 | 3 | use dces::prelude::*; 4 | 5 | use crate::{prelude::*, render::RenderContext2D, theming::Selector, tree::Tree}; 6 | 7 | /// This system is used to initializes the widgets. 8 | #[derive(Constructor)] 9 | pub struct InitSystem { 10 | context_provider: ContextProvider, 11 | registry: Rc<RefCell<Registry>>, 12 | } 13 | 14 | impl System<Tree, RenderContext2D> for InitSystem { 15 | fn run_with_context( 16 | &self, 17 | ecm: &mut EntityComponentManager<Tree>, 18 | render_context: &mut RenderContext2D, 19 | ) { 20 | let root = ecm.entity_store().root(); 21 | 22 | #[cfg(feature = "debug")] 23 | let debug = true; 24 | #[cfg(not(feature = "debug"))] 25 | let debug = false; 26 | 27 | if debug { 28 | crate::shell::CONSOLE.log("\n------ Widget tree ------\n".to_string()); 29 | 30 | print_tree(root, 0, ecm); 31 | 32 | crate::shell::CONSOLE.log("\n------ Widget tree ------\n".to_string()); 33 | } 34 | 35 | // init css ids 36 | let theme = ecm 37 | .component_store() 38 | .get::<Rc<Theme>>("theme", root) 39 | .unwrap() 40 | .clone(); 41 | 42 | let mut current_node = root; 43 | 44 | loop { 45 | { 46 | let mut ctx = Context::new( 47 | (current_node, ecm), 48 | &theme, 49 | &self.context_provider, 50 | render_context, 51 | ); 52 | 53 | if let Some(state) = self 54 | .context_provider 55 | .states 56 | .borrow_mut() 57 | .get_mut(¤t_node) 58 | { 59 | state.init(&mut *self.registry.borrow_mut(), &mut ctx); 60 | } 61 | 62 | drop(ctx); 63 | } 64 | 65 | let mut it = ecm.entity_store().start_node(current_node).into_iter(); 66 | it.next(); 67 | 68 | if let Some(node) = it.next() { 69 | current_node = node; 70 | } else { 71 | break; 72 | } 73 | } 74 | } 75 | } 76 | 77 | pub fn print_tree(entity: Entity, depth: usize, ecm: &mut EntityComponentManager<Tree>) { 78 | let name = ecm.component_store().get::<String>("name", entity).unwrap(); 79 | 80 | let selector = if let Ok(selector) = ecm.component_store().get::<Selector>("selector", entity) { 81 | selector.clone() 82 | } else { 83 | Selector::default() 84 | }; 85 | 86 | crate::shell::CONSOLE.log(format!( 87 | "{}{} (entity: {}, {})", 88 | "| ".repeat(depth), 89 | name, 90 | entity.0, 91 | selector 92 | )); 93 | 94 | for child in ecm.entity_store().clone().children.get(&entity).unwrap() { 95 | print_tree(*child, depth + 1, ecm); 96 | } 97 | } 98 | -------------------------------------------------------------------------------- /orbtk_core/src/systems/layout_system.rs: -------------------------------------------------------------------------------- 1 | use dces::prelude::*; 2 | 3 | use crate::{prelude::*, render::RenderContext2D, tree::Tree, utils::*}; 4 | 5 | /// The `LayoutSystem` takes care to rebuild the layout of the current 6 | /// `UI` on a per iteration cycle. `layout widgets` are organized in 7 | /// layout objects, that will in term calculate the resulting 8 | /// positions of it objects inside the view. 9 | #[derive(Constructor)] 10 | pub struct LayoutSystem { 11 | context_provider: ContextProvider, 12 | } 13 | 14 | impl System<Tree, RenderContext2D> for LayoutSystem { 15 | fn run_with_context( 16 | &self, 17 | ecm: &mut EntityComponentManager<Tree>, 18 | render_context: &mut RenderContext2D, 19 | ) { 20 | let root = ecm.entity_store().root(); 21 | 22 | if ecm 23 | .component_store() 24 | .get::<Vec<Entity>>("dirty_widgets", root) 25 | .unwrap() 26 | .is_empty() 27 | && !self.context_provider.first_run.get() 28 | { 29 | return; 30 | } 31 | 32 | let mut window_size = (0.0, 0.0); 33 | let root = ecm.entity_store().root(); 34 | 35 | if let Ok(bounds) = ecm.component_store().get::<Rectangle>("bounds", root) { 36 | window_size.0 = bounds.width(); 37 | window_size.1 = bounds.height(); 38 | }; 39 | 40 | let theme = ecm 41 | .component_store() 42 | .get::<Rc<Theme>>("theme", root) 43 | .unwrap() 44 | .clone(); 45 | 46 | self.context_provider.layouts.borrow()[&root].measure( 47 | render_context, 48 | root, 49 | ecm, 50 | &self.context_provider.layouts.borrow(), 51 | &theme, 52 | ); 53 | 54 | self.context_provider.layouts.borrow()[&root].arrange( 55 | render_context, 56 | window_size, 57 | root, 58 | ecm, 59 | &self.context_provider.layouts.borrow(), 60 | &theme, 61 | ); 62 | 63 | // if self.debug_flag.get() { 64 | // println!("\n------ End layout update ------\n"); 65 | // } 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /orbtk_core/src/systems/mod.rs: -------------------------------------------------------------------------------- 1 | //! Provides dedicated `system` pipelines inside OrbTk. 2 | //! 3 | //! System pipelines are modules, that handle specific tasks when 4 | //! iteratively walking the widget tree. Because each widget 5 | //! implements the `state` trait, all system modules are accessible. 6 | //! Pipelines are connected in a logical order. E.g. the `InitSystem` 7 | //! is triggered **before** the `LayoutSystem`. The `LayoutSystem` is 8 | //! triggerd **before** the `RenderSystem`. Handling of widget objects 9 | //! inside the pipelines rely on the Entity Component System 10 | //! ([`DCES`]). 11 | //! 12 | //! [`DCES`]: https://gitlab.redox-os.org/redox-os/dces-rust 13 | 14 | pub use self::cleanup_system::*; 15 | pub use self::event_state_system::*; 16 | pub use self::init_system::*; 17 | pub use self::layout_system::*; 18 | pub use self::post_layout_state_system::*; 19 | pub use self::render_system::*; 20 | 21 | mod cleanup_system; 22 | mod event_state_system; 23 | mod init_system; 24 | mod layout_system; 25 | mod post_layout_state_system; 26 | mod render_system; 27 | -------------------------------------------------------------------------------- /orbtk_core/src/systems/render_system.rs: -------------------------------------------------------------------------------- 1 | use std::collections::BTreeMap; 2 | 3 | use dces::prelude::*; 4 | 5 | use crate::{prelude::*, render::RenderContext2D, tree::Tree}; 6 | 7 | /// The `RenderSystem` iterates over all visual widgets. 8 | /// 9 | /// For any widgets that have been marked dirty, new bounds have to be 10 | /// recalculated. The resulting tree is rendered to the render buffer 11 | /// which is then drawn to the screen. 12 | #[derive(Constructor)] 13 | pub struct RenderSystem { 14 | context_provider: ContextProvider, 15 | } 16 | 17 | impl System<Tree, RenderContext2D> for RenderSystem { 18 | fn run_with_context( 19 | &self, 20 | ecm: &mut EntityComponentManager<Tree>, 21 | render_context: &mut RenderContext2D, 22 | ) { 23 | let root = ecm.entity_store().root(); 24 | 25 | let dirty_widgets = ecm 26 | .component_store() 27 | .get::<Vec<Entity>>("dirty_widgets", root) 28 | .unwrap() 29 | .clone(); 30 | 31 | // Only process, if 32 | // * there are `dirty` elements inside the entity vector 33 | // * context_provider it marked for first_run. 34 | if dirty_widgets.is_empty() && !self.context_provider.first_run.get() { 35 | return; 36 | } 37 | 38 | // reset the dirty flag of all dirty widgets to `false` 39 | for widget in dirty_widgets { 40 | if let Ok(dirty) = ecm.component_store_mut().get_mut::<bool>("dirty", widget) { 41 | *dirty = false; 42 | } 43 | } 44 | 45 | ecm.component_store_mut() 46 | .get_mut::<Vec<Entity>>("dirty_widgets", root) 47 | .unwrap() 48 | .clear(); 49 | 50 | #[cfg(feature = "debug")] 51 | let debug = true; 52 | #[cfg(not(feature = "debug"))] 53 | let debug = false; 54 | 55 | let root = ecm.entity_store().root(); 56 | let theme = ecm 57 | .component_store() 58 | .get::<Rc<Theme>>("theme", root) 59 | .unwrap() 60 | .clone(); 61 | 62 | let mut offsets = BTreeMap::new(); 63 | offsets.insert(root, (0.0, 0.0)); 64 | 65 | // CONSOLE.time("render"); 66 | 67 | render_context.start(); 68 | render_context.begin_path(); 69 | self.context_provider.render_objects.borrow()[&root].render( 70 | render_context, 71 | root, 72 | ecm, 73 | &self.context_provider, 74 | &theme, 75 | &mut offsets, 76 | debug, 77 | ); 78 | render_context.finish(); 79 | 80 | if self.context_provider.first_run.get() { 81 | self.context_provider.first_run.set(false); 82 | } 83 | } 84 | } 85 | -------------------------------------------------------------------------------- /orbtk_core/src/theming/config/mod.rs: -------------------------------------------------------------------------------- 1 | pub use self::style_config::*; 2 | pub use self::theme_config::*; 3 | 4 | mod style_config; 5 | mod theme_config; 6 | -------------------------------------------------------------------------------- /orbtk_core/src/theming/config/style_config.rs: -------------------------------------------------------------------------------- 1 | use std::collections::HashMap; 2 | 3 | use ron::Value; 4 | use serde_derive::{Deserialize, Serialize}; 5 | 6 | use crate::theming::ThemeState; 7 | 8 | /// Defines a style. A style could be base on other styles and 9 | /// contains a list for properties and a list of state properties. 10 | #[derive(Default, Clone, Debug, Serialize, Deserialize)] 11 | pub struct StyleConfig { 12 | // set default string to base style 13 | #[serde(default)] 14 | pub base: String, 15 | #[serde(default)] 16 | pub states: Vec<ThemeState>, 17 | #[serde(default)] 18 | pub properties: HashMap<String, Value>, 19 | } 20 | -------------------------------------------------------------------------------- /orbtk_core/src/theming/config/theme_config.rs: -------------------------------------------------------------------------------- 1 | use std::collections::HashMap; 2 | 3 | use ron::{de::from_str, Value}; 4 | use serde_derive::{Deserialize, Serialize}; 5 | 6 | use crate::theming::config::StyleConfig; 7 | 8 | pub static BASE_STYLE: &str = "base"; 9 | pub static RESOURCE_KEY: &str = "quot;; 10 | 11 | /// Used to store and read properties that could be requested by a 12 | /// given property name and a selector. 13 | #[derive(Default, Clone, Debug, Serialize, Deserialize)] 14 | #[serde(rename = "Theme")] 15 | pub struct ThemeConfig { 16 | #[serde(default)] 17 | pub styles: HashMap<String, StyleConfig>, 18 | #[serde(default)] 19 | pub resources: HashMap<String, Value>, 20 | } 21 | 22 | impl ThemeConfig { 23 | /// Extends the given theme with another theme. Replaces the 24 | /// current name with the new choosen name `other`. If `other` 25 | /// contains a style with the same key entry, this key will be 26 | /// replaced in the current theme. 27 | pub fn extend(mut self, other: ThemeConfig) -> Self { 28 | let mut other = other; 29 | 30 | for style in other.styles.drain() { 31 | self.styles.insert(style.0, style.1); 32 | } 33 | 34 | for resource in other.resources.drain() { 35 | self.resources.insert(resource.0, resource.1); 36 | } 37 | 38 | self 39 | } 40 | } 41 | 42 | impl From<&str> for ThemeConfig { 43 | fn from(s: &str) -> Self { 44 | from_str(s).unwrap() 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /orbtk_core/src/theming/mod.rs: -------------------------------------------------------------------------------- 1 | //! This module contains the theming methods, that handle runtime based rendering of OrbTk entities. 2 | 3 | pub use self::config::*; 4 | pub use self::selector::*; 5 | pub use self::style::*; 6 | pub use self::theme::*; 7 | pub use self::theme_state::*; 8 | 9 | mod config; 10 | mod selector; 11 | mod style; 12 | mod theme; 13 | mod theme_state; 14 | -------------------------------------------------------------------------------- /orbtk_core/src/theming/selector.rs: -------------------------------------------------------------------------------- 1 | use std::fmt; 2 | 3 | /// The selector is used to read a property value from the `Theme`. 4 | #[derive(Debug, Clone, Default, Eq, PartialEq)] 5 | pub struct Selector { 6 | /// Represents the key of a style. 7 | pub style: Option<String>, 8 | 9 | // Used to reference the state property list of the given 10 | // style. The state on the top of the vector is the active one. 11 | states: Vec<String>, 12 | 13 | /// Check if the selector is dirty. 14 | dirty: bool, 15 | } 16 | 17 | impl Selector { 18 | /// Creates a new selector with the given style key. 19 | pub fn new(style: impl Into<String>) -> Self { 20 | Selector { 21 | style: Some(style.into()), 22 | states: vec![], 23 | dirty: true, 24 | } 25 | } 26 | 27 | /// Returns a reference to list of active states. 28 | pub fn states(&self) -> &Vec<String> { 29 | &self.states 30 | } 31 | 32 | /// Pushes a state to the states vector. 33 | pub fn push_state(&mut self, state: impl Into<String>) { 34 | let state = state.into(); 35 | 36 | if self.states.contains(&state) { 37 | return; 38 | } 39 | 40 | self.states.push(state); 41 | self.dirty = true; 42 | } 43 | 44 | /// Removes all states with a similar name compared to the given pattern. 45 | pub fn remove_all_similar_states(&mut self, pattern: &str) { 46 | while let Some(pos) = self.states.iter().position(|x| x.contains(pattern)) { 47 | self.states.remove(pos); 48 | self.dirty = true; 49 | } 50 | } 51 | 52 | /// Removes all instances of the give state from the vector and returns it. 53 | pub fn remove_state(&mut self, state: impl Into<String>) -> Option<String> { 54 | let state: String = state.into(); 55 | 56 | if !state.is_empty() && self.states.contains(&state) { 57 | while let Some(pos) = self.states.iter().position(|x| *x == state) { 58 | self.states.remove(pos); 59 | } 60 | 61 | self.dirty = true; 62 | return Some(state); 63 | } 64 | 65 | None 66 | } 67 | 68 | /// Removes the last state from a vector and returns it, or None 69 | /// if it is empty. If there is a new last vector it will be the 70 | /// new active state. 71 | pub fn pop_state(&mut self) -> Option<String> { 72 | self.dirty = true; 73 | self.states.pop() 74 | } 75 | 76 | /// Gets the dirty flag. 77 | pub fn dirty(&self) -> bool { 78 | self.dirty 79 | } 80 | 81 | /// Sets the dirty flag. 82 | pub fn set_dirty(&mut self, dirty: bool) { 83 | self.dirty = dirty; 84 | } 85 | 86 | /// Check if the selector has the given state. 87 | pub fn has_state(&self, state: &str) -> bool { 88 | self.states.contains(&state.to_string()) 89 | } 90 | } 91 | 92 | impl fmt::Display for Selector { 93 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 94 | if let Some(style) = &self.style { 95 | return write!(f, "Selector ( style: {} )", style); 96 | } 97 | write!(f, "Selector ( empty )") 98 | } 99 | } 100 | 101 | impl From<&str> for Selector { 102 | fn from(s: &str) -> Self { 103 | Selector::new(s) 104 | } 105 | } 106 | 107 | impl From<String> for Selector { 108 | fn from(s: String) -> Self { 109 | Selector::new(s) 110 | } 111 | } 112 | -------------------------------------------------------------------------------- /orbtk_core/src/theming/style.rs: -------------------------------------------------------------------------------- 1 | use std::collections::HashMap; 2 | 3 | use super::ThemeState; 4 | 5 | use ron::Value; 6 | 7 | /// A style is used internaly by `Theme`. It contains a map of default 8 | /// properties beside a list of states. 9 | #[derive(Debug, Default, Clone, Eq, PartialEq)] 10 | pub struct Style { 11 | /// Represents the map of default properties. 12 | pub properties: HashMap<String, Value>, 13 | 14 | /// Represents the list of states. 15 | pub states: Vec<ThemeState>, 16 | } 17 | 18 | impl Style { 19 | /// Creates a new style. 20 | pub fn new() -> Self { 21 | Style::default() 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /orbtk_core/src/theming/theme_state.rs: -------------------------------------------------------------------------------- 1 | use std::collections::HashMap; 2 | 3 | use serde_derive::{Deserialize, Serialize}; 4 | 5 | use ron::Value; 6 | 7 | /// Contains a list of properties corresponding to the state key. 8 | #[derive(Clone, Debug, Default, Deserialize, Eq, PartialEq, Serialize)] 9 | pub struct ThemeState { 10 | #[serde(default)] 11 | pub key: String, 12 | #[serde(default)] 13 | pub properties: HashMap<String, Value>, 14 | } 15 | 16 | impl ThemeState { 17 | /// Creates a new state. 18 | pub fn new(key: String) -> Self { 19 | ThemeState { 20 | key, 21 | ..Default::default() 22 | } 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /orbtk_core/src/widget_base/template.rs: -------------------------------------------------------------------------------- 1 | use dces::prelude::Entity; 2 | 3 | use crate::{layout::*, render_object::*}; 4 | 5 | use super::BuildContext; 6 | 7 | /// The `Template` trait is used to implement a particular widget type. 8 | /// 9 | /// When a widget's `Template` is implemented, it provides three object types: 10 | /// * a template objet (with default values for its properties, children, handlers) 11 | /// * a render object 12 | /// * a layout object 13 | pub trait Template: Sized { 14 | /// Builds the template of the widget and returns it. 15 | /// 16 | /// # Arguments 17 | /// * `_id`: The id (Entity) of the instantiated widget in the Entity Store 18 | /// * `_context`: The BuildContext used to build and instantiate new widgets 19 | /// 20 | /// # Example 21 | /// 22 | /// Define a widget called MyWidget with min, max and val 23 | /// properties. The properties have a type of `usize` and we do 24 | /// preset suitable default values. The definition is completed 25 | /// with a TextBlock widget, that is the only child. 26 | /// 27 | /// ```rust 28 | /// widget!(MyWidget { 29 | /// min: usize, 30 | /// max: usize, 31 | /// val: usize 32 | /// }); 33 | /// 34 | /// impl Template for MyWidget { 35 | /// fn template(self, _id: Entity, context: &mut BuildContext) -> Self { 36 | /// self.name("MyWidget") 37 | /// .min(100) 38 | /// .max(1000) 39 | /// .val(500) 40 | /// .child(TextBlock::new().text("Set a value!").build(context)) 41 | /// } 42 | /// 43 | /// fn render_object(&self) -> Box<dyn RenderObject> { 44 | /// Box::new(RectangleRenderObject) 45 | /// } 46 | /// 47 | /// fn layout(&self) -> Box<dyn Layout> { 48 | /// Box::new(AbsoluteLayout) 49 | /// } 50 | /// } 51 | /// ``` 52 | fn template(self, _id: Entity, _ctx: &mut BuildContext) -> Self { 53 | self 54 | } 55 | 56 | /// Returns a pointer to a heap allocated object. 57 | /// 58 | /// Widgets will be rendered as pixmaps inside the render buffer. 59 | /// For the list of available render objects, see the 60 | /// [`render_object`] module. 61 | /// 62 | /// [`render_object`]: ../../orbtk_core/render_object/index.html 63 | fn render_object(&self) -> Box<dyn RenderObject> { 64 | DefaultRenderObject.into() 65 | } 66 | 67 | /// Returns a pointer to a heap allocated object. 68 | /// 69 | /// The `layout` process will arrange all `box` entities of a widget 70 | /// in a tree, were size properties will meet the individual constraints. 71 | /// For the list of available layout objects, see 72 | /// the [`layout`] module. 73 | /// 74 | /// [`layout`]: ../../orbtk_core/layout/index.html 75 | fn layout(&self) -> Box<dyn Layout> { 76 | GridLayout::new().into() 77 | } 78 | } 79 | -------------------------------------------------------------------------------- /orbtk_orbclient/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | authors = ["Florian Blasius <flovanpt@posteo.de>"] 3 | description = "Window shell crate used by OrbTk." 4 | edition = "2021" 5 | keywords = ["shell", "window", "ui"] 6 | license = "MIT" 7 | name = "orbtk_orbclient" 8 | readme = "README.md" 9 | repository = "https://github.com/redox-os/orbtk" 10 | version = "0.3.1-alpha5" 11 | 12 | [dependencies] 13 | image = { version = "0.24", default-features = false, features = ["ico"] } 14 | lazy_static = "1.4.0" 15 | orbtk_tinyskia = { path = "../orbtk_tinyskia", version = "0.3.1-alpha5", default-features = false } 16 | orbtk_utils = { path = "../utils", version = "0.3.1-alpha5" } 17 | raw-window-handle = { version = "0.4" } 18 | 19 | [dependencies.orbclient] 20 | #version = "0.3.33" 21 | # git reference is needed until PR is merged upstream 22 | git = "https://gitlab.redox-os.org/rzerres/orbclient.git" 23 | branch = "master" 24 | default-features = false 25 | 26 | [features] 27 | log = [] 28 | bundled = ["sdl2/bundled", "sdl2/static-link", "orbclient/bundled"] 29 | 30 | [lib] 31 | doctest = false 32 | 33 | [target.'cfg(not(target_os = "redox"))'.dependencies] 34 | sdl2 = { version = "0.35", features = ["raw-window-handle"] } 35 | -------------------------------------------------------------------------------- /orbtk_orbclient/README.md: -------------------------------------------------------------------------------- 1 | # orbtk_orbclient 2 | 3 | This crate offers a cross platform `window shell` library. It's part of 4 | [OrbTk](https://gitlab.redox-os.org/redox-os/orbtk) - The Rust 5 | UI-Toolkit. 6 | 7 | [](https://github.com/redox-os/orbtk/actions) 8 | [](../../LICENSE) 9 | 10 | ## Platforms 11 | 12 | * Redox OS 13 | * Linux 14 | * macOS 15 | * Windows 16 | * openBSD (not tested, but should work) 17 | * Web 18 | * Android (planned) 19 | * iOS (planned) 20 | * Ubuntu Touch (planned) 21 | 22 | ## Dependencies 23 | 24 | * [lazy_static](https://github.com/rust-lang-nursery/lazy-static.rs) (MIT): A macro for declaring lazily evaluated statics in Rust 25 | * [image](https://github.com/image-rs/image) (MIT): load pixel images e.g. png 26 | * [orbclient](https://gitlab.redox-os.org/redox-os/orbclient) (MIT): The Orbital Client Library 27 | * [raw-window-handle](https://github.com/rust-windowing/raw-window-handle) (MIT): access to a window's platform-specific raw window handle 28 | * [sdl2](https://www.libsdl.org) (zlib): Simple DirectMedia Layer 29 | * [stdweb](https://github.com/koute/stdweb) (Apache 2.0, MIT): web window and events 30 | 31 | ## License 32 | 33 | Licensed under MIT license ([LICENSE](../../LICENSE)). -------------------------------------------------------------------------------- /orbtk_orbclient/src/lib.rs: -------------------------------------------------------------------------------- 1 | /*! 2 | 3 | Window shell abstraction layer used by OrbTk. Provides support for desktop and web. 4 | 5 | # Example 6 | 7 | Basic usage of the shell: 8 | 9 | ```rust,no_run 10 | 11 | use orbtk_orbclient::prelude::*; 12 | 13 | let shell = WindowBuilder::new(MyCustomWindowAdapter::new()) 14 | .title("Window") 15 | .bounds((0.0, 0.0, 100.0, 100.0)) 16 | .build(); 17 | 18 | let runner = ShellRunner { 19 | shell, 20 | updater: Box::new(MyCustomUpdater::new()) 21 | }; 22 | 23 | runner.run() 24 | ``` 25 | 26 | */ 27 | #[macro_use] 28 | extern crate lazy_static; 29 | 30 | pub mod event; 31 | pub mod prelude; 32 | pub mod window_adapter; 33 | 34 | pub use orbtk_utils::prelude as utils; 35 | 36 | pub mod orbclient; 37 | 38 | #[cfg(not(target_arch = "wasm32"))] 39 | pub mod native; 40 | 41 | pub use orbtk_tinyskia::prelude as render; 42 | 43 | use std::{collections::HashMap, sync::mpsc}; 44 | 45 | /// Used to send a request to the window. 46 | #[derive(Clone, Debug, PartialEq, Eq)] 47 | pub enum WindowRequest { 48 | /// Request to change the title of the `Windows`. 49 | ChangeTitle(String), 50 | 51 | /// Request to close the `Windows`. 52 | Close, 53 | 54 | /// Request redraw of the `Windows`s content. 55 | Redraw, 56 | } 57 | 58 | /// Used to send a request to the application shell. 59 | pub enum ShellRequest<W> 60 | where 61 | W: window_adapter::WindowAdapter, 62 | { 63 | /// Request redraw of the `Windows`s content. 64 | CreateWindow(W, WindowSettings, mpsc::Receiver<WindowRequest>), 65 | 66 | None, 67 | } 68 | 69 | impl<W> Default for ShellRequest<W> 70 | where 71 | W: window_adapter::WindowAdapter, 72 | { 73 | fn default() -> Self { 74 | ShellRequest::None 75 | } 76 | } 77 | 78 | /// Contains settings of a window. 79 | #[derive(Clone, Debug, Default)] 80 | pub struct WindowSettings { 81 | /// Will the window always shown on top of other windows. 82 | pub always_on_top: bool, 83 | 84 | /// Is the window borderless / without decorations? 85 | pub borderless: bool, 86 | 87 | /// List of fonts to register. 88 | pub fonts: HashMap<String, &'static [u8]>, 89 | 90 | /// The initial position of the window. 91 | pub position: (f64, f64), 92 | 93 | /// Is the window resizable? 94 | pub resizeable: bool, 95 | 96 | /// The initial size of the window. 97 | pub size: (f64, f64), 98 | 99 | /// Title of the window. 100 | pub title: String, 101 | } 102 | -------------------------------------------------------------------------------- /orbtk_orbclient/src/native/mod.rs: -------------------------------------------------------------------------------- 1 | //! This module provides native OS implementations. 2 | 3 | use std::{collections::HashMap, sync::Mutex, time::Instant}; 4 | 5 | lazy_static! { 6 | pub static ref CONSOLE: Console = Console { 7 | instants: Mutex::new(HashMap::new()) 8 | }; 9 | } 10 | 11 | pub struct Console { 12 | instants: Mutex<HashMap<String, Instant>>, 13 | } 14 | 15 | impl Console { 16 | pub fn time(&self, name: impl Into<String>) { 17 | self.instants 18 | .lock() 19 | .unwrap() 20 | .insert(name.into(), Instant::now()); 21 | } 22 | 23 | pub fn time_end(&self, name: impl Into<String>) { 24 | if let Some((_k, _v)) = self.instants.lock().unwrap().remove_entry(&name.into()) { 25 | #[cfg(feature = "log")] 26 | println!("{} {}ms - timer ended", _k, _v.elapsed().as_millis()); 27 | } 28 | } 29 | 30 | #[allow(unused_variables)] 31 | pub fn log(&self, message: impl Into<String>) { 32 | #[cfg(feature = "log")] 33 | println!("{}", message.into()); 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /orbtk_orbclient/src/orbclient/mod.rs: -------------------------------------------------------------------------------- 1 | //! This module contains a platform specific implementation of the window shell. 2 | 3 | use std::sync::mpsc; 4 | 5 | pub use super::native::*; 6 | 7 | use crate::prelude::*; 8 | 9 | use self::states::*; 10 | pub use self::window::*; 11 | pub use self::window_builder::*; 12 | 13 | mod states; 14 | mod window; 15 | mod window_builder; 16 | 17 | /// Does nothing. This function is only used by the web backend. 18 | pub fn initialize() {} 19 | 20 | /// Represents an application shell that could handle multiple windows. 21 | pub struct Shell<A: 'static> 22 | where 23 | A: WindowAdapter, 24 | { 25 | window_shells: Vec<Window<A>>, 26 | requests: mpsc::Receiver<ShellRequest<A>>, 27 | } 28 | 29 | impl<A> Shell<A> 30 | where 31 | A: WindowAdapter, 32 | { 33 | /// Creates a window builder, that could be used to create a window and add it to the application shell. 34 | pub fn create_window(&mut self, adapter: A) -> WindowBuilder<A> { 35 | WindowBuilder::new(self, adapter) 36 | } 37 | 38 | /// Creates a window builder from a settings object. 39 | pub fn create_window_from_settings( 40 | &mut self, 41 | settings: WindowSettings, 42 | adapter: A, 43 | ) -> WindowBuilder<A> { 44 | WindowBuilder::from_settings(settings, self, adapter) 45 | } 46 | 47 | /// Creates a new application shell. 48 | pub fn new(requests: mpsc::Receiver<ShellRequest<A>>) -> Self { 49 | Shell { 50 | window_shells: vec![], 51 | requests, 52 | } 53 | } 54 | 55 | /// Receives window request from the application and handles them. 56 | pub fn receive_requests(&mut self) { 57 | let mut requests = vec![]; 58 | for request in self.requests.try_iter() { 59 | requests.push(request); 60 | } 61 | 62 | for request in requests { 63 | if let ShellRequest::CreateWindow(adapter, settings, window_requests) = request { 64 | self.create_window_from_settings(settings, adapter) 65 | .request_receiver(window_requests) 66 | .build(); 67 | } 68 | } 69 | } 70 | 71 | /// Runs (starts) the application shell and its windows. 72 | pub fn run(&mut self) { 73 | loop { 74 | if self.window_shells.is_empty() { 75 | return; 76 | } 77 | 78 | for i in 0..self.window_shells.len() { 79 | let mut remove = false; 80 | if let Some(window_shell) = self.window_shells.get_mut(i) { 81 | window_shell.update(); 82 | window_shell.render(); 83 | 84 | window_shell.update_clipboard(); 85 | window_shell.drain_events(); 86 | window_shell.receive_requests(); 87 | 88 | if !window_shell.is_open() { 89 | remove = true; 90 | } 91 | } 92 | 93 | if remove { 94 | self.window_shells.remove(i); 95 | break; 96 | } 97 | } 98 | 99 | self.receive_requests(); 100 | } 101 | } 102 | } 103 | -------------------------------------------------------------------------------- /orbtk_orbclient/src/orbclient/states.rs: -------------------------------------------------------------------------------- 1 | /// Internal helper state to handle current mouse state. 2 | #[derive(Copy, Clone, Default, Debug)] 3 | pub struct MouseState { 4 | pub button_left: bool, 5 | pub button_middle: bool, 6 | pub button_right: bool, 7 | pub mouse_pos: (f32, f32), 8 | } 9 | 10 | /// Internal helper state to handle current window state. 11 | #[derive(Copy, Clone, Default, Debug)] 12 | pub struct WindowState { 13 | pub active: bool, 14 | pub size: (usize, usize), 15 | } 16 | -------------------------------------------------------------------------------- /orbtk_orbclient/src/prelude.rs: -------------------------------------------------------------------------------- 1 | //! This module pre-selects commonly used OrbTk crates and put them into scope. 2 | pub use crate::{ 3 | event::*, orbclient::*, window_adapter::*, ShellRequest, WindowRequest, WindowSettings, 4 | }; 5 | -------------------------------------------------------------------------------- /orbtk_orbclient/src/web/states.rs: -------------------------------------------------------------------------------- 1 | use std::{cell::RefCell, rc::Rc}; 2 | 3 | use stdweb::web::event; 4 | 5 | /// Used to store and read web events. 6 | pub struct EventState { 7 | pub mouse_move_events: Rc<RefCell<Vec<event::MouseMoveEvent>>>, 8 | pub mouse_up_events: Rc<RefCell<Vec<event::MouseUpEvent>>>, 9 | pub touch_start_events: Rc<RefCell<Vec<event::TouchStart>>>, 10 | pub touch_end_events: Rc<RefCell<Vec<event::TouchEnd>>>, 11 | pub touch_move_events: Rc<RefCell<Vec<event::TouchMove>>>, 12 | pub mouse_down_events: Rc<RefCell<Vec<event::MouseDownEvent>>>, 13 | pub scroll_events: Rc<RefCell<Vec<event::MouseWheelEvent>>>, 14 | pub key_up_events: Rc<RefCell<Vec<event::KeyUpEvent>>>, 15 | pub key_down_events: Rc<RefCell<Vec<event::KeyDownEvent>>>, 16 | pub resize_events: Rc<RefCell<Vec<event::ResizeEvent>>>, 17 | } 18 | -------------------------------------------------------------------------------- /orbtk_orbclient/src/window_adapter.rs: -------------------------------------------------------------------------------- 1 | //! This module contains traits to inject custom logic into the window shell. 2 | 3 | use crate::render::RenderContext2D; 4 | use crate::{event::*, utils::Point}; 5 | 6 | /// The `WindowAdapter` represents the bridge to the `Shell` backend. 7 | /// It receives events from the `Window` and runs it's own logic. 8 | pub trait WindowAdapter { 9 | /// Is called if active state of the window is changed. 10 | fn active(&mut self, active: bool); 11 | 12 | /// Used to update the clipboard, could be used to read and set the current clipboard value. 13 | fn clipboard_update(&mut self, value: &mut Option<String>); 14 | 15 | /// This method is called when a file is dropped on the window. 16 | fn file_drop_event(&mut self, file_name: String); 17 | 18 | /// Is called after the state of a keyboard key is changed. 19 | fn key_event(&mut self, _event: KeyEvent) {} 20 | 21 | /// Is called after the mouse was moved. 22 | fn mouse(&mut self, _x: f64, _y: f64) {} 23 | 24 | /// Is called after the state of a mouse button is changed. 25 | fn mouse_event(&mut self, _event: MouseEvent) {} 26 | 27 | /// Gets the current mouse position. 28 | fn mouse_position(&self) -> Point; 29 | 30 | /// Is called after the quit event of the window is called. 31 | fn quit_event(&mut self) {} 32 | 33 | /// Is called after the window is resized. 34 | fn resize(&mut self, _width: f64, _height: f64) {} 35 | 36 | /// Runs the inner logic of the shell adapter. 37 | fn run(&mut self, render_context: &mut RenderContext2D); 38 | 39 | /// Is called if mouse wheel or trackpad detect scroll event. 40 | fn scroll(&mut self, _delta_x: f64, _delta_y: f64) {} 41 | 42 | /// Sets raw window handle. 43 | fn set_raw_window_handle(&mut self, raw_window_handle: raw_window_handle::RawWindowHandle); 44 | 45 | /// Is called when the keyboard emits an text input. 46 | fn text_input(&mut self, _text: String) {} 47 | 48 | /// This method is called when a text string is dropped on the window. 49 | fn text_drop_event(&mut self, text: String); 50 | } 51 | -------------------------------------------------------------------------------- /orbtk_tinyskia/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | authors = ["Florian Blasius <flovanpt@posteo.de>"] 3 | description = "2D Render library use by OrbTk." 4 | edition = "2021" 5 | keywords = ["2D", "orbtk_tinyskia", "canvas"] 6 | license = "MIT" 7 | name = "orbtk_tinyskia" 8 | readme = "README.md" 9 | repository = "https://github.com/redox-os/orbtk" 10 | version = "0.3.1-alpha5" 11 | 12 | [dependencies] 13 | orbtk_utils = { path = "../utils", version = "0.3.1-alpha5" } 14 | 15 | [target.'cfg(not(target_arch = "wasm32"))'.dependencies] 16 | image = { version = "0.24", default-features = false, features = ["ico"] } 17 | rusttype = { version = "0.9" } 18 | smallvec = "1" 19 | tiny-skia = { version = "0.8.1" } 20 | -------------------------------------------------------------------------------- /orbtk_tinyskia/README.md: -------------------------------------------------------------------------------- 1 | # orbtk_tinyskia 2 | 3 | Cross platform 2D/3D render library. It's part of [OrbTk](https://gitlab.redox-os.org/redox-os/orbtk) - The Rust UI-Toolkit. 4 | 5 | [](https://github.com/redox-os/orbtk/actions) 6 | [](../../LICENSE) 7 | 8 | ## Platforms 9 | 10 | * Redox OS 11 | * Linux 12 | * macOS 13 | * Windows 14 | * openBSD (not tested, but should work) 15 | * Web 16 | * Android (not tested, but should work) 17 | * iOS (not tested, but should work) 18 | * Ubuntu Touch (not tested, but should work) 19 | 20 | ## Dependencies 21 | 22 | * [stdweb](https://github.com/koute/stdweb) (Apache 2.0, MIT): 2D render context web 23 | * [raqote](https://github.com/jrmuizel/raqote) (BSD-3-Clause): 2D render context 24 | * [rusttype](https://gitlab.redox-os.org/redox-os/rusttype) (Apache 2.0, MIT): font processing 25 | * [image](https://github.com/image-rs/image) (MIT): load pixel images e.g. png 26 | 27 | ## License 28 | 29 | Licensed under MIT license ([LICENSE](../../LICENSE)). -------------------------------------------------------------------------------- /orbtk_tinyskia/src/lib.rs: -------------------------------------------------------------------------------- 1 | #![recursion_limit = "256"] 2 | 3 | //! This module provides the render environment. 4 | //! 5 | //! OrbTk has choosen the [tinyskia] crate to handle all 2D rendering 6 | //! tasks. Implemented as wrapper functions, it consumes the native 7 | //! rendering functions provided from tinyskia. 8 | //! 9 | //! [tinyskia]: https://docs.rs/tiny-skia 10 | 11 | use std::{any::Any, fmt}; 12 | 13 | /// Pre-selects commonly used OrbTk crates and put them into scope. 14 | pub mod prelude; 15 | 16 | /// Handles helper utilities and global methods. 17 | pub use orbtk_utils::prelude as utils; 18 | 19 | mod common; 20 | 21 | pub use tinyskia::*; 22 | 23 | pub mod tinyskia; 24 | 25 | pub use self::render_target::*; 26 | 27 | mod render_target; 28 | 29 | /// Defines the current configuration used inside a render resource. 30 | #[derive(Debug, Clone)] 31 | pub struct RenderConfig { 32 | pub fill_style: utils::Brush, 33 | pub stroke_style: utils::Brush, 34 | pub line_width: f64, 35 | pub font_config: FontConfig, 36 | pub alpha: f32, 37 | } 38 | 39 | impl Default for RenderConfig { 40 | fn default() -> Self { 41 | RenderConfig { 42 | fill_style: utils::Brush::default(), 43 | stroke_style: utils::Brush::default(), 44 | line_width: 1., 45 | font_config: FontConfig::default(), 46 | alpha: 1., 47 | } 48 | } 49 | } 50 | 51 | /// The TextMetrics struct represents the dimension of a text. 52 | #[derive(Clone, Copy, Default, Debug)] 53 | pub struct TextMetrics { 54 | pub width: f64, 55 | pub height: f64, 56 | } 57 | 58 | /// Internal font helper. 59 | #[derive(Default, Clone, PartialEq, Debug)] 60 | pub struct FontConfig { 61 | pub family: String, 62 | pub font_size: f64, 63 | } 64 | 65 | impl ToString for FontConfig { 66 | fn to_string(&self) -> String { 67 | format!("{}px {}", self.font_size, self.family) 68 | } 69 | } 70 | 71 | /// Provides methods to handle the 2D render pipeline. 72 | pub trait RenderPipeline { 73 | /// Draws the ctx of the pipeline. 74 | fn draw(&self, image: &mut RenderTarget); 75 | } 76 | 77 | /// Used to implement a custom render pipeline. 78 | pub trait PipelineTrait: RenderPipeline + Any + Send { 79 | /// Equality for two Pipeline objects. 80 | fn box_eq(&self, other: &dyn Any) -> bool; 81 | 82 | /// Converts self to an any reference. 83 | fn as_any(&self) -> &dyn Any; 84 | 85 | /// Clones self as box. 86 | fn clone_box(&self) -> Box<dyn PipelineTrait>; 87 | 88 | /// Draws the ctx of the pipeline. 89 | fn draw_pipeline(&self, image: &mut RenderTarget) { 90 | self.draw(image); 91 | } 92 | } 93 | 94 | impl PartialEq for Box<dyn PipelineTrait> { 95 | fn eq(&self, other: &Box<dyn PipelineTrait>) -> bool { 96 | self.box_eq(other.as_any()) 97 | } 98 | } 99 | 100 | impl Clone for Box<dyn PipelineTrait> { 101 | fn clone(&self) -> Self { 102 | self.clone_box() 103 | } 104 | } 105 | 106 | impl fmt::Debug for Box<dyn PipelineTrait> { 107 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 108 | write!(f, "Box<dyn PipelineTrait>") 109 | } 110 | } 111 | -------------------------------------------------------------------------------- /orbtk_tinyskia/src/prelude.rs: -------------------------------------------------------------------------------- 1 | /// This module pre-selects commonly used OrbTk crates and put them into scope. 2 | #[cfg(not(target_arch = "wasm32"))] 3 | pub use crate::tinyskia::Font; 4 | pub use crate::tinyskia::Image; 5 | pub use crate::*; 6 | -------------------------------------------------------------------------------- /orbtk_tinyskia/src/render_target.rs: -------------------------------------------------------------------------------- 1 | use std::fmt; 2 | 3 | use crate::utils::*; 4 | 5 | /// Structure used to define render targets. 6 | #[derive(Clone, Default)] 7 | pub struct RenderTarget { 8 | width: u32, 9 | height: u32, 10 | pub data: Vec<u32>, 11 | } 12 | 13 | impl fmt::Debug for RenderTarget { 14 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 15 | write!( 16 | f, 17 | "RenderTarget ( width: {}, height: {})", 18 | self.width, self.height 19 | ) 20 | } 21 | } 22 | 23 | impl std::cmp::PartialEq for RenderTarget { 24 | fn eq(&self, other: &Self) -> bool { 25 | self.width == other.width && self.height == other.height 26 | } 27 | } 28 | 29 | impl RenderTarget { 30 | /// Creates a new image with the given width and height. 31 | pub fn new(width: u32, height: u32) -> Self { 32 | RenderTarget { 33 | width, 34 | height, 35 | data: vec![Color::rgba(0, 0, 0, 0).data; width as usize * height as usize], 36 | } 37 | } 38 | 39 | /// Draws a u32 slice into the image. 40 | pub fn draw(&mut self, data: &[u32]) { 41 | self.data.clone_from_slice(data); 42 | } 43 | 44 | /// Create a new image from a boxed slice of colors 45 | pub fn from_data(width: u32, height: u32, data: Vec<u32>) -> Result<Self, String> { 46 | Ok(RenderTarget { 47 | width, 48 | height, 49 | data, 50 | }) 51 | } 52 | 53 | /// Gets the width. 54 | pub fn width(&self) -> f64 { 55 | self.width as f64 56 | } 57 | 58 | /// Gets the height. 59 | pub fn height(&self) -> f64 { 60 | self.height as f64 61 | } 62 | 63 | /// Gets the data object. 64 | pub fn data(&self) -> &[u32] { 65 | &self.data 66 | } 67 | 68 | /// Gets a mutable data object. 69 | pub fn data_mut(&mut self) -> &mut [u32] { 70 | &mut self.data 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /orbtk_tinyskia/src/tinyskia/image.rs: -------------------------------------------------------------------------------- 1 | use std::{fmt, path::Path}; 2 | 3 | use crate::RenderTarget; 4 | 5 | /// Structure that defines elements of an image object. 6 | #[derive(Clone, Default)] 7 | pub struct Image { 8 | render_target: RenderTarget, 9 | source: String, 10 | } 11 | 12 | impl fmt::Debug for Image { 13 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 14 | write!(f, "Image ( source: {})", self.source) 15 | } 16 | } 17 | 18 | impl std::cmp::PartialEq for Image { 19 | fn eq(&self, other: &Self) -> bool { 20 | self.source == other.source 21 | } 22 | } 23 | 24 | impl Image { 25 | /// Creates a new image with the given width and height. 26 | pub fn new(width: u32, height: u32) -> Self { 27 | Image { 28 | render_target: RenderTarget::new(width, height), 29 | source: String::default(), 30 | } 31 | } 32 | 33 | /// Draws a u32 slice into the image. 34 | pub fn draw(&mut self, data: &[u32]) { 35 | self.render_target.data.clone_from_slice(data); 36 | } 37 | 38 | /// Create a new image from a boxed slice of colors 39 | pub fn from_data(width: u32, height: u32, data: Vec<u32>) -> Result<Self, String> { 40 | Ok(Image { 41 | render_target: RenderTarget::from_data(width, height, data).unwrap(), 42 | source: String::new(), 43 | }) 44 | } 45 | 46 | /// Creates a new image from an `RgbaImage`. 47 | pub fn from_rgba_image(image: image::RgbaImage) -> Result<Self, String> { 48 | let data: Vec<u32> = image 49 | .pixels() 50 | .map(|p| { 51 | ((p[3] as u32) << 24) | ((p[0] as u32) << 16) | ((p[1] as u32) << 8) | (p[2] as u32) 52 | }) 53 | .collect(); 54 | Self::from_data(image.width(), image.height(), data) 55 | } 56 | 57 | /// Load an image from file path. Supports BMP and PNG 58 | pub fn from_path<P: AsRef<Path> + std::fmt::Debug + Clone>(path: P) -> Result<Self, String> { 59 | let img = image::open(path.clone()); 60 | 61 | if let Ok(img) = img { 62 | return Self::from_rgba_image(img.to_rgba8()); 63 | } 64 | 65 | Err(format!("Could not load image width path: {:?}", path)) 66 | } 67 | 68 | /// Gets the width. 69 | pub fn width(&self) -> f64 { 70 | self.render_target.width() as f64 71 | } 72 | 73 | /// Gets the height. 74 | pub fn height(&self) -> f64 { 75 | self.render_target.height() as f64 76 | } 77 | 78 | pub fn data(&self) -> &[u32] { 79 | &self.render_target.data 80 | } 81 | 82 | pub fn data_mut(&mut self) -> &mut [u32] { 83 | &mut self.render_target.data 84 | } 85 | } 86 | 87 | impl From<(u32, u32, Vec<u32>)> for Image { 88 | fn from(image: (u32, u32, Vec<u32>)) -> Self { 89 | Image::from_data(image.0, image.1, image.2).unwrap() 90 | } 91 | } 92 | 93 | pub fn os_path(path: String) -> String { 94 | if cfg!(windows) { 95 | path.replace('/', "\\") 96 | } else { 97 | path.replace('\\', "/") 98 | } 99 | } 100 | 101 | // --- Conversions --- 102 | 103 | impl From<&str> for Image { 104 | fn from(s: &str) -> Image { 105 | Image::from_path(os_path(s.to_string())).unwrap() 106 | } 107 | } 108 | 109 | impl From<String> for Image { 110 | fn from(s: String) -> Image { 111 | Image::from_path(os_path(s)).unwrap() 112 | } 113 | } 114 | 115 | // --- Conversions --- 116 | -------------------------------------------------------------------------------- /orbtk_widgets/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | authors = ["Florian Blasius <flovanpt@posteo.de>"] 3 | description = "Base OrbTk widget library." 4 | edition = "2021" 5 | keywords = ["ui", "widgets"] 6 | license = "MIT" 7 | name = "orbtk_widgets" 8 | version = "0.3.1-alpha5" 9 | readme = "README.md" 10 | repository = "https://github.com/redox-os/orbtk" 11 | 12 | [dependencies] 13 | lazy_static = "1.4" 14 | orbtk_core = { path = "../orbtk_core", version = "0.3.1-alpha5" } 15 | orbtk_orbclient = { path = "../orbtk_orbclient", version = "0.3.1-alpha5", default-features = false } 16 | orbtk_tinyskia = { path = "../orbtk_tinyskia", version = "0.3.1-alpha5", default-features = false } 17 | orbtk_utils = { path = "../utils", version = "0.3.1-alpha5" } 18 | orbtk_proc_macros = { version = "0.3.1-alpha5", path = "../proc_macros" } 19 | rust_decimal = "1.15" 20 | #rust_decimal_macros = "1.15" 21 | 22 | [dependencies.dces] 23 | #version = "0.3.1" 24 | git = "https://gitlab.redox-os.org/redox-os/dces-rust.git" 25 | branch = "master" 26 | #branch = "develop" 27 | 28 | [lib] 29 | doctest = false 30 | -------------------------------------------------------------------------------- /orbtk_widgets/README.md: -------------------------------------------------------------------------------- 1 | # orbtk_widgets 2 | 3 | OrbTks default widget library. It's part of [OrbTk](https://gitlab.redox-os.org/redox-os/orbtk) - The Rust UI-Toolkit. 4 | 5 | [](https://github.com/redox-os/orbtk/actions) 6 | [](../../LICENSE) 7 | 8 | ## Dependencies 9 | 10 | * [dces](https://gitlab.redox-os.org/redox-os/dces-rust) (MIT): Entity Component System 11 | 12 | ## License 13 | 14 | Licensed under MIT license ([LICENSE](../../LICENSE)). -------------------------------------------------------------------------------- /orbtk_widgets/assets/fonts/material/MaterialIcons-Baseline.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/redox-os/orbtk/eba9e77821551076bbf1d9f7ab44d788150e3446/orbtk_widgets/assets/fonts/material/MaterialIcons-Baseline.woff2 -------------------------------------------------------------------------------- /orbtk_widgets/assets/fonts/material/MaterialIcons-Outlined.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/redox-os/orbtk/eba9e77821551076bbf1d9f7ab44d788150e3446/orbtk_widgets/assets/fonts/material/MaterialIcons-Outlined.woff2 -------------------------------------------------------------------------------- /orbtk_widgets/assets/fonts/material/MaterialIcons-Round.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/redox-os/orbtk/eba9e77821551076bbf1d9f7ab44d788150e3446/orbtk_widgets/assets/fonts/material/MaterialIcons-Round.woff2 -------------------------------------------------------------------------------- /orbtk_widgets/assets/fonts/material/MaterialIcons-Sharp.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/redox-os/orbtk/eba9e77821551076bbf1d9f7ab44d788150e3446/orbtk_widgets/assets/fonts/material/MaterialIcons-Sharp.woff2 -------------------------------------------------------------------------------- /orbtk_widgets/assets/fonts/material/MaterialIcons-TwoTone.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/redox-os/orbtk/eba9e77821551076bbf1d9f7ab44d788150e3446/orbtk_widgets/assets/fonts/material/MaterialIcons-TwoTone.woff2 -------------------------------------------------------------------------------- /orbtk_widgets/assets/fonts/material/MaterialIcons.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/redox-os/orbtk/eba9e77821551076bbf1d9f7ab44d788150e3446/orbtk_widgets/assets/fonts/material/MaterialIcons.ttf -------------------------------------------------------------------------------- /orbtk_widgets/assets/fonts/material/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@material-icons/font", 3 | "description": "Material Design icons by Google, updated (Font)", 4 | "version": "1.0.1", 5 | "author": "Material Design Authors", 6 | "license": "Apache-2.0", 7 | "bugs": { 8 | "url": "https://github.com/material-icons/material-icons/issues" 9 | }, 10 | "homepage": "https://github.com/material-icons/material-icons", 11 | "scripts": { 12 | "build": "node build/font.js" 13 | }, 14 | "devDependencies": { 15 | "cheerio": "^1.0.0-rc.3", 16 | "node-sass": "^4.13.1", 17 | "svg2ttf": "^4.3.0", 18 | "svgicons2svgfont": "^9.1.1", 19 | "ttf2woff": "^2.0.1", 20 | "ttf2woff2": "^3.0.0" 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /orbtk_widgets/assets/fonts/mdl2/mdl2.otf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/redox-os/orbtk/eba9e77821551076bbf1d9f7ab44d788150e3446/orbtk_widgets/assets/fonts/mdl2/mdl2.otf -------------------------------------------------------------------------------- /orbtk_widgets/assets/fonts/roboto/Roboto-Medium.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/redox-os/orbtk/eba9e77821551076bbf1d9f7ab44d788150e3446/orbtk_widgets/assets/fonts/roboto/Roboto-Medium.ttf -------------------------------------------------------------------------------- /orbtk_widgets/assets/fonts/roboto/Roboto-Regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/redox-os/orbtk/eba9e77821551076bbf1d9f7ab44d788150e3446/orbtk_widgets/assets/fonts/roboto/Roboto-Regular.ttf -------------------------------------------------------------------------------- /orbtk_widgets/assets/fonts/selawik/Selawik-Bold.otf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/redox-os/orbtk/eba9e77821551076bbf1d9f7ab44d788150e3446/orbtk_widgets/assets/fonts/selawik/Selawik-Bold.otf -------------------------------------------------------------------------------- /orbtk_widgets/assets/fonts/selawik/Selawik-Light.otf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/redox-os/orbtk/eba9e77821551076bbf1d9f7ab44d788150e3446/orbtk_widgets/assets/fonts/selawik/Selawik-Light.otf -------------------------------------------------------------------------------- /orbtk_widgets/assets/fonts/selawik/Selawik-Regular.otf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/redox-os/orbtk/eba9e77821551076bbf1d9f7ab44d788150e3446/orbtk_widgets/assets/fonts/selawik/Selawik-Regular.otf -------------------------------------------------------------------------------- /orbtk_widgets/assets/themes/fluent/theme_fluent_colors_dark.ron: -------------------------------------------------------------------------------- 1 | Theme ( 2 | resources: { 3 | // common 4 | "BLACK": "#000000", 5 | "WHITE": "#ffffff", 6 | "ACCENT_COLOR": "#0078D7", 7 | "ACCENT_COLOR_LIGHTER": "#f0d860", 8 | "ACCENT_COLOR_LIGHT": "#589BE6", 9 | "ACCENT_COLOR_DARK": "#2259A1", 10 | "BACKGROUND": "#000000", 11 | "BACKGROUND_SECONDARY": "#333333", 12 | 13 | // box colors (TextBox, CheckBox, ComboBox, ...) 14 | "BOX_BACKGROUND": "tranparent", 15 | "BOX_BORDER": "#CBCBCB", 16 | "BOX_BORDER_HOVER": "#ffffff", 17 | "BOX_BACKGROUND_PRESSED": "#666666", 18 | 19 | // CheckBox 20 | "CHECKBOX_ICON_BRUSH": "#ffffff", 21 | 22 | // cursor 23 | "CURSOR_BORDER_BRUSH": "#ffffff", 24 | "CURSOR_BACKGROUND": "#0078D7", 25 | 26 | // content 27 | "CONTENT_FOREGROUND": "#ffffff", 28 | "CONTENT_FOREGROUND_INVERTED": "#3b434a", 29 | "CONTENT_FOREGROUND_SECONDARY": "#9dafbf", 30 | "CONRENT_FOREGROUND_ACCENT": "#ffffff", 31 | 32 | // button 33 | "BUTTON_BACKGROUND": "#333333", 34 | "BUTTON_BACKGROUND_HOVER": "#191919", 35 | "BUTTON_BACKGORUND_PRESSED": "#666666", 36 | "BUTTON_BACKGROUND_PRIMARY_SELECTED": "#ebbf13", 37 | "BUTTON_BACKGORUND_PRIMARY_PRESSED": "#ebbf13", 38 | "BUTTON_FOREGROUND_DISABLED": "#6A6A6A", 39 | "BUTTON_FOREGROUND_SELECTED": "#ffffff", 40 | 41 | // switch toggle 42 | "SWITCH_TOGGLE_BACKGROUND": "#ffffff", 43 | "SWITCH_TOGGLE_BACKGROUND_SELECTED": "#ffffff", 44 | 45 | // text box 46 | "TEXT_BOX_FOCUSED_BORDER": "#efd035", 47 | "TEXT_BOX_HOVER_BACKGROUND": "#000000", 48 | 49 | // combobox 50 | "COMBO_BOX_BACKGROUND": "#647b91", 51 | "POPUP_BACKGROUND": "#2B2B2B", 52 | 53 | // slider 54 | "SLIDER_BACKGROUND": "#666666", 55 | "SLIDER_BACKGROUND_HOVER": "#999999", 56 | 57 | // progress bar 58 | "PROGRESS_BAR_BACKGROUND": "#333334", 59 | 60 | // containers such es lists and text box 61 | "CONTAINER_BACKGROUND": "#2B2D2F", 62 | "CONTAINER_BORDER": "#A5A5A5", 63 | 64 | // items (combobox item listview item) 65 | "ITEM_BACKGROUND_HOVER": "#404040", 66 | "ITEM_BACKGROUND_PRESSED": "#555555", 67 | 68 | // tab header 69 | "TAB_HEADER_HOVER": "#1F1F1F" 70 | }, 71 | ) -------------------------------------------------------------------------------- /orbtk_widgets/assets/themes/fluent/theme_fluent_colors_light.ron: -------------------------------------------------------------------------------- 1 | Theme ( 2 | resources: { 3 | // common 4 | "BLACK": "#000000", 5 | "WHITE": "#ffffff", 6 | "ACCENT_COLOR": "#0078D7", 7 | "ACCENT_COLOR_LIGHTER": "#f0d860", 8 | "ACCENT_COLOR_LIGHT": "#589BE6", 9 | "ACCENT_COLOR_DARK": "#2259A1", 10 | "BACKGROUND": "#FFFFFF", 11 | "BACKGROUND_SECONDARY": "#E6E6E6", 12 | 13 | // box colors (TextBox, CheckBox, ComboBox, ...) 14 | "BOX_BACKGROUND": "tranparent", 15 | "BOX_BORDER": "#666666", 16 | "BOX_BORDER_HOVER": "#4C4C4C", 17 | "BOX_BACKGROUND_PRESSED": "#999999", 18 | 19 | // CheckBox 20 | "CHECKBOX_ICON_BRUSH": "#ffffff", 21 | 22 | // cursor 23 | "CURSOR_BORDER_BRUSH": "#000000", 24 | "CURSOR_BACKGROUND": "#0078D7", 25 | 26 | // content 27 | "CONTENT_FOREGROUND": "#242424", 28 | "CONTENT_FOREGROUND_INVERTED": "#3b434a", 29 | "CONTENT_FOREGROUND_SECONDARY": "#9dafbf", 30 | "CONRENT_FOREGROUND_ACCENT": "#ffffff", 31 | 32 | // button 33 | "BUTTON_BACKGROUND": "#CCCCCC", 34 | "BUTTON_BACKGROUND_HOVER": "#E6E6E6", 35 | "BUTTON_BACKGORUND_PRESSED": "#999999", 36 | "BUTTON_BACKGROUND_PRIMARY_SELECTED": "#ebbf13", 37 | "BUTTON_BACKGORUND_PRIMARY_PRESSED": "#ebbf13", 38 | "BUTTON_FOREGROUND_DISABLED": "#7A7A7A", 39 | "BUTTON_FOREGROUND_SELECTED": "#ffffff", 40 | 41 | // switch toggle 42 | "SWITCH_TOGGLE_BACKGROUND": "#000000", 43 | "SWITCH_TOGGLE_BACKGROUND_SELECTED": "#ffffff", 44 | 45 | // text box 46 | "TEXT_BOX_FOCUSED_BORDER": "#efd035", 47 | "TEXT_BOX_HOVER_BACKGROUND": "#000000", 48 | 49 | // combobox 50 | "COMBO_BOX_BACKGROUND": "#647b91", 51 | "POPUP_BACKGROUND": "#E1E1E1", 52 | 53 | // slider 54 | "SLIDER_BACKGROUND": "#999999", 55 | "SLIDER_BACKGROUND_HOVER": "#666666", 56 | 57 | // progress bar 58 | "PROGRESS_BAR_BACKGROUND": "#CCCCCC", 59 | 60 | // containers such es lists and text box 61 | "CONTAINER_BACKGROUND": "#2B2D2F", 62 | "CONTAINER_BORDER": "#A5A5A5", 63 | 64 | // items (combobox item listview item) 65 | "ITEM_BACKGROUND_HOVER": "#DADADA", 66 | "ITEM_BACKGROUND_PRESSED": "#C2C2C2", 67 | 68 | // tab header 69 | "TAB_HEADER_HOVER": "#EDEDED" 70 | }, 71 | ) -------------------------------------------------------------------------------- /orbtk_widgets/assets/themes/fluent/theme_fluent_fonts.ron: -------------------------------------------------------------------------------- 1 | Theme ( 2 | resources: { 3 | // fonts 4 | "REGULAR_FONT": "Selawik-Regular", 5 | "BOLD_FONT": "Selawik-Bold", 6 | "LIGHT_FONT": "Selawik-Light", 7 | // material icon fonts is used as default: 8 | // OrbTk's default widget library reference it, thus resolution is needed. 9 | "ICON_FONT": "MaterialIcons-Regular", 10 | "MDL2_ICON_FONT": "MDL2-Assets-Regular", 11 | 12 | // font sizes 13 | "FONT_SIZE_12": 12, 14 | "FONT_SIZE_13": 13, 15 | "FONT_SIZE_14": 14, 16 | "FONT_SIZE_16": 16, 17 | "FONT_SIZE_24": 24, 18 | "FONT_SIZE_32": 32, 19 | "FONT_SIZE_36": 36, 20 | 21 | // icon sizes 22 | "ICON_SIZE_4": 4, 23 | "ICON_SIZE_6": 6, 24 | "ICON_SIZE_8": 8, 25 | "ICON_SIZE_10": 10, 26 | "ICON_SIZE_12": 12, 27 | "ICON_SIZE_14": 14, 28 | "ICON_SIZE_16": 16, 29 | "ICON_SIZE_18": 18, 30 | }, 31 | ) 32 | -------------------------------------------------------------------------------- /orbtk_widgets/assets/themes/orbtk/theme_default_colors_dark.ron: -------------------------------------------------------------------------------- 1 | Theme ( 2 | resources: { 3 | // common 4 | "BLACK": "#000000", 5 | "WHITE": "#ffffff", 6 | "ACCENT_COLOR": "#efd035", 7 | "ACCENT_COLOR_LIGHTER": "#f0d860", 8 | "ACCENT_COLOR_LIGHT": "#5B6764", 9 | "ACCENT_COLOR_DARK": "#ebbf13", 10 | "BACKGROUND": "#3b434a", 11 | "BACKGROUND_SECONDARY": "#475b6e", 12 | 13 | // content 14 | "CONTENT_FOREGROUND": "#dfebf5", 15 | "CONTENT_FOREGROUND_INVERTED": "#3b434a", 16 | "CONTENT_FOREGROUND_SECONDARY": "#9dafbf", 17 | 18 | // button 19 | "BUTTON_BACKGROUND": "linear-gradient(to top, #7a8fa3, #647b91)", 20 | "BUTTON_BACKGROUND_SELECTED": "#516475", 21 | "BUTTON_BACKGROUND_HOVER": "linear-gradient(to top, #647b91, #475b6e)", 22 | "BUTTON_BACKGORUND_PRESSED": "#516475", 23 | "BUTTON_BACKGROUND_DISABLED": "linear-gradient(to top, #797d80, #808488)", 24 | "BUTTON_BACKGROUND_PRIMARY": "linear-gradient(to top, #f7e899, #ebbf13)", 25 | "BUTTON_BACKGROUND_PRIMARY_SELECTED": "#ebbf13", 26 | "BUTTON_BACKGROUND_PRIMARY_HOVER": "linear-gradient(to top, #f0d860, #efd035)", 27 | "BUTTON_BACKGORUND_PRIMARY_PRESSED": "#ebbf13", 28 | "BUTTON_FOREGROUND_DISABLED": "#3b434a", 29 | 30 | // text box 31 | "TEXT_BOX_FOCUSED_BORDER": "#efd035", 32 | "TEXT_BOX_HOVER_BACKGROUND": "#000000", 33 | 34 | // combobox 35 | "COMBO_BOX_BACKGROUND": "#647b91", 36 | "POPUP_BACKGROUND": "#444e55", 37 | "POPUP_BORDER": "#adb3b8", 38 | 39 | // switch 40 | "SWITCH_TOGGLE_BACKGROUND": "#dfebf5", 41 | "SWITCH_TOGGLE_BORDER": "#dfebf5", 42 | 43 | // slider 44 | "SLIDER_THUMB_BACKGROUND": "#dfebf5", 45 | "SLIDER_THUMB_BORDER": "#dfebf5", 46 | 47 | // combobox 48 | "COMBO_BOX_ITEM_SELECTED": "#3b434a", 49 | 50 | // progress bar 51 | "PROGRESS_BAR_BACKGROUND": "#000000", 52 | 53 | // containers such es lists and text box 54 | "CONTAINER_BACKGROUND": "#2B2D2F", 55 | "CONTAINER_BORDER": "#dfebf5", 56 | }, 57 | ) -------------------------------------------------------------------------------- /orbtk_widgets/assets/themes/orbtk/theme_default_colors_light.ron: -------------------------------------------------------------------------------- 1 | Theme ( 2 | resources: { 3 | // common 4 | "BLACK": "#000000", 5 | "WHITE": "#ffffff", 6 | "ACCENT_COLOR": "#efd035", 7 | "ACCENT_COLOR_LIGHTER": "#f0d860", 8 | "ACCENT_COLOR_LIGHT": "#eeead8", 9 | "ACCENT_COLOR_DARK": "#ebbf13", 10 | "BACKGROUND": "#fafafa", 11 | "BACKGROUND_SECONDARY": "#d1d1d1", 12 | 13 | // content 14 | "CONTENT_FOREGROUND": "#3b434a", 15 | "CONTENT_FOREGROUND_INVERTED": "#3b434a", 16 | "CONTENT_FOREGROUND_SECONDARY": "#71757A", 17 | 18 | // button 19 | "BUTTON_BACKGROUND": "linear-gradient(to top, #e5e5e5, #d2d2d2)", 20 | "BUTTON_BACKGROUND_SELECTED": "#a1a1a1", 21 | "BUTTON_BACKGROUND_HOVER": "linear-gradient(to top, #c7c7c7, #b2b3b1)", 22 | "BUTTON_BACKGORUND_PRESSED": "#a1a1a1", 23 | "BUTTON_BACKGROUND_DISABLED": "linear-gradient(to top, #e9e9e8, #F2F1F1)", 24 | "BUTTON_BACKGROUND_PRIMARY": "linear-gradient(to top, #f7e899, #efd035)", 25 | "BUTTON_BACKGROUND_PRIMARY_SELECTED": "#f0d860", 26 | "BUTTON_BACKGROUND_PRIMARY_HOVER": "linear-gradient(to top, #ebbf13, #ebbf14)", 27 | "BUTTON_BACKGORUND_PRIMARY_PRESSED": "#f0d860", 28 | "BUTTON_FOREGROUND_DISABLED": "#BEC1C2", 29 | 30 | // text box 31 | "TEXT_BOX_FOCUSED_BORDER": "#ebbf13", 32 | "TEXT_BOX_HOVER_BACKGROUND": "#a1a1a1", 33 | 34 | // combobox 35 | "COMBO_BOX_BACKGROUND": "#647b91", 36 | "POPUP_BACKGROUND": "#ffffff", 37 | "POPUP_BORDER": "#000000", 38 | 39 | // switch 40 | "SWITCH_TOGGLE_BACKGROUND": "#ffffff", 41 | "SWITCH_TOGGLE_BORDER": "#3b434a", 42 | 43 | // slider 44 | "SLIDER_THUMB_BACKGROUND": "#ffffff", 45 | "SLIDER_THUMB_BORDER": "#3b434a", 46 | 47 | // combobox 48 | "COMBO_BOX_ITEM_SELECTED": "#3b434a", 49 | 50 | // progress bar 51 | "PROGRESS_BAR_BACKGROUND": "#ffffff", 52 | 53 | // containers such es lists and text box 54 | "CONTAINER_BACKGROUND": "#ffffff", 55 | "CONTAINER_BORDER": "#000000", 56 | }, 57 | ) -------------------------------------------------------------------------------- /orbtk_widgets/assets/themes/orbtk/theme_default_fonts.ron: -------------------------------------------------------------------------------- 1 | Theme ( 2 | resources: { 3 | // fonts 4 | "MEDIUM_FONT": "Roboto-Medium", 5 | "REGULAR_FONT": "Roboto-Regular", 6 | "ICON_FONT": "MaterialIcons-Regular", 7 | 8 | // font sizes 9 | "FONT_SIZE_12": 12, 10 | "FONT_SIZE_13": 13, 11 | "FONT_SIZE_14": 14, 12 | "FONT_SIZE_16": 16, 13 | "FONT_SIZE_24": 24, 14 | "FONT_SIZE_32": 32, 15 | "FONT_SIZE_36": 36, 16 | 17 | // icon sizes 18 | "ICON_SIZE_4": 4, 19 | "ICON_SIZE_6": 6, 20 | "ICON_SIZE_8": 8, 21 | "ICON_SIZE_10": 10, 22 | "ICON_SIZE_12": 12, 23 | "ICON_SIZE_14": 14, 24 | "ICON_SIZE_16": 16, 25 | "ICON_SIZE_18": 18, 26 | }, 27 | ) 28 | -------------------------------------------------------------------------------- /orbtk_widgets/assets/themes/redox/theme_redox_colors.ron: -------------------------------------------------------------------------------- 1 | Theme ( 2 | resources: { 3 | // common 4 | "BLACK": "#000000", 5 | "WHITE": "#ffffff", 6 | "ACCENT_COLOR": "#3C95DF", 7 | "ACCENT_COLOR_LIGHTER": "#5ba2de", 8 | "ACCENT_COLOR_LIGHT": "#5B6764", 9 | "ACCENT_COLOR_DARK": "#1986e0", 10 | "BACKGROUND": "#F5F6F7", 11 | "BACKGROUND_SECONDARY": "#F5F6F7", 12 | "BORDER_DFAULT": "#CED6E5", 13 | 14 | // content 15 | "CONTENT_FOREGROUND": "#000000", 16 | "CONTENT_FOREGROUND_INVERTED": "#ffffff", 17 | "CONTENT_FOREGROUND_SECONDARY": "#9dafbf", 18 | 19 | // button 20 | "BUTTON_BACKGROUND": "#ffffff", 21 | "BUTTON_BACKGROUND_SELECTED": "#3C95DF", 22 | "BUTTON_BACKGROUND_HOVER": "#CED6E5", 23 | "BUTTON_BACKGORUND_PRESSED": "#3C95DF", 24 | "BUTTON_BACKGROUND_DISABLED": "linear-gradient(to top, #797d80, #808488)", 25 | "BUTTON_BACKGROUND_PRIMARY": "linear-gradient(to top, #f7e899, #ebbf13)", 26 | "BUTTON_BACKGROUND_PRIMARY_SELECTED": "#ebbf13", 27 | "BUTTON_BACKGROUND_PRIMARY_HOVER": "linear-gradient(to top, #f0d860, #efd035)", 28 | "BUTTON_BACKGORUND_PRIMARY_PRESSED": "#ebbf13", 29 | "BUTTON_FOREGROUND_DISABLED": "#3b434a", 30 | 31 | // text box 32 | "TEXT_BOX_FOCUSED_BORDER": "#3C95DF", 33 | "TEXT_BOX_HOVER_BACKGROUND": "#000000", 34 | 35 | // combobox 36 | "COMBO_BOX_BACKGROUND": "#647b91", 37 | "POPUP_BACKGROUND": "#ffffff", 38 | "POPUP_BORDER": "#CED6E5", 39 | 40 | // switch 41 | "SWITCH_TOGGLE_BACKGROUND": "#dfebf5", 42 | "SWITCH_TOGGLE_BORDER": "#dfebf5", 43 | 44 | // slider 45 | "SLIDER_THUMB_BACKGROUND": "#dfebf5", 46 | "SLIDER_THUMB_BORDER": "#dfebf5", 47 | 48 | // combobox 49 | "COMBO_BOX_ITEM_SELECTED": "#ffffff", 50 | 51 | // progress bar 52 | "PROGRESS_BAR_BACKGROUND": "#ffffff", 53 | 54 | // containers such es lists and text box 55 | "CONTAINER_BACKGROUND": "#ffffff", 56 | "CONTAINER_BORDER": "#CED6E5", 57 | }, 58 | ) -------------------------------------------------------------------------------- /orbtk_widgets/assets/themes/redox/theme_redox_fonts.ron: -------------------------------------------------------------------------------- 1 | Theme ( 2 | resources: { 3 | "MEDIUM_FONT": "Roboto-Medium", 4 | "REGULAR_FONT": "Roboto-Regular", 5 | "ICON_FONT": "MaterialIcons-Regular", 6 | "FONT_SIZE_12": 12, 7 | "FONT_SIZE_16": 16, 8 | "FONT_SIZE_24": 24, 9 | "FONT_SIZE_32": 32, 10 | "FONT_SIZE_36": 36, 11 | "ICON_SIZE_12": 12, 12 | "ICON_SIZE_14": 14, 13 | "ICON_SIZE_16": 16, 14 | "ICON_SIZE_18": 18, 15 | }, 16 | ) -------------------------------------------------------------------------------- /orbtk_widgets/src/behaviors/mod.rs: -------------------------------------------------------------------------------- 1 | //! Behavior widgets are provides a default set of event actions like mouse event handling. 2 | //! Use them as child to expand the event behavior of your widget. 3 | 4 | pub use self::mouse_behavior::*; 5 | pub use self::selection_behavior::*; 6 | pub use self::text_behavior::*; 7 | 8 | mod mouse_behavior; 9 | mod selection_behavior; 10 | mod text_behavior; 11 | -------------------------------------------------------------------------------- /orbtk_widgets/src/behaviors/selection_behavior.rs: -------------------------------------------------------------------------------- 1 | use crate::{api::prelude::*, proc_macros::*}; 2 | 3 | /// Used for selection. 4 | pub enum SelectionAction { 5 | ToggleSelection, 6 | } 7 | 8 | /// The `SelectionBehaviorState` handles the `SelectionBehavior` widget. 9 | #[derive(Default, AsAny)] 10 | pub struct SelectionBehaviorState { 11 | target: Entity, 12 | } 13 | 14 | impl State for SelectionBehaviorState { 15 | fn init(&mut self, _: &mut Registry, ctx: &mut Context) { 16 | self.target = (*ctx.widget().get::<u32>("target")).into(); 17 | toggle_flag("selected", &mut ctx.get_widget(self.target)); 18 | ctx.get_widget(self.target).update(false); 19 | } 20 | 21 | fn messages( 22 | &mut self, 23 | mut messages: MessageReader, 24 | _registry: &mut Registry, 25 | ctx: &mut Context, 26 | ) { 27 | for message in messages.read::<SelectionAction>() { 28 | match message { 29 | SelectionAction::ToggleSelection => { 30 | let selected = *ctx.get_widget(self.target).get::<bool>("selected"); 31 | ctx.get_widget(self.target).set("selected", !selected); 32 | toggle_flag("selected", &mut ctx.get_widget(self.target)); 33 | ctx.get_widget(self.target).update(false); 34 | } 35 | }; 36 | } 37 | } 38 | } 39 | 40 | widget!( 41 | /// The `SelectionBehavior` widget will take care to handle the actions, 42 | /// that should be triggered if text regions are marked or selected. 43 | /// 44 | /// **style:** `check_box` 45 | SelectionBehavior<SelectionBehaviorState>: MouseHandler { 46 | /// Sets or shares the target of the behavior. 47 | target: u32, 48 | 49 | /// Sets or shares the selected property. 50 | selected: bool, 51 | 52 | /// Sets the parent id. 53 | parent: u32 54 | } 55 | ); 56 | 57 | impl Template for SelectionBehavior { 58 | fn template(self, id: Entity, _: &mut BuildContext) -> Self { 59 | self.name("SelectionBehavior") 60 | .selected(true) 61 | .on_click(move |ctx, _| { 62 | ctx.send_message(SelectionAction::ToggleSelection, id); 63 | false 64 | }) 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /orbtk_widgets/src/canvas.rs: -------------------------------------------------------------------------------- 1 | use crate::{api::prelude::*, proc_macros::*}; 2 | 3 | widget!( 4 | /// Canvas is used to render 3D graphics. 5 | Canvas { 6 | /// Sets or shares the render pipeline. 7 | render_pipeline: DefaultRenderPipeline 8 | } 9 | ); 10 | 11 | impl Template for Canvas { 12 | fn template(self, _: Entity, _: &mut BuildContext) -> Self { 13 | self.name("Canvas").style("canvas-three") 14 | } 15 | 16 | fn render_object(&self) -> Box<dyn RenderObject> { 17 | PipelineRenderObject.into() 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /orbtk_widgets/src/container.rs: -------------------------------------------------------------------------------- 1 | use crate::{api::prelude::*, proc_macros::*}; 2 | 3 | widget!( 4 | /// The `Container` layout widget surrounds its child with a padding. Draws a box around the child. 5 | Container { 6 | /// Sets or shares the background property. 7 | background: Brush, 8 | 9 | /// Sets or shares the border radius property. 10 | border_radius: f64, 11 | 12 | /// Sets or shares the border thickness property. 13 | border_width: Thickness, 14 | 15 | /// Sets or shares the border brush property. 16 | border_brush: Brush, 17 | 18 | /// Sets or shares the padding property. 19 | padding: Thickness 20 | } 21 | ); 22 | 23 | impl Template for Container { 24 | fn template(self, _: Entity, _: &mut BuildContext) -> Self { 25 | self.name("Container") 26 | .padding(0.0) 27 | .background("transparent") 28 | .border_radius(0.0) 29 | .border_width(0.0) 30 | .border_brush("transparent") 31 | } 32 | 33 | fn render_object(&self) -> Box<dyn RenderObject> { 34 | RectangleRenderObject.into() 35 | } 36 | 37 | fn layout(&self) -> Box<dyn Layout> { 38 | PaddingLayout::new().into() 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /orbtk_widgets/src/cursor.rs: -------------------------------------------------------------------------------- 1 | use crate::{api::prelude::*, proc_macros::*}; 2 | 3 | widget!( 4 | /// The `Cursor` widget represents a text cursor that is used to mark text. 5 | /// 6 | /// **style:** `cursor` 7 | /// 8 | /// # Example 9 | /// 10 | /// Create a cursor and share its text selection. 11 | /// 12 | /// ```rust 13 | /// Cursor::new().selection(id).build(ctx) 14 | /// ``` 15 | Cursor { 16 | /// Defines the selection of that cursor that is shared with the `TextBehavior`. 17 | selection: TextSelection, 18 | 19 | /// Defines the background of the whole selection range. 20 | background: Brush, 21 | 22 | /// Defines the brush of the text selection indicator. 23 | border_brush: Brush, 24 | 25 | /// Defines the with of the text selection indicator. 26 | border_width: Thickness, 27 | 28 | // Defines the opacity of the background of the selection range. 29 | background_opacity: f32, 30 | 31 | /// Defines the current width of the selection. 32 | selection_width: f64, 33 | 34 | /// Defines the start position of the current selection. 35 | selection_x: f64, 36 | 37 | /// Defines the x position of the cursor. 38 | cursor_x: f64, 39 | 40 | /// Defines the of the cursor. 41 | offset: f64 42 | } 43 | ); 44 | 45 | impl Template for Cursor { 46 | fn template(self, _: Entity, _: &mut BuildContext) -> Self { 47 | self.name("Cursor") 48 | .style("cursor") 49 | .background_opacity(0.3) 50 | .background("transparent") 51 | .h_align("stretch") 52 | } 53 | 54 | fn render_object(&self) -> Box<dyn RenderObject> { 55 | CursorRenderObject.into() 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /orbtk_widgets/src/font_icon_block.rs: -------------------------------------------------------------------------------- 1 | use crate::{api::prelude::*, proc_macros::*, themes::theme_orbtk::*}; 2 | 3 | widget!( 4 | /// The `FontIconBlock` widget is used to draw text. It is not interactive. 5 | /// 6 | /// **style:** `font-icon-block` 7 | FontIconBlock { 8 | /// Sets or shares the icon property. 9 | icon: String, 10 | 11 | /// Sets or shares the icon brush property. 12 | icon_brush: Brush, 13 | 14 | /// Sets or share the icon font size property. 15 | icon_size: f64, 16 | 17 | /// Sets or shares the icon font property. 18 | icon_font: String 19 | } 20 | ); 21 | 22 | impl Template for FontIconBlock { 23 | fn template(self, _: Entity, _: &mut BuildContext) -> Self { 24 | self.name("FontIconBlock") 25 | .style("font-icon-block") 26 | .icon("") 27 | .icon_brush(colors::LINK_WATER_COLOR) 28 | .icon_size(orbtk_fonts::ICON_FONT_SIZE_12) 29 | .icon_font("MaterialIcons-Regular") 30 | } 31 | 32 | fn render_object(&self) -> Box<dyn RenderObject> { 33 | FontIconRenderObject.into() 34 | } 35 | 36 | fn layout(&self) -> Box<dyn Layout> { 37 | FixedSizeLayout::new().into() 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /orbtk_widgets/src/grid.rs: -------------------------------------------------------------------------------- 1 | use crate::{api::prelude::*, proc_macros::*}; 2 | 3 | widget!( 4 | /// The `Grid` defines a flexible grid area that consists of columns and rows. 5 | /// 6 | /// **style:** `grid` 7 | Grid { 8 | /// Sets or shares the background property. 9 | background: Brush, 10 | 11 | /// Sets or shares the columns property. 12 | columns: Blocks, 13 | 14 | /// Sets or shares the rows property. 15 | rows: Blocks, 16 | 17 | /// Sets or shares the border radius property. 18 | border_radius: f64 19 | 20 | attached_properties: { 21 | /// Attach a column position to a widget. 22 | column: usize, 23 | 24 | /// Attach a column span to a widget. 25 | column_span: usize, 26 | 27 | /// Attach a row position to a widget. 28 | row: usize, 29 | 30 | /// Attach a row span to a widget. 31 | row_span: usize 32 | } 33 | } 34 | ); 35 | 36 | impl Grid { 37 | /// Sets column and row to the given widget and add it as child. 38 | pub fn place<W>(self, ctx: &mut BuildContext, child: W, column: usize, row: usize) -> Self 39 | where 40 | W: Widget, 41 | { 42 | self.child( 43 | child 44 | .attach(Grid::column(column)) 45 | .attach(Grid::row(row)) 46 | .build(ctx), 47 | ) 48 | } 49 | } 50 | 51 | impl Template for Grid { 52 | fn template(self, _: Entity, _: &mut BuildContext) -> Self { 53 | self.name("Grid") 54 | .style("grid") 55 | .border_radius(0.0) 56 | .background("transparent") 57 | .rows(Blocks::default()) 58 | .columns(Blocks::default()) 59 | } 60 | 61 | fn render_object(&self) -> Box<dyn RenderObject> { 62 | RectangleRenderObject.into() 63 | } 64 | 65 | fn layout(&self) -> Box<dyn Layout> { 66 | GridLayout::new().into() 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /orbtk_widgets/src/image_widget.rs: -------------------------------------------------------------------------------- 1 | use crate::{api::prelude::*, proc_macros::*, render::prelude::*}; 2 | 3 | widget!( 4 | /// The `ImageWidget` widget is used to draw an image. It is not interactive. 5 | /// 6 | /// **style:** `image-widget` 7 | ImageWidget { 8 | /// Sets or shares the image property. 9 | /// 10 | /// Set image property: 11 | /// * &str: `ImageWidget::new().image("path/to/image.png").build(xt)` 12 | /// * String: `ImageWidget::new().image(String::from()).build(xt)` 13 | /// * (width: u32, height: u32, data: Vec<u32>): `ImageWidget::new().image((width, height, vec![0; width * height]));` 14 | image: Image 15 | } 16 | ); 17 | 18 | impl Template for ImageWidget { 19 | fn template(self, _: Entity, _: &mut BuildContext) -> Self { 20 | self.name("ImageWidget").style("image-widget").image("") 21 | } 22 | 23 | fn render_object(&self) -> Box<dyn RenderObject> { 24 | ImageRenderObject.into() 25 | } 26 | 27 | fn layout(&self) -> Box<dyn Layout> { 28 | FixedSizeLayout::new().into() 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /orbtk_widgets/src/lib.rs: -------------------------------------------------------------------------------- 1 | /*! 2 | Base OrbTk widget library. 3 | */ 4 | 5 | /// This module pre-selects commonly used OrbTk crates and put them into scope. 6 | pub mod prelude; 7 | 8 | pub(crate) use orbtk_core as api; 9 | pub(crate) use orbtk_orbclient as shell; 10 | pub(crate) use orbtk_proc_macros as proc_macros; 11 | pub(crate) use orbtk_tinyskia as render; 12 | 13 | pub use self::button::*; 14 | pub use self::canvas::*; 15 | pub use self::check_box::*; 16 | pub use self::combo_box::*; 17 | pub use self::container::*; 18 | pub use self::cursor::*; 19 | pub use self::font_icon_block::*; 20 | pub use self::grid::*; 21 | pub use self::image_widget::*; 22 | pub use self::items_widget::*; 23 | pub use self::list_view::*; 24 | pub use self::master_detail::*; 25 | pub use self::numeric_box::*; 26 | pub use self::pager::*; 27 | pub use self::password_box::*; 28 | pub use self::popup::*; 29 | pub use self::progress_bar::*; 30 | pub use self::scroll_bar::*; 31 | pub use self::scroll_indicator::*; 32 | pub use self::scroll_viewer::*; 33 | pub use self::slider::*; 34 | pub use self::stack::*; 35 | pub use self::switch::*; 36 | pub use self::tab_widget::*; 37 | pub use self::text_block::*; 38 | pub use self::text_box::*; 39 | pub use self::toggle_button::*; 40 | pub use self::window::*; 41 | 42 | pub mod behaviors; 43 | mod button; 44 | mod canvas; 45 | mod check_box; 46 | mod combo_box; 47 | mod container; 48 | mod cursor; 49 | mod font_icon_block; 50 | mod grid; 51 | mod image_widget; 52 | mod items_widget; 53 | mod list_view; 54 | mod master_detail; 55 | mod numeric_box; 56 | mod pager; 57 | mod password_box; 58 | mod popup; 59 | mod progress_bar; 60 | mod scroll_bar; 61 | mod scroll_indicator; 62 | mod scroll_viewer; 63 | mod slider; 64 | mod stack; 65 | mod switch; 66 | mod tab_widget; 67 | mod text_block; 68 | mod text_box; 69 | pub mod themes; 70 | mod toggle_button; 71 | mod window; 72 | -------------------------------------------------------------------------------- /orbtk_widgets/src/popup.rs: -------------------------------------------------------------------------------- 1 | use crate::{api::prelude::*, proc_macros::*}; 2 | 3 | /// The `PopupState` handles the open and close behavior of the `Popup` widget. 4 | #[derive(AsAny, Default)] 5 | pub struct PopupState { 6 | //actions: Vec<PopupAction>, 7 | } 8 | 9 | impl PopupState {} 10 | 11 | impl State for PopupState {} 12 | 13 | widget!( 14 | /// The `Popup` is used to presents content bound to target entity. 15 | /// 16 | /// The `target` is specified either via its widget id (`Entitiy`) 17 | /// or using a point coordinate (`Point`). The placmement of the 18 | /// popup widget itself is controlled via its `placement` 19 | /// property. An optional attribute (float), defines the 20 | /// margin between the target and the popup widget. 21 | /// 22 | /// [`placement`]: ../orbtk_core/render_object/enum.Placement.html 23 | /// 24 | /// **style:** `popup`` 25 | Popup<PopupState> : KeyDownHandler, MouseHandler { 26 | /// Sets or shares the background property. 27 | background: Brush, 28 | 29 | /// Sets or shares the border brush property. 30 | border_brush: Brush, 31 | 32 | /// Sets or shares the border radius property. 33 | border_radius: f64, 34 | 35 | /// Sets or shares the border thickness property. 36 | border_width: Thickness, 37 | 38 | /// Sets or shares the popup open state. 39 | open: bool, 40 | 41 | /// Sets or shares the padding property. 42 | padding: Thickness, 43 | 44 | /// Sets or shares the placement property relative to the 45 | /// target position. Valid placement variants are defined via 46 | /// the `Placement` enumeration. 47 | placement: Placement, 48 | 49 | /// Sets or shares the offset property that assignes a margin 50 | /// between popup and target entity. 51 | offset: f64, 52 | 53 | /// 54 | /// Defined ether as an entity (Entity), or as a point 55 | /// coordinate (Point). 56 | target: PopupTarget 57 | } 58 | ); 59 | 60 | impl Template for Popup { 61 | fn template(self, _id: Entity, _: &mut BuildContext) -> Self { 62 | self.name("Popup").style("popup").open(false) 63 | } 64 | 65 | fn render_object(&self) -> Box<dyn RenderObject> { 66 | Box::new(PopupRenderObject::new()) 67 | } 68 | 69 | fn layout(&self) -> Box<dyn Layout> { 70 | PopupLayout::new().into() 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /orbtk_widgets/src/prelude.rs: -------------------------------------------------------------------------------- 1 | pub use std::{ 2 | any::{Any, TypeId}, 3 | cell::RefCell, 4 | collections::{HashMap, HashSet}, 5 | fmt::Debug, 6 | rc::Rc, 7 | }; 8 | 9 | pub use crate::*; 10 | -------------------------------------------------------------------------------- /orbtk_widgets/src/scroll_bar.rs: -------------------------------------------------------------------------------- 1 | use crate::{api::prelude::*, proc_macros::*}; 2 | 3 | // --- KEYS -- 4 | 5 | pub static STYLE_SCROLL_BAR: &str = "scroll_bar"; 6 | 7 | // --- KEYS -- 8 | 9 | widget!( 10 | /// The `ScrollBar` widget represents a position inside of a scroll container. 11 | /// 12 | /// **style:** `scroll_bar` 13 | ScrollBar { 14 | /// Sets or shares the background property. 15 | background: Brush, 16 | 17 | /// Sets or shares the border radius property. 18 | border_radius: f64 19 | } 20 | ); 21 | 22 | impl Template for ScrollBar { 23 | fn template(self, _: Entity, _: &mut BuildContext) -> Self { 24 | self.name("ScrollBar") 25 | .style(STYLE_SCROLL_BAR) 26 | .width(4.0) 27 | .border_radius(2.0) 28 | .background("#647b91") 29 | } 30 | 31 | fn render_object(&self) -> Box<dyn RenderObject> { 32 | RectangleRenderObject.into() 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /orbtk_widgets/src/stack.rs: -------------------------------------------------------------------------------- 1 | use crate::{api::prelude::*, proc_macros::*}; 2 | 3 | widget!( 4 | /// The `Stack` defines a layout that is used to stack its children vertical or horizontal. 5 | /// 6 | /// **style:** `stack` 7 | Stack { 8 | /// Sets or shares the orientation property. 9 | orientation: Orientation, 10 | 11 | /// Margin between widgets in the stack. 12 | spacing: f64 13 | } 14 | ); 15 | 16 | impl Template for Stack { 17 | fn template(self, _: Entity, _: &mut BuildContext) -> Self { 18 | self.name("Stack").orientation("vertical").style("stack") 19 | } 20 | 21 | fn layout(&self) -> Box<dyn Layout> { 22 | Box::new(StackLayout::new()) 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /orbtk_widgets/src/text_block.rs: -------------------------------------------------------------------------------- 1 | use crate::{api::prelude::*, proc_macros::*, themes::theme_orbtk::*}; 2 | 3 | enum TextAction { 4 | Localize, 5 | } 6 | 7 | /// Handles the localization of the text. 8 | #[derive(Debug, Clone, Default, AsAny)] 9 | pub struct TextBlockState; 10 | 11 | impl TextBlockState { 12 | fn localize(&self, ctx: &mut Context) { 13 | if !*TextBlock::localizable_ref(&ctx.widget()) { 14 | return; 15 | } 16 | 17 | let text = TextBlock::text_clone(&ctx.widget()); 18 | let localized_text = ctx.localize_text(text); 19 | 20 | TextBlock::localized_text_set(&mut ctx.widget(), localized_text); 21 | } 22 | } 23 | 24 | impl State for TextBlockState { 25 | fn init(&mut self, _registry: &mut Registry, ctx: &mut Context) { 26 | self.localize(ctx); 27 | } 28 | 29 | fn update(&mut self, _registry: &mut Registry, ctx: &mut Context) { 30 | self.localize(ctx); 31 | } 32 | 33 | fn messages( 34 | &mut self, 35 | mut messages: MessageReader, 36 | _registry: &mut Registry, 37 | ctx: &mut Context, 38 | ) { 39 | for message in messages.read::<TextAction>() { 40 | match message { 41 | TextAction::Localize => self.localize(ctx), 42 | } 43 | } 44 | } 45 | } 46 | 47 | widget!( 48 | /// The `TextBlock` widget is used to draw text. It is not interactive. 49 | /// 50 | /// **style:** `text-block` 51 | TextBlock<TextBlockState> { 52 | /// Sets or shares the text property. 53 | text: String, 54 | 55 | /// If the `TextBlock` is localizable and the localized text is not empty, the localized_text will be drawn. 56 | localized_text: String, 57 | 58 | /// Sets or shares the water_mark text property. 59 | water_mark: String, 60 | 61 | /// Sets or shares the foreground property. 62 | foreground: Brush, 63 | 64 | /// Sets or shares the font size property. 65 | font_size: f64, 66 | 67 | /// Sets or shares the font property. 68 | font: String, 69 | 70 | /// Defines an extra offset that can be used to the text on x axis. 71 | offset: f64, 72 | 73 | /// Defines if the text is localizable. If set to `false` the text will not be localized. 74 | localizable: bool 75 | } 76 | ); 77 | 78 | impl Template for TextBlock { 79 | fn template(self, id: Entity, _: &mut BuildContext) -> Self { 80 | self.name("TextBlock") 81 | .text("") 82 | .foreground(colors::LINK_WATER_COLOR) 83 | .font_size(orbtk_fonts::FONT_SIZE_12) 84 | .font("Roboto-Regular") 85 | .localizable(true) 86 | .on_changed("text", move |ctx, _| { 87 | ctx.send_message(TextAction::Localize, id) 88 | }) 89 | } 90 | 91 | fn render_object(&self) -> Box<dyn RenderObject> { 92 | TextRenderObject.into() 93 | } 94 | 95 | fn layout(&self) -> Box<dyn Layout> { 96 | FixedSizeLayout::new().into() 97 | } 98 | } 99 | -------------------------------------------------------------------------------- /orbtk_widgets/src/themes/mod.rs: -------------------------------------------------------------------------------- 1 | //! Theme widgets provide fonts, icons and colors handling 2 | //! that can refine the look and feel of an application at runtime. 3 | 4 | pub mod theme_fluent; 5 | pub mod theme_orbtk; 6 | pub mod theme_redox; 7 | 8 | pub use theme_fluent::*; 9 | pub use theme_orbtk::*; 10 | pub use theme_redox::*; 11 | -------------------------------------------------------------------------------- /orbtk_widgets/src/themes/theme_fluent/fluent_fonts.rs: -------------------------------------------------------------------------------- 1 | /// The `regular` variant of the `MDL2 Icons` asset font family. 2 | pub const MDL2_ICONS_FONT: &[u8] = 3 | include_bytes!("../../../assets/fonts/mdl2/mdl2_assets_font.ron"); 4 | 5 | /// The `regular` variant of the `Selawik UI` font family. 6 | pub const SELAWIK_REGULAR_FONT: &[u8] = 7 | include_bytes!("../../../assets/fonts/selawik/Selawik-Regular.otf"); 8 | 9 | /// The `bold` variant of the `Selawik UI` font family. 10 | pub const SELAWIK_BOLD_FONT: &[u8] = 11 | include_bytes!("../../../assets/fonts/selawik/Selawik-Bold.otf"); 12 | 13 | /// The `light` variant of the `Selawik UI` font family. 14 | pub const SELAWIK_LIGHT_FONT: &[u8] = 15 | include_bytes!("../../../assets/fonts/selawik/Selawik-Light.otf"); 16 | -------------------------------------------------------------------------------- /orbtk_widgets/src/themes/theme_fluent/mod.rs: -------------------------------------------------------------------------------- 1 | /*! 2 | 3 | This submodule provides the default theme resources of OrbTks fluent theme dark and light. 4 | It provides fonts, icons and colors. 5 | 6 | */ 7 | use orbtk_core::theming::*; 8 | 9 | /// provides `constants` associated to fonts. 10 | pub mod fluent_fonts; 11 | 12 | /// provides information processed by the `graphic render` (e.g. glyphs, icons). 13 | pub mod mdl2_assets_font; 14 | 15 | /// Resource file of default theme 16 | pub const THEME_FLUENT: &str = include_str!("../../../assets/themes/fluent/theme_fluent.ron"); 17 | 18 | /// The default dark theme colors resource file. 19 | pub const THEME_FLUENT_COLORS_DARK: &str = 20 | include_str!("../../../assets/themes/fluent/theme_fluent_colors_dark.ron"); 21 | 22 | /// The default light theme colors resource file. 23 | pub const THEME_FLUENT_COLORS_LIGHT: &str = 24 | include_str!("../../../assets/themes/fluent/theme_fluent_colors_light.ron"); 25 | 26 | /// The font resources of the Fluent theme. 27 | pub const THEME_FLUENT_FONTS: &str = 28 | include_str!("../../../assets/themes/fluent/theme_fluent_fonts.ron"); 29 | 30 | /// Segeo Icon Font map. 31 | pub const MDL2_ASSETS_ICONS: &str = include_str!("../../../assets/fonts/mdl2/mdl2_assets_font.ron"); 32 | 33 | /// Returns the fluent OrbTk theme. 34 | pub fn theme_fluent() -> Theme { 35 | theme_fluent_dark() 36 | } 37 | 38 | /// Creates OrbTks fluent dark theme. 39 | pub fn theme_fluent_dark() -> Theme { 40 | register_fluent_fonts(Theme::from_config( 41 | ThemeConfig::from(THEME_FLUENT) 42 | .extend(ThemeConfig::from(THEME_FLUENT_COLORS_DARK)) 43 | .extend(ThemeConfig::from(THEME_FLUENT_FONTS)) 44 | .extend(ThemeConfig::from(MDL2_ASSETS_ICONS)), 45 | )) 46 | } 47 | 48 | /// Creates OrbTks fluent light theme. 49 | pub fn theme_fluent_light() -> Theme { 50 | register_fluent_fonts(Theme::from_config( 51 | ThemeConfig::from(THEME_FLUENT) 52 | .extend(ThemeConfig::from(THEME_FLUENT_COLORS_LIGHT)) 53 | .extend(ThemeConfig::from(THEME_FLUENT_FONTS)) 54 | .extend(ThemeConfig::from(MDL2_ASSETS_ICONS)), 55 | )) 56 | } 57 | 58 | /// Register roboto and material icon fonts to the given theme. 59 | #[cfg(not(target_arch = "wasm32"))] 60 | pub fn register_fluent_fonts(theme: Theme) -> Theme { 61 | theme 62 | .register_font("Selawik-Regular", fluent_fonts::SELAWIK_REGULAR_FONT) 63 | .register_font("Selawik-Bold", fluent_fonts::SELAWIK_BOLD_FONT) 64 | .register_font("Selawik-Light", fluent_fonts::SELAWIK_LIGHT_FONT) 65 | .register_font("MDL2-Assets-Regular", fluent_fonts::MDL2_ICONS_FONT) 66 | // register also material icon fonts because OrbTk's default widget library relies on its availability. 67 | .register_font( 68 | "MaterialIcons-Regular", 69 | super::theme_orbtk::orbtk_fonts::MATERIAL_ICONS_FONT, 70 | ) 71 | } 72 | 73 | /// Dummy implementation for web to be compatible to other platforms. 74 | #[cfg(target_arch = "wasm32")] 75 | pub fn register_fluent_fonts(theme: Theme) -> Theme { 76 | theme 77 | } 78 | -------------------------------------------------------------------------------- /orbtk_widgets/src/themes/theme_orbtk/colors.rs: -------------------------------------------------------------------------------- 1 | pub const LINK_WATER_COLOR: &str = "#dfebf5"; 2 | pub const LYNCH_COLOR: &str = "#647b91"; 3 | pub const BOMBAY_COLOR: &str = "#adb3B8"; 4 | pub const SLATE_GRAY_COLOR: &str = "#6c7a90"; 5 | pub const BRIGHT_GRAY_COLOR: &str = "#3b434a"; 6 | -------------------------------------------------------------------------------- /orbtk_widgets/src/themes/theme_orbtk/mod.rs: -------------------------------------------------------------------------------- 1 | /*! 2 | 3 | This submodule provides the default theme resources of OrbTks default theme dark and light. 4 | It provides fonts, icons and colors. 5 | 6 | */ 7 | 8 | use orbtk_core::theming::*; 9 | 10 | /// provides `constants` to reference colors. 11 | pub mod colors; 12 | /// provides `constants` associated to fonts. 13 | pub mod orbtk_fonts; 14 | 15 | /// provides information processed by the `graphic render` (e.g. glyphs, icons). 16 | pub mod material_icons_font; 17 | 18 | /// Resource file of default theme. 19 | pub const THEME_DEFAULT: &str = include_str!("../../../assets/themes/orbtk/theme_default.ron"); 20 | 21 | /// The default dark theme colors resource file. 22 | pub const THEME_DEFAULT_COLORS_DARK: &str = 23 | include_str!("../../../assets/themes/orbtk/theme_default_colors_dark.ron"); 24 | 25 | /// The default light theme colors resource file. 26 | pub const THEME_DEFAULT_COLORS_LIGHT: &str = 27 | include_str!("../../../assets/themes/orbtk/theme_default_colors_light.ron"); 28 | 29 | /// The font resources of the default theme 30 | pub const THEME_DEFAULT_FONTS: &str = 31 | include_str!("../../../assets/themes/orbtk/theme_default_fonts.ron"); 32 | 33 | /// Segeo Icon Font map. 34 | pub const MATERIAL_ICONS: &str = 35 | include_str!("../../../assets/fonts/material/material_icons_font.ron"); 36 | 37 | /// Returns the default OrbTk theme. 38 | pub fn theme_default() -> Theme { 39 | theme_default_dark() 40 | } 41 | 42 | /// Creates OrbTks default dark theme. 43 | pub fn theme_default_dark() -> Theme { 44 | register_default_fonts(Theme::from_config( 45 | ThemeConfig::from(THEME_DEFAULT) 46 | .extend(ThemeConfig::from(THEME_DEFAULT_COLORS_DARK)) 47 | .extend(ThemeConfig::from(THEME_DEFAULT_FONTS)) 48 | .extend(ThemeConfig::from(MATERIAL_ICONS)), 49 | )) 50 | } 51 | 52 | /// Creates OrbTks default light theme. 53 | pub fn theme_default_light() -> Theme { 54 | register_default_fonts(Theme::from_config( 55 | ThemeConfig::from(THEME_DEFAULT) 56 | .extend(ThemeConfig::from(THEME_DEFAULT_COLORS_LIGHT)) 57 | .extend(ThemeConfig::from(THEME_DEFAULT_FONTS)) 58 | .extend(ThemeConfig::from(MATERIAL_ICONS)), 59 | )) 60 | } 61 | 62 | /// Register roboto and material icon fonts to the given theme. 63 | #[cfg(not(target_arch = "wasm32"))] 64 | pub fn register_default_fonts(theme: Theme) -> Theme { 65 | theme 66 | .register_font("Roboto-Regular", orbtk_fonts::ROBOTO_REGULAR_FONT) 67 | .register_font("Roboto-Medium", orbtk_fonts::ROBOTO_MEDIUM_FONT) 68 | .register_font("MaterialIcons-Regular", orbtk_fonts::MATERIAL_ICONS_FONT) 69 | } 70 | 71 | /// Dummy implementation for web to be compatible to other platforms. 72 | #[cfg(target_arch = "wasm32")] 73 | pub fn register_default_fonts(theme: Theme) -> Theme { 74 | theme 75 | } 76 | -------------------------------------------------------------------------------- /orbtk_widgets/src/themes/theme_orbtk/orbtk_fonts.rs: -------------------------------------------------------------------------------- 1 | // The 'Material Design Icons' provide an icon set developed by Google. 2 | // Desing guidelines document the underlying structure. 3 | // orbtk uses an activly maintained successor project called 'Material Icons'. 4 | // Supported are 'ttf' and 'woff2' fonts. 5 | 6 | // Project root: https://github.com/material-icons/material-icons-font 7 | // Package-Info: package.json 8 | 9 | // TL;DR 10 | // https://stackoverflow.com/questions/11002820/why-should-we-include-ttf-eot-woff-svg-in-a-font-face 11 | // ... woff2 gets drafted and accepted, which improves the compression 12 | // leading to even smaller files, along with the ability to load a single font 13 | // "in parts" so that a font that supports 20 scripts can be stored as "chunks" 14 | // on disk instead, with browsers automatically able to load the font "in parts" 15 | // as needed, rather than needing to transfer the entire font up front, further 16 | // improving the typesetting experience. 17 | // If you don't want to support IE 8 and lower, and iOS 4 and lower, 18 | // and android 4.3 or earlier, then you can just use WOFF 19 | // (and WOFF2, a more highly compressed WOFF, for the newest browsers that support it.) 20 | 21 | /// The `regular` TrueType font, offering glyphs maintained in the `Material Incos Font` project. 22 | pub const MATERIAL_ICONS_FONT: &[u8] = 23 | include_bytes!("../../../assets/fonts/material/MaterialIcons.ttf"); 24 | 25 | /// The baseline variant of the woff2 encoded font, offering glyphs maintained in the `Material Incos Font` project. 26 | pub const MATERIAL_ICONS_BASELINE_FONT: &[u8] = 27 | include_bytes!("../../../assets/fonts/material/MaterialIcons-Baseline.woff2"); 28 | 29 | /// The 'outlined' variant of the woff2 encoded font, offering glyphs maintained in the `Material Incos Font` project. 30 | pub const MATERIAL_ICONS_OUTLINED_FONT: &[u8] = 31 | include_bytes!("../../../assets/fonts/material/MaterialIcons-Outlined.woff2"); 32 | 33 | /// The `round` variant of the woff2 encoded font, offering glyphs maintained in the `Material Incos Font` project. 34 | pub const MATERIAL_ICONS_ROUND_FONT: &[u8] = 35 | include_bytes!("../../../assets/fonts/material/MaterialIcons-Round.woff2"); 36 | 37 | /// The `sharp` variant of the woff2 encoded font, offering glyphs maintained in the `Material Incos Font` project. 38 | pub const MATERIAL_ICONS_SHARP_FONT: &[u8] = 39 | include_bytes!("../../../assets/fonts/material/MaterialIcons-Sharp.woff2"); 40 | 41 | /// The `twotone` variant of the woff2 encoded font, offering glyphs maintained in the `Material Incos Font` project. 42 | pub const MATERIAL_ICONS_TWOTONE_FONT: &[u8] = 43 | include_bytes!("../../../assets/fonts/material/MaterialIcons-TwoTone.woff2"); 44 | 45 | /// The `regular` variant of the `Roboto` font family. 46 | pub const ROBOTO_REGULAR_FONT: &[u8] = 47 | include_bytes!("../../../assets/fonts/roboto/Roboto-Regular.ttf"); 48 | 49 | /// The `medium` variant of the `Roboto` font family. 50 | pub const ROBOTO_MEDIUM_FONT: &[u8] = 51 | include_bytes!("../../../assets/fonts/roboto/Roboto-Medium.ttf"); 52 | 53 | /// Use fixed `font size` of 12pt. 54 | pub const FONT_SIZE_12: f64 = 12.0; 55 | 56 | /// Use fixed `icon font size` of 12pt. 57 | pub const ICON_FONT_SIZE_12: f64 = 12.0; 58 | -------------------------------------------------------------------------------- /orbtk_widgets/src/themes/theme_redox.rs: -------------------------------------------------------------------------------- 1 | /*! 2 | 3 | This submodule provides the default theme resources of OrbTks redox theme dark and light. 4 | It provides fonts, icons and colors. 5 | 6 | */ 7 | use orbtk_core::theming::*; 8 | 9 | use super::theme_orbtk::{register_default_fonts, MATERIAL_ICONS}; 10 | 11 | /// Resource file of redox theme. 12 | pub const THEME_REDOX: &str = include_str!("../../assets/themes/redox/theme_redox.ron"); 13 | 14 | /// The redox dark theme colors resource file. 15 | pub const THEME_REDOX_COLORS_DARK: &str = 16 | include_str!("../../assets/themes/redox/theme_redox_colors.ron"); 17 | 18 | /// The font resources of the redox theme. 19 | pub const THEME_REDOX_FONTS: &str = include_str!("../../assets/themes/redox/theme_redox_fonts.ron"); 20 | 21 | /// Returns the redox OrbTk theme. 22 | pub fn theme_redox() -> Theme { 23 | register_default_fonts(Theme::from_config( 24 | ThemeConfig::from(THEME_REDOX) 25 | .extend(ThemeConfig::from(THEME_REDOX_COLORS_DARK)) 26 | .extend(ThemeConfig::from(THEME_REDOX_FONTS)) 27 | .extend(ThemeConfig::from(MATERIAL_ICONS)), 28 | )) 29 | } 30 | -------------------------------------------------------------------------------- /policies/code-of-conduct.md: -------------------------------------------------------------------------------- 1 | # Conduct 2 | 3 | * We are committed to providing a friendly, safe and welcoming environment for 4 | all, regardless of level of experience, gender identity and expression, sexual 5 | orientation, disability, personal appearance, body size, race, ethnicity, age, 6 | religion, nationality, or other similar characteristic. 7 | * Please avoid using overtly sexual aliases or other nicknames that might 8 | detract from a friendly, safe and welcoming environment for all. 9 | * Please be kind and courteous. There’s no need to be mean or rude. 10 | * Respect that people have differences of opinion and that every design or 11 | implementation choice carries a trade-off and numerous costs. There is seldom 12 | a right answer. 13 | * Please keep unstructured critique to a minimum. If you have solid ideas you 14 | want to experiment with, make a fork and see how it works. 15 | * We will exclude you from interaction if you insult, demean or harass anyone. 16 | That is not welcome behavior. We interpret the term “harassment” as including 17 | the definition in the Citizen Code of Conduct; if you have any lack of clarity 18 | about what might be included in that concept, please read their definition. In 19 | particular, we don’t tolerate behavior that excludes people in socially 20 | marginalized groups. 21 | * Private harassment is also unacceptable. No matter who you are, if you feel 22 | you have been or are being harassed or made uncomfortable by a community 23 | member, please contact one of the channel ops or any of the Rust moderation 24 | team immediately. Whether you’re a regular contributor or a newcomer, we care 25 | about making this community a safe place for you and we’ve got your back. 26 | * Likewise any spamming, trolling, flaming, baiting or other attention-stealing 27 | behavior is not welcome. 28 | -------------------------------------------------------------------------------- /proc_macros/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | authors = ["Florian Blasius <flovanpt@posteo.de>"] 3 | description = "Procedural macros used by OrbTk." 4 | edition = "2021" 5 | license = "MIT" 6 | keywords = ["ui", "api"] 7 | name = "orbtk_proc_macros" 8 | readme = "README.md" 9 | repository = "https://github.com/redox-os/orbtk" 10 | version = "0.3.1-alpha5" 11 | 12 | [lib] 13 | proc-macro = true 14 | 15 | [dependencies] 16 | quote = "1.0" 17 | syn = "1.0" 18 | proc-macro2 = "1.0" 19 | case = "1.0" 20 | 21 | [features] 22 | debug = [] -------------------------------------------------------------------------------- /proc_macros/README.md: -------------------------------------------------------------------------------- 1 | # orbtk_proc_macros 2 | 3 | Provides procedural helper macros. It's part of [OrbTk](https://gitlab.redox-os.org/redox-os/orbtk) - The Rust UI-Toolkit. 4 | 5 | [](https://github.com/redox-os/orbtk/actions) 6 | [](../../LICENSE) 7 | 8 | ## Dependencies 9 | 10 | * [dces](https://gitlab.redox-os.org/redox-os/dces-rust) (MIT): Entity Component System 11 | 12 | ## License 13 | 14 | Licensed under MIT license ([LICENSE](../../LICENSE)). -------------------------------------------------------------------------------- /utils/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | authors = ["Florian Blasius <flovanpt@posteo.de>"] 3 | description = "Helper utils and traits for OrbTk." 4 | edition = "2021" 5 | keywords = ["helpers", "utils", "traits", "ui"] 6 | license = "MIT" 7 | name = "orbtk_utils" 8 | readme = "README.md" 9 | repository = "https://github.com/redox-os/orbtk" 10 | version = "0.3.1-alpha5" 11 | 12 | [dependencies] 13 | ron = { version = "0.8" } 14 | serde = { version = "1.0" } 15 | derive_more = { version = "0.99", default-features = false, features = ["add", "constructor", "from", "mul"] } 16 | lexical-core = { version = "0.8" } 17 | phf = { version = "0.10", default-features = false } 18 | 19 | [build-dependencies] 20 | phf_codegen = { version = "0.11", default-features = false } -------------------------------------------------------------------------------- /utils/README.md: -------------------------------------------------------------------------------- 1 | # orbtk_utils 2 | 3 | Base structs like point, brush, border, ... . It's part of [OrbTk](https://github.com/redox-os/orbtk) - The Rust UI-Toolkit. 4 | 5 | [](https://github.com/redox-os/orbtk/actions) 6 | [](../../LICENSE) 7 | 8 | 9 | ## License 10 | 11 | Licensed under MIT license ([LICENSE](../../LICENSE)). -------------------------------------------------------------------------------- /utils/build.rs: -------------------------------------------------------------------------------- 1 | use std::env; 2 | use std::error::Error; 3 | use std::fs::File; 4 | use std::io::{BufRead, BufReader, BufWriter, Write}; 5 | use std::path::{Path, PathBuf}; 6 | 7 | fn main() -> Result<(), Box<dyn Error>> { 8 | println!("cargo:rerun-if-changed=colors.txt"); 9 | let path = Path::new(&env::var("OUT_DIR").unwrap()).join("colors.rs"); 10 | let mut file = BufWriter::new(File::create(&path).unwrap()); 11 | 12 | let rdr = BufReader::new(File::open( 13 | PathBuf::from(env::var("CARGO_MANIFEST_DIR").unwrap()).join("colors.txt"), 14 | )?); 15 | 16 | let mut map = phf_codegen::Map::new(); 17 | let mut recs: Vec<(String, String)> = Vec::new(); 18 | 19 | for result in rdr.lines() { 20 | let record = result?; 21 | let record = record.split("//").next().unwrap().trim(); 22 | if record.is_empty() { 23 | continue; 24 | } 25 | let mut record = record.split_whitespace(); 26 | let id = record.next().unwrap().to_owned(); 27 | let hex = record.next().unwrap().to_owned(); 28 | let clean_hex = hex.trim_start_matches('#'); 29 | let color = match clean_hex.len() { 30 | 3 => { 31 | let d = u32::from_str_radix(clean_hex, 16).unwrap_or(0); 32 | 33 | let mut r = (d & 0xF) << 4; 34 | let mut g = ((d >> 4) & 0xF) << 4; 35 | let mut b = ((d >> 8) & 0xF) << 4; 36 | r |= r >> 4; 37 | g |= g >> 4; 38 | b |= b >> 4; 39 | 40 | format!("Color::rgb({}, {}, {})", r, g, b) 41 | } 42 | 6 => { 43 | let x = u32::from_str_radix(clean_hex, 16).unwrap_or(0); 44 | 45 | format!( 46 | "Color::rgb({}, {}, {})", 47 | ((x >> 16) & 0xFF) as u8, 48 | ((x >> 8) & 0xFF) as u8, 49 | (x & 0xFF) as u8 50 | ) 51 | } 52 | _ => panic!(""), 53 | }; 54 | recs.push((id, color)); 55 | } 56 | 57 | for rec in recs.iter() { 58 | map.entry(&*rec.0, &rec.1); 59 | } 60 | 61 | map.entry("transparent", "Color::rgba(0, 0, 0, 0)"); 62 | 63 | writeln!( 64 | &mut file, 65 | "static COLORS: phf::Map<&'static str, Color> = \n{};\n", 66 | map.build() 67 | ) 68 | .unwrap(); 69 | 70 | Ok(()) 71 | } 72 | -------------------------------------------------------------------------------- /utils/src/angle.rs: -------------------------------------------------------------------------------- 1 | use derive_more::{Add, Div, From, Mul, Sub}; 2 | use std::f64::consts::{PI, TAU}; 3 | 4 | /// The OrbTk way to handle angles 5 | #[derive(Add, Sub, Copy, From, Clone, Debug, PartialEq, Mul, Div)] 6 | pub struct Angle(f64); 7 | 8 | impl Angle { 9 | // TODO: Make this const fns, when the floating point operations on const fns become stable 10 | pub fn from_radians(radians: f64) -> Angle { 11 | Angle(radians) 12 | } 13 | 14 | pub fn from_degrees(degrees: f64) -> Angle { 15 | Angle(degrees * PI / 180.0) 16 | } 17 | 18 | /// Takes a number between 0.0 and 1.0 where 0.0 represents 0 degrees and 1.0 360 degrees 19 | pub fn from_turn(turn: f64) -> Angle { 20 | Angle(turn * TAU) 21 | } 22 | 23 | pub fn to_radians(self) -> f64 { 24 | self.0 25 | } 26 | 27 | pub fn to_degrees(self) -> f64 { 28 | self.0 * 180.0 / PI 29 | } 30 | 31 | /// Gives a number between 0.0 and 1.0 where 0.0 represents 0 degrees and 1.0 360 degrees 32 | pub fn to_turn(self) -> f64 { 33 | self.0 / std::f64::consts::TAU 34 | } 35 | 36 | /// Creates a `Angle` with a value of 0.0 37 | pub fn zero() -> Angle { 38 | Angle(0.0) 39 | } 40 | } 41 | 42 | impl Default for Angle { 43 | fn default() -> Self { 44 | Self::zero() 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /utils/src/dirty_size.rs: -------------------------------------------------------------------------------- 1 | /// Size with width, height and dirty flag. If the dirty flag is `true`, 2 | /// layout tasks will handle this objects in its arrange and measure 3 | /// tasks. 4 | #[derive(Copy, Clone, PartialEq)] 5 | pub struct DirtySize { 6 | width: f64, 7 | height: f64, 8 | dirty: bool, 9 | } 10 | 11 | impl Default for DirtySize { 12 | fn default() -> Self { 13 | DirtySize { 14 | width: 0.0, 15 | height: 0.0, 16 | dirty: true, 17 | } 18 | } 19 | } 20 | 21 | impl DirtySize { 22 | /// Creates a new dirty size with default values. 23 | pub fn new() -> Self { 24 | DirtySize::default() 25 | } 26 | 27 | pub fn width(&self) -> f64 { 28 | self.width 29 | } 30 | 31 | pub fn set_width(&mut self, width: f64) { 32 | if (self.width - width).abs() > std::f64::EPSILON { 33 | self.dirty = true; 34 | } 35 | 36 | self.width = width; 37 | } 38 | 39 | pub fn height(&self) -> f64 { 40 | self.height 41 | } 42 | 43 | pub fn set_height(&mut self, height: f64) { 44 | if (self.height - height).abs() > std::f64::EPSILON { 45 | self.dirty = true; 46 | } 47 | 48 | self.height = height; 49 | } 50 | 51 | pub fn size(&self) -> (f64, f64) { 52 | (self.width, self.height) 53 | } 54 | 55 | pub fn set_size(&mut self, width: f64, height: f64) { 56 | if (self.width - width).abs() > std::f64::EPSILON 57 | && (self.height - height).abs() > std::f64::EPSILON 58 | { 59 | self.dirty = true 60 | } 61 | 62 | self.width = width; 63 | self.height = height; 64 | } 65 | 66 | /// Gets the dirty flag. 67 | pub fn dirty(&self) -> bool { 68 | self.dirty 69 | } 70 | 71 | /// Sets the dirty flag. 72 | pub fn set_dirty(&mut self, dirty: bool) { 73 | self.dirty = dirty; 74 | } 75 | } 76 | 77 | #[cfg(test)] 78 | mod tests { 79 | use crate::prelude::*; 80 | 81 | #[test] 82 | fn test_set_width() { 83 | let width = 10.0; 84 | 85 | let mut dirty_size = DirtySize::default(); 86 | 87 | dirty_size.set_width(width); 88 | 89 | assert!(crate::f64_cmp(dirty_size.width(), width)); 90 | assert!(dirty_size.dirty()); 91 | } 92 | 93 | #[test] 94 | fn test_set_height() { 95 | let height = 10.0; 96 | 97 | let mut dirty_size = DirtySize::default(); 98 | dirty_size.set_height(height); 99 | 100 | assert!(crate::f64_cmp(dirty_size.height(), height)); 101 | assert!(dirty_size.dirty()); 102 | } 103 | 104 | #[test] 105 | fn test_set_size() { 106 | let size = (10.0, 20.0); 107 | 108 | let mut dirty_size = DirtySize::default(); 109 | 110 | dirty_size.set_size(size.0, size.1); 111 | 112 | assert_eq!(dirty_size.size(), size); 113 | assert!(dirty_size.dirty()); 114 | } 115 | 116 | #[test] 117 | fn test_set_dirty() { 118 | let mut dirty_size = DirtySize::default(); 119 | 120 | dirty_size.set_dirty(false); 121 | 122 | assert!(!dirty_size.dirty()); 123 | } 124 | } 125 | -------------------------------------------------------------------------------- /utils/src/f32_cmp.rs: -------------------------------------------------------------------------------- 1 | /// Compares two f32 values. 2 | pub fn f32_cmp(v1: f32, v2: f32) { 3 | let error_margin = f32::EPSILON; 4 | 5 | assert!((v1 - v2).abs() < error_margin) 6 | } 7 | -------------------------------------------------------------------------------- /utils/src/f64_cmp.rs: -------------------------------------------------------------------------------- 1 | /// Compares two f64 values. 2 | pub fn f64_cmp(v1: f64, v2: f64) -> bool { 3 | let error_margin = f64::EPSILON; 4 | 5 | (v1 - v2).abs() < error_margin 6 | } 7 | -------------------------------------------------------------------------------- /utils/src/filter.rs: -------------------------------------------------------------------------------- 1 | /// Used to filter stuff such as the `on_changed` callback. 2 | #[derive(Debug, Clone, Eq, PartialEq)] 3 | pub enum Filter { 4 | // Everting will be filtered. No element will be available. 5 | Complete, 6 | 7 | // Nothing will be filtered, all elements will be available. 8 | Nothing, 9 | 10 | /// Define a list of filtered element. 11 | List(Vec<String>), 12 | } 13 | 14 | impl From<&str> for Filter { 15 | fn from(s: &str) -> Self { 16 | match s { 17 | "nothing" | "Nothing" => Filter::Nothing, 18 | _ => Filter::Complete, 19 | } 20 | } 21 | } 22 | 23 | impl From<String> for Filter { 24 | fn from(s: String) -> Self { 25 | Filter::from(s.as_str()) 26 | } 27 | } 28 | 29 | impl From<Vec<String>> for Filter { 30 | fn from(v: Vec<String>) -> Self { 31 | Filter::List(v) 32 | } 33 | } 34 | 35 | impl From<Vec<&str>> for Filter { 36 | fn from(v: Vec<&str>) -> Self { 37 | let vec: Vec<String> = v.iter().map(|s| s.to_string()).collect(); 38 | Filter::from(vec) 39 | } 40 | } 41 | 42 | impl Default for Filter { 43 | fn default() -> Self { 44 | Filter::Complete 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /utils/src/lib.rs: -------------------------------------------------------------------------------- 1 | //! Handles helper utilities and global methods. 2 | 3 | pub use self::alignment::*; 4 | pub use self::angle::*; 5 | pub use self::border::*; 6 | pub use self::brush::*; 7 | pub use self::color::*; 8 | pub use self::constraint::*; 9 | pub use self::dirty_size::*; 10 | pub use self::expression::*; 11 | pub use self::f32_cmp::*; 12 | pub use self::f64_cmp::*; 13 | pub use self::filter::*; 14 | pub use self::gradients::*; 15 | pub use self::number::*; 16 | pub use self::orientation::*; 17 | pub use self::point::*; 18 | pub use self::rectangle::*; 19 | pub use self::relative_direction::*; 20 | pub use self::selection_mode::*; 21 | pub use self::size::*; 22 | pub use self::string16::*; 23 | pub use self::text_alignment::*; 24 | pub use self::text_baseline::*; 25 | pub use self::thickness::*; 26 | pub use self::value::*; 27 | pub use self::visibility::*; 28 | 29 | mod alignment; 30 | mod angle; 31 | mod border; 32 | mod brush; 33 | mod color; 34 | mod constraint; 35 | mod dirty_size; 36 | mod expression; 37 | mod f32_cmp; 38 | mod f64_cmp; 39 | mod filter; 40 | mod gradients; 41 | mod number; 42 | mod orientation; 43 | mod point; 44 | 45 | /// This module pre-selects commonly used OrbTk crates and put them into scope. 46 | pub mod prelude; 47 | 48 | mod rectangle; 49 | mod relative_direction; 50 | mod selection_mode; 51 | mod size; 52 | mod spacer; 53 | mod string16; 54 | mod text_alignment; 55 | mod text_baseline; 56 | mod thickness; 57 | mod value; 58 | mod visibility; 59 | -------------------------------------------------------------------------------- /utils/src/number.rs: -------------------------------------------------------------------------------- 1 | use std::ops::Neg; 2 | 3 | /// Valid number types (64bit) 4 | #[derive(Debug, Copy, Clone, PartialEq, PartialOrd)] 5 | pub enum Number { 6 | Real(i64), 7 | Float(f64), 8 | } 9 | 10 | impl Default for Number { 11 | fn default() -> Self { 12 | Self::Real(0) 13 | } 14 | } 15 | 16 | impl Eq for Number {} 17 | 18 | impl Neg for Number { 19 | type Output = Number; 20 | 21 | fn neg(self) -> Self::Output { 22 | match self { 23 | Number::Float(n) => Number::Float(-n), 24 | Number::Real(n) => Number::Real(-n), 25 | } 26 | } 27 | } 28 | 29 | macro_rules! impl_float { 30 | ($t:ty) => { 31 | impl From<$t> for Number { 32 | fn from(n: $t) -> Number { 33 | Number::Float(n as f64) 34 | } 35 | } 36 | 37 | impl From<Number> for $t { 38 | fn from(n: Number) -> Self { 39 | match n { 40 | Number::Real(n) => n as $t, 41 | Number::Float(n) => n as $t, 42 | } 43 | } 44 | } 45 | }; 46 | } 47 | 48 | macro_rules! impl_real { 49 | ($t:ty) => { 50 | impl From<$t> for Number { 51 | fn from(n: $t) -> Number { 52 | Number::Real(n as i64) 53 | } 54 | } 55 | 56 | impl From<Number> for $t { 57 | fn from(n: Number) -> $t { 58 | match n { 59 | Number::Real(n) => n as $t, 60 | Number::Float(n) => n as $t, 61 | } 62 | } 63 | } 64 | }; 65 | } 66 | 67 | impl_float!(f32); 68 | impl_float!(f64); 69 | 70 | impl_real!(u8); 71 | impl_real!(i8); 72 | impl_real!(u16); 73 | impl_real!(i16); 74 | impl_real!(u32); 75 | impl_real!(i32); 76 | impl_real!(u64); 77 | impl_real!(i64); 78 | -------------------------------------------------------------------------------- /utils/src/orientation.rs: -------------------------------------------------------------------------------- 1 | /// Is used to control the orientation of the `Stack`. 2 | #[derive(Debug, Copy, Clone, Eq, PartialEq)] 3 | pub enum Orientation { 4 | /// Vertical orientation. 5 | Vertical, 6 | 7 | /// Horizontal orientation. 8 | Horizontal, 9 | } 10 | 11 | // --- Conversions --- 12 | 13 | impl From<&str> for Orientation { 14 | fn from(t: &str) -> Self { 15 | match t { 16 | "Horizontal" | "horizontal" => Orientation::Horizontal, 17 | _ => Orientation::Vertical, 18 | } 19 | } 20 | } 21 | 22 | impl Default for Orientation { 23 | fn default() -> Orientation { 24 | Orientation::Vertical 25 | } 26 | } 27 | 28 | #[cfg(test)] 29 | mod tests { 30 | use super::*; 31 | 32 | #[test] 33 | fn test_into() { 34 | let orientation: Orientation = "Vertical".into(); 35 | assert_eq!(orientation, Orientation::Vertical); 36 | 37 | let orientation: Orientation = "vertical".into(); 38 | assert_eq!(orientation, Orientation::Vertical); 39 | 40 | let orientation: Orientation = "Horizontal".into(); 41 | assert_eq!(orientation, Orientation::Horizontal); 42 | 43 | let orientation: Orientation = "horizontal".into(); 44 | assert_eq!(orientation, Orientation::Horizontal); 45 | 46 | let orientation: Orientation = "other".into(); 47 | assert_eq!(orientation, Orientation::Vertical); 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /utils/src/prelude.rs: -------------------------------------------------------------------------------- 1 | pub use crate::*; 2 | -------------------------------------------------------------------------------- /utils/src/relative_direction.rs: -------------------------------------------------------------------------------- 1 | use crate::Point; 2 | 3 | /// Describes the relative position of the object. 4 | #[derive(Debug, Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash)] 5 | pub enum RelativeDir { 6 | Top, 7 | TopRight, 8 | Right, 9 | BottomRight, 10 | Bottom, 11 | BottomLeft, 12 | Left, 13 | TopLeft, 14 | } 15 | 16 | impl RelativeDir { 17 | /// Computes the start and end points of a line that crosses a given field in the `self` direction 18 | pub fn cross(&self, width: f64, height: f64) -> (Point, Point) { 19 | let (start, end); 20 | let mid_width = width / 2.0; 21 | let mid_height = height / 2.0; 22 | match self { 23 | RelativeDir::Top => { 24 | start = Point::new(mid_width, height); 25 | end = Point::new(mid_width, 0.0); 26 | } 27 | RelativeDir::TopRight => { 28 | start = Point::new(0.0, height); 29 | end = Point::new(width, 0.0); 30 | } 31 | RelativeDir::Right => { 32 | start = Point::new(0.0, mid_height); 33 | end = Point::new(width, mid_height); 34 | } 35 | RelativeDir::BottomRight => { 36 | start = Point::new(0.0, 0.0); 37 | end = Point::new(width, height); 38 | } 39 | RelativeDir::Bottom => { 40 | start = Point::new(mid_width, 0.0); 41 | end = Point::new(mid_width, height); 42 | } 43 | RelativeDir::BottomLeft => { 44 | start = Point::new(width, 0.0); 45 | end = Point::new(0.0, height); 46 | } 47 | RelativeDir::Left => { 48 | start = Point::new(width, mid_height); 49 | end = Point::new(0.0, mid_height); 50 | } 51 | RelativeDir::TopLeft => { 52 | start = Point::new(width, height); 53 | end = Point::new(0.0, 0.0); 54 | } 55 | } 56 | (start, end) 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /utils/src/selection_mode.rs: -------------------------------------------------------------------------------- 1 | /// Represents a selection mode. 2 | #[derive(Copy, Clone, Eq, PartialEq, Debug)] 3 | pub enum SelectionMode { 4 | None, 5 | Single, 6 | Multiple, 7 | } 8 | 9 | impl Default for SelectionMode { 10 | fn default() -> Self { 11 | SelectionMode::Single 12 | } 13 | } 14 | 15 | impl From<&str> for SelectionMode { 16 | fn from(t: &str) -> Self { 17 | match t { 18 | "Single" | "single" => SelectionMode::Single, 19 | "Multiple" | "multiple" => SelectionMode::Multiple, 20 | _ => SelectionMode::None, 21 | } 22 | } 23 | } 24 | 25 | #[cfg(test)] 26 | mod tests { 27 | use crate::prelude::*; 28 | 29 | #[test] 30 | fn test_into() { 31 | let selection_mode: SelectionMode = "Single".into(); 32 | assert_eq!(selection_mode, SelectionMode::Single); 33 | 34 | let selection_mode: SelectionMode = "single".into(); 35 | assert_eq!(selection_mode, SelectionMode::Single); 36 | 37 | let selection_mode: SelectionMode = "Multiple".into(); 38 | assert_eq!(selection_mode, SelectionMode::Multiple); 39 | 40 | let selection_mode: SelectionMode = "multiple".into(); 41 | assert_eq!(selection_mode, SelectionMode::Multiple); 42 | 43 | let selection_mode: SelectionMode = "None".into(); 44 | assert_eq!(selection_mode, SelectionMode::None); 45 | 46 | let selection_mode: SelectionMode = "none".into(); 47 | assert_eq!(selection_mode, SelectionMode::None); 48 | 49 | let selection_mode: SelectionMode = "other".into(); 50 | assert_eq!(selection_mode, SelectionMode::None); 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /utils/src/size.rs: -------------------------------------------------------------------------------- 1 | use derive_more::{Add, Constructor, From, Sub}; 2 | use std::ops::Div; 3 | 4 | /// A `Size` specified by width and height. 5 | /// 6 | /// # Examples 7 | /// ```rust 8 | /// # use orbtk_utils::Size; 9 | /// let size = Size::new(10., 10.); 10 | /// let other_size = Size::new(5., 7.); 11 | /// let result = size - other_size; 12 | /// 13 | /// assert_eq!(result.width(), 5.); 14 | /// assert_eq!(result.height(), 3.); 15 | /// ``` 16 | #[derive(Constructor, Add, Sub, Copy, From, Clone, Default, Debug, PartialEq)] 17 | pub struct Size { 18 | width: f64, 19 | height: f64, 20 | } 21 | 22 | impl Size { 23 | /// Gets the width of the size. 24 | pub fn width(&self) -> f64 { 25 | self.width 26 | } 27 | 28 | /// Sets the width of the size. 29 | pub fn set_width(&mut self, width: f64) { 30 | self.width = width; 31 | } 32 | 33 | /// Gets the height of the size. 34 | pub fn height(&self) -> f64 { 35 | self.height 36 | } 37 | 38 | /// Sets the height of the size. 39 | pub fn set_height(&mut self, height: f64) { 40 | self.height = height; 41 | } 42 | } 43 | 44 | // Operations 45 | 46 | impl Div<f64> for Size { 47 | type Output = Size; 48 | 49 | fn div(mut self, rhs: f64) -> Self::Output { 50 | self.width /= rhs; 51 | self.height /= rhs; 52 | self 53 | } 54 | } 55 | 56 | impl Div<Size> for f64 { 57 | type Output = Size; 58 | 59 | fn div(self, mut rhs: Size) -> Self::Output { 60 | rhs.width /= self; 61 | rhs.height /= self; 62 | rhs 63 | } 64 | } 65 | 66 | // --- Conversions --- 67 | 68 | impl From<f64> for Size { 69 | fn from(t: f64) -> Self { 70 | Size::new(t, t) 71 | } 72 | } 73 | 74 | impl From<i32> for Size { 75 | fn from(t: i32) -> Self { 76 | Size::new(t as f64, t as f64) 77 | } 78 | } 79 | 80 | impl From<(i32, i32)> for Size { 81 | fn from(s: (i32, i32)) -> Size { 82 | Size::from((s.0 as f64, s.1 as f64)) 83 | } 84 | } 85 | 86 | // --- Conversions --- 87 | 88 | #[cfg(test)] 89 | mod tests { 90 | use super::*; 91 | 92 | #[test] 93 | fn test_sub() { 94 | let expected_result = Size::new(-3., 5.); 95 | const ERROR_MARGIN: f64 = 0.00001; 96 | 97 | let left_side = Size::new(5., 7.); 98 | let right_side = Size::new(8., 2.); 99 | 100 | let result = left_side - right_side; 101 | 102 | assert!((result.width() - expected_result.width()).abs() < ERROR_MARGIN); 103 | assert!((result.height() - expected_result.height()).abs() < ERROR_MARGIN); 104 | } 105 | 106 | #[test] 107 | fn test_add() { 108 | let expected_result = Size::new(13., 9.); 109 | const ERROR_MARGIN: f64 = 0.00001; 110 | 111 | let left_side = Size::new(5., 7.); 112 | let right_side = Size::new(8., 2.); 113 | 114 | let result = left_side + right_side; 115 | 116 | assert!((result.width() - expected_result.width()).abs() < ERROR_MARGIN); 117 | assert!((result.height() - expected_result.height()).abs() < ERROR_MARGIN); 118 | } 119 | } 120 | -------------------------------------------------------------------------------- /utils/src/spacer.rs: -------------------------------------------------------------------------------- 1 | use crate::Thickness; 2 | 3 | // todo: documentation 4 | pub trait Spacer { 5 | /// Gets left. 6 | fn left(&self) -> f64; 7 | 8 | /// Sets left. 9 | fn set_left(&mut self, left: f64); 10 | 11 | /// Gets top. 12 | fn top(&self) -> f64; 13 | 14 | /// Sets top. 15 | fn set_top(&mut self, top: f64); 16 | 17 | /// Gets right. 18 | fn right(&self) -> f64; 19 | 20 | /// Sets right. 21 | fn set_right(&mut self, right: f64); 22 | 23 | /// Gets bottom. 24 | fn bottom(&self) -> f64; 25 | 26 | /// Sets bottom. 27 | fn set_bottom(&mut self, bottom: f64); 28 | 29 | /// Gets thickness. 30 | fn thickness(&self) -> Thickness; 31 | 32 | /// Sets thickness. 33 | fn set_thickness<T: Into<Thickness>>(&mut self, thickness: T); 34 | } 35 | -------------------------------------------------------------------------------- /utils/src/text_alignment.rs: -------------------------------------------------------------------------------- 1 | /// Used to align a text. 2 | #[derive(Clone, Copy, Eq, Debug, PartialEq)] 3 | pub enum TextAlignment { 4 | Left, 5 | Right, 6 | Center, 7 | Start, 8 | End, 9 | } 10 | 11 | impl ToString for TextAlignment { 12 | fn to_string(&self) -> String { 13 | match self { 14 | TextAlignment::Left => "left".to_string(), 15 | TextAlignment::Right => "right".to_string(), 16 | TextAlignment::Center => "center".to_string(), 17 | TextAlignment::Start => "start".to_string(), 18 | TextAlignment::End => "end".to_string(), 19 | } 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /utils/src/text_baseline.rs: -------------------------------------------------------------------------------- 1 | /// Text baseline being used when drawing text 2 | pub enum TextBaseline { 3 | /// Text baseline is top of the em square 4 | Top, 5 | 6 | /// Text baseline is the hanging baseline. 7 | Hanging, 8 | 9 | /// Text baseline is the middle of the em square. 10 | Middle, 11 | 12 | /// Text baseline is the normal alphabetic baseline. (default) 13 | Alphabetic, 14 | 15 | /// Text baseline is the ideographic baseline 16 | Ideographic, 17 | 18 | /// Text baseline is the bottom of the bounding box. 19 | Bottom, 20 | } 21 | 22 | impl Default for TextBaseline { 23 | fn default() -> Self { 24 | TextBaseline::Alphabetic 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /utils/src/value.rs: -------------------------------------------------------------------------------- 1 | use serde::de::DeserializeOwned; 2 | /// Wraps a ron value and is used to support conversion to different types. 3 | pub struct Value(pub ron::Value); 4 | 5 | impl Value { 6 | /// Converts the internal value to the given type. 7 | pub fn get<T>(self) -> T 8 | where 9 | T: Default + DeserializeOwned, 10 | { 11 | if let Ok(value) = self.0.into_rust::<T>() { 12 | return value; 13 | } 14 | 15 | T::default() 16 | } 17 | } 18 | 19 | impl From<ron::Value> for Value { 20 | fn from(v: ron::Value) -> Self { 21 | Value(v) 22 | } 23 | } 24 | 25 | impl From<Value> for String { 26 | fn from(v: Value) -> String { 27 | v.get::<String>() 28 | } 29 | } 30 | 31 | impl From<Value> for f64 { 32 | fn from(v: Value) -> f64 { 33 | v.get::<f64>() 34 | } 35 | } 36 | 37 | impl From<Value> for f32 { 38 | fn from(v: Value) -> f32 { 39 | v.get::<f32>() 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /utils/src/visibility.rs: -------------------------------------------------------------------------------- 1 | /// Is used to control the visibility of a widget 2 | #[derive(Debug, Copy, Clone, Eq, PartialEq)] 3 | pub enum Visibility { 4 | /// The widget is visible. 5 | Visible, 6 | 7 | /// The widget will not be displayed, isn't taken into account in the 8 | /// layout pipeline. It **does** consume memory in the render buffer. 9 | Hidden, 10 | 11 | /// The widget isn't displayed but `is` rendered. Thus it **does 12 | /// not** consume space in the layout. 13 | Collapsed, 14 | } 15 | 16 | impl Default for Visibility { 17 | fn default() -> Visibility { 18 | Visibility::Visible 19 | } 20 | } 21 | 22 | // --- Conversions --- 23 | 24 | impl From<&str> for Visibility { 25 | fn from(t: &str) -> Self { 26 | match t { 27 | "Hidden" | "hidden" => Visibility::Hidden, 28 | "Collapsed" | "collapsed" => Visibility::Collapsed, 29 | _ => Visibility::Visible, 30 | } 31 | } 32 | } 33 | 34 | #[cfg(test)] 35 | mod tests { 36 | use super::*; 37 | 38 | #[test] 39 | fn test_into() { 40 | let visibility: Visibility = "Hidden".into(); 41 | assert_eq!(visibility, Visibility::Hidden); 42 | 43 | let visibility: Visibility = "hidden".into(); 44 | assert_eq!(visibility, Visibility::Hidden); 45 | 46 | let visibility: Visibility = "Collapsed".into(); 47 | assert_eq!(visibility, Visibility::Collapsed); 48 | 49 | let visibility: Visibility = "collapsed".into(); 50 | assert_eq!(visibility, Visibility::Collapsed); 51 | 52 | let visibility: Visibility = "Visible".into(); 53 | assert_eq!(visibility, Visibility::Visible); 54 | 55 | let visibility: Visibility = "visible".into(); 56 | assert_eq!(visibility, Visibility::Visible); 57 | 58 | let visibility: Visibility = "other".into(); 59 | assert_eq!(visibility, Visibility::Visible); 60 | } 61 | } 62 | --------------------------------------------------------------------------------