Compare commits

..

390 Commits

Author SHA1 Message Date
2dc42b4a22 Use less iterator magic to avoid compile error on phone 2021-06-26 20:27:44 +01:00
343e6332d7 Format code 2021-06-26 20:19:55 +01:00
3a5f866782 Downgrade OpenGL and GLSL for glow renderer 2021-06-26 20:19:51 +01:00
e4b626b91f Use glow_glyph_compat 2021-06-26 19:47:12 +01:00
45ecf02504 Add tour_glow for OpenGL platforms 2021-06-26 16:09:25 +01:00
Héctor Ramón Jiménez
1b6cf05f5f
Install libxkbcommon-dev for ubuntu-latest in CI 2021-06-26 12:19:23 +02:00
Héctor Ramón
c6c3594c83
Merge pull request #927 from diegodox/fix-typo
Fix typo in documentation of `canvas::Program`
2021-06-26 11:57:31 +02:00
Héctor Ramón
06d0158efb
Merge pull request #917 from derezzedex/macos-url
Enable receiving URLs on MacOS
2021-06-25 14:47:14 +02:00
Richard
612585109f
Use winit and glutin forks in iced-rs org 2021-06-25 14:15:11 +02:00
Richard
96a462d2f2
Use new enum variant and new winit repo 2021-06-25 14:14:03 +02:00
Richard
9ae22b58d8
Added events for url handling and create example 2021-06-25 14:14:03 +02:00
Héctor Ramón
d2c8a3e04b
Merge pull request #919 from Imberflur/winit-0.25
Bump winit to 0.25
2021-06-24 19:30:59 +02:00
Imbris
ba51661a2a Bump winit to 0.25 2021-06-23 17:38:08 -04:00
Diego Fujii
80df17ab55 fix-typo 2021-06-24 00:16:08 +09:00
Héctor Ramón
b62fcca9b9
Merge pull request #915 from yusdacra/docs/update-links
docs: update all 0.2 github links to 0.3
2021-06-22 12:19:38 +02:00
Héctor Ramón
94ee2566c4
Merge pull request #920 from clarkmoody/feature/pane-grid-title-bar-overlay
Pane Grid Title Bar Overlay
2021-06-22 12:02:07 +02:00
Héctor Ramón
bb6e06127e
Merge pull request #925 from PolyMeilex/master
Update `wgpu` to `0.9`
2021-06-22 11:39:18 +02:00
Héctor Ramón Jiménez
15c17a7250
Use match statement in Content::overlay
... to improve readability a bit.
2021-06-22 11:36:36 +02:00
Poly
a53e7559fe Use vertex_attr_array macro 2021-06-22 11:23:11 +02:00
Poly
c70f90f320 Update wgpu 0.9 2021-06-22 11:23:10 +02:00
Clark Moody
27b42ca6b6 Allow overlay from pane grid title bar 2021-06-17 14:51:23 -05:00
Yusuf Bera Ertan
83d19689c8
docs: update all 0.2 github links to 0.3 2021-06-14 21:01:37 +03:00
Héctor Ramón
e68da229b3
Merge pull request #646 from mtsr/disable-button
Disabled button docs and consistent behavior
2021-06-10 19:12:11 +07:00
Héctor Ramón
d1797dda87 Revert changes in tour example 2021-06-10 18:58:44 +07:00
Héctor Ramón
d46dd67a91 Update disabled example of Button in docs 2021-06-10 18:58:40 +07:00
Jonas Matser
e66120b9c1 Fix failing doctests 2021-06-10 18:28:38 +07:00
Jonas Matser
dbc1181011 Adds doc comment for disabled button
Makes disabled button behavior consistent in web
2021-06-10 18:28:37 +07:00
Héctor Ramón
f6ff87bb8f
Merge pull request #818 from thenlevy/check_bounds
Prevent scissor_rect region to be larger than the target texture in wgpu::Backend::flush
2021-06-10 18:24:42 +07:00
Héctor Ramón
56f673d819 Revert "Attempt to fix scissor_rect validation error"
This reverts commit 656dc357f8.
2021-06-09 21:30:48 +07:00
Héctor Ramón
5224cc7f26 Floor width and height in Rectangle::floor 2021-06-09 21:30:20 +07:00
nlevy
656dc357f8 Attempt to fix scissor_rect validation error
Update wgpu/src/backend.rs

Cargo fmt
2021-06-09 21:30:00 +07:00
Héctor Ramón
0e70b11e00
Merge pull request #607 from yusdacra/scrollable_programmatically
Add methods to control `Scrollable` programmatically
2021-06-07 22:00:07 +07:00
Héctor Ramón
ce3a5f19b9 Add scrolling progress indicators to scrollable example 2021-06-04 20:46:47 +07:00
Héctor Ramón
3051d4ec76 Introduce on_scroll event in Scrollable 2021-06-04 20:46:27 +07:00
Héctor Ramón
57510c43c8 Add buttons to control scrolling in scrollable example 2021-06-04 20:15:06 +07:00
Héctor Ramón
827577c179 Introduce snap_to and unsnap to scrollable::State 2021-06-04 20:09:15 +07:00
Yusuf Bera Ertan
f7d6e40bf0 feat(native): Make scrollable programmatically scrollable for some use cases, add snap_to_bottom by default 2021-06-04 19:29:58 +07:00
Héctor Ramón
397a5c06ec
Merge pull request #535 from Kaiden42/toggler
Implement `Toggler` widget for iced_native
2021-06-03 20:55:50 +07:00
Héctor Ramón
d3d6f3efb3 Add some horizontal padding to toggler section in tour example 2021-06-03 20:35:26 +07:00
Héctor Ramón
ef5f46bcdd Use intra-doc links in Toggler docs 2021-06-03 20:28:36 +07:00
Héctor Ramón
a32ce271bd Rename text_align to text_alignment in Toggler 2021-06-03 20:27:32 +07:00
Kaiden42
2a5aa69024 Fix format 2021-06-03 20:21:55 +07:00
Kaiden42
e00fca6372 Add Toggler widget to iced_web 2021-06-03 20:21:55 +07:00
Kaiden42
c0cfd9d5cf Update documentation of Toggler 2021-06-03 20:21:55 +07:00
Kaiden42
be3ee9adf1 Add Toggler to tour example 2021-06-03 20:21:55 +07:00
Kaiden42
1ef38cc207 Add Toggler to styling example 2021-06-03 20:21:55 +07:00
Kaiden42
7a626f3b7b Change label of Toggler to optional 2021-06-03 20:21:55 +07:00
Kaiden42
aa18a6e0d5 Add alignment of Toggler label. 2021-06-03 20:21:55 +07:00
Kaiden42
bab71971fb fix format 2021-06-03 20:21:55 +07:00
Kaiden42
88da268724 add missing glow support 2021-06-03 20:21:53 +07:00
Kaiden42
7370dfac6e fix missing semicolon in doc test 2021-06-03 20:21:04 +07:00
Kaiden42
52a185fbab Implement Toggler widget for iced_native 2021-06-03 20:21:02 +07:00
Héctor Ramón
1dce929dfc
Merge pull request #893 from traxys/drag_and_drop
Allow disabling drag and drop on windows
2021-06-01 19:59:33 +07:00
Héctor Ramón
aab2176802
Merge pull request #630 from blefevre/asymmetric-padding
Add support for asymmetrical padding
2021-06-01 19:59:02 +07:00
Héctor Ramón
8a3b71df8b Replace ignored doc-tests with additional documentation for Padding 2021-06-01 19:45:47 +07:00
Héctor Ramón
b94cd7a2a8 Use Padding::horizontal and Padding::vertical helpers 2021-06-01 19:21:43 +07:00
Héctor Ramón
2e17d7860b Fix unused variable warning in iced_web::text_input 2021-06-01 19:14:04 +07:00
Héctor Ramón
0a14492343 Fix Tooltip widget 2021-06-01 19:13:52 +07:00
Héctor Ramón
92361ef07d Fix overlay::Menu implementation 2021-06-01 19:13:34 +07:00
Héctor Ramón
d83e263abe Introduce vertical and horizontal methods to Padding 2021-06-01 19:13:11 +07:00
Ben LeFevre
fe0a27c56d Add support for asymmetrical padding 2021-06-01 19:05:39 +07:00
Héctor Ramón
903570846e Fix documentation of PlatformSpecific settings 2021-06-01 18:57:36 +07:00
Héctor Ramón
a9eb591628
Merge pull request #901 from hecrj/feature/default-rule-style
Implement `Default` for `Style` in `rule`
2021-06-01 18:54:08 +07:00
Héctor Ramón
662889bb83 Implement Default for Style in rule 2021-06-01 18:32:56 +07:00
Quentin Boyer
f04bc94b80 allow disabling drag and drop on windows 2021-05-27 14:22:11 +02:00
Héctor Ramón
e292821c37
Merge pull request #892 from clarkmoody/title-bar-events
PaneGrid Events in Title Bar Area
2021-05-25 16:34:17 +07:00
Clark Moody
d4c5f3ee95 Enable event handling within the title elements
Shrink the pick area to avoid both the controls and the title elements.
Handle events and merge title area event status with control events.
2021-05-24 16:37:47 -05:00
Clark Moody
1a2fd4e743 Example: Add Pin button to prevent closing a pane
Functionality will not work until PaneGrid implementation is updated
to support events within the title area.
2021-05-24 15:53:20 -05:00
Clark Moody
e6f8b32583 Example: Add close button to pane grid controls
Refactors the state data structure to hold content and controls in
separate fields. Adds a new button style for the control button.
2021-05-24 14:28:52 -05:00
Héctor Ramón
df971ac99b
Merge pull request #830 from Dispersia/upgrade-wgpu
Upgrade wgpu
2021-05-24 20:26:56 +07:00
Héctor Ramón
4cbc345245 Use FilterMode::Nearest in triangle::msaa 2021-05-21 20:34:08 +07:00
Héctor Ramón
ebec84ea7c Revert "Remove padding from triangle"
This reverts commit 2d549d806c.
2021-05-21 20:29:17 +07:00
Dispersia
0772310c4f Fix duplicating fragment position 2021-05-20 23:10:22 -07:00
Dispersia
a70715ad9e run format 2021-05-19 22:07:27 -07:00
Dispersia
2d549d806c Remove padding from triangle 2021-05-19 21:09:19 -07:00
Dispersia
b40c441646 Add padding to quad to fix alignment issue 2021-05-19 21:04:47 -07:00
Dispersia
d91422e345 temporary up 2021-05-19 08:09:03 -07:00
Aaron Housh
ae484429d3 Merge branch 'hecrj:master' into upgrade-wgpu 2021-05-19 07:14:26 -07:00
Héctor Ramón
cf6af4c256 Use latest wgpu releases instead of patched sources 2021-05-19 17:18:43 +07:00
Héctor Ramón
8b7452a55d Fix formatting with cargo fmt 2021-05-19 16:26:04 +07:00
Héctor Ramón
3918257883
Merge pull request #886 from Chiheisen/fix/pick_list_layouting
pick_list: fix layouting not respecting fonts
2021-05-19 15:40:17 +07:00
chiheisen
59f3896392 fix pick_list layouting not respecting fonts 2021-05-18 12:37:23 +02:00
zdevwu
40d21d2365
Added text color and font options for native radio and checkbox (#831)
* text color and font options to radio

* code formatting

* code formatting

* code formatting

* Added text_color for native checkbox

* Removed clone as color has Copy

* Fix code formatting with `cargo fmt`

Co-authored-by: Héctor Ramón <hector@lich.io>
2021-05-17 20:22:55 +07:00
Héctor Ramón
0ce6a2db21
Merge pull request #876 from Cupnfish/master
Add a primary backend that can be set
2021-05-17 19:54:28 +07:00
Héctor Ramón
70ee917bac
Merge pull request #875 from ZakisM/optimize_svg_algorithm
This commit optimizes the algorithm used to convert rgba pixels into bgra pixels for SVG's.
2021-05-17 19:51:17 +07:00
Downtime
88defb65ca update doc 2021-05-14 21:21:25 +08:00
Héctor Ramón
faa68534cf
Merge pull request #877 from RDambrosio016/patch-1
Change examples to point to 0.3 examples, not 0.2
2021-05-14 20:05:49 +07:00
Riccardo D'Ambrosio
5f1b880521
Change examples to point to 0.3 examples, not 0.2 2021-05-13 11:04:09 -04:00
Downtime
e6db439870 Add a primary backend that can be set
Add a primary backend that can be set
2021-05-13 16:46:20 +08:00
Zak
77a17cde83 This commit optimizes the function used to converg rgba pixels into bgra pixels. 2021-05-11 22:41:55 +01:00
Héctor Ramón
08f5591148
Merge pull request #868 from hecrj/wgpu-compatible-surface
Provide `compatible_surface` in `iced_wgpu::Compositor`
2021-05-08 01:24:26 +07:00
Héctor Ramón
3840b75bea Provide compatible_surface in iced_wgpu::Compositor 2021-05-05 14:33:30 +07:00
Héctor Ramón
6b4bf34bf9
Merge pull request #851 from hecrj/fix/scrollbar-under-content
Fix `Scrollable` scrollbar being rendered behind contents
2021-05-04 13:03:43 +07:00
Héctor Ramón
59c2500c55 Fix Scrollable scrollbar being rendered behind contents
... by issuing a new clip layer just for the scrollbar itself.
2021-05-03 15:42:02 +07:00
Dispersia
983aa1b366 Run cargo fmt 2021-04-12 23:23:47 -07:00
Dispersia
0722d5e3ec add temporary fix for image wgsl 2021-04-12 23:07:58 -07:00
Dispersia
c719091c3d Add staging belt fix 2021-04-12 22:06:16 -07:00
Dispersia
9a2c78c405 Upgrade wgpu 2021-04-11 18:55:57 -07:00
Héctor Ramón
4b8ba8309f
Merge pull request #825 from tarkah/feat/window-visibility
add window visibility
2021-04-11 10:38:52 +07:00
Cory Forsstrom
84c0c9bc7a use Mode::Hidden instead 2021-04-09 09:04:30 -07:00
Cory Forsstrom
6f6f1d82e8 add to glutin 2021-04-08 13:30:38 -07:00
Cory Forsstrom
cdab8f90fb add window visibility 2021-04-08 12:58:08 -07:00
Héctor Ramón
3ea2c4595a
Merge pull request #815 from taiki-e/docs
Enable qr_code feature on docs.rs
2021-04-06 13:47:30 +07:00
Taiki Endo
8473d8e984 Enable qr_code feature on docs.rs 2021-04-06 02:46:44 +09:00
Héctor Ramón
854b2e0c4d
Merge pull request #810 from Cupnfish/master
fix typo
2021-04-01 17:54:39 +02:00
Downtime
ca4257ff5c Update frame.rs 2021-04-01 21:58:25 +08:00
Héctor Ramón Jiménez
90fee3a923 Fix iced_wgpu link in CHANGELOG 2021-03-31 20:46:59 +02:00
Héctor Ramón
06517aa7e8
Merge pull request #806 from hecrj/release/0.3.0
Release 0.3 — Touch support, clipboard write access, image viewer, tooltips, and more!
2021-03-31 20:34:45 +02:00
Héctor Ramón Jiménez
0864e63bde Bump versions 🎉 2021-03-31 20:07:17 +02:00
Héctor Ramón Jiménez
2b20512a3d Update CHANGELOG 2021-03-31 19:59:23 +02:00
Héctor Ramón
b9ec44446e
Merge pull request #804 from hecrj/feature/graceful-exit
Graceful exiting for `Application`
2021-03-31 10:20:22 +02:00
Héctor Ramón Jiménez
8f952452ce Showcase graceful exiting in events example 2021-03-30 21:45:49 +02:00
Héctor Ramón Jiménez
67db13ff7c Add support for graceful exits in Application
- `Settings` now contains an `exit_on_close_request` field
- `Application` has a new `should_exit` method
2021-03-30 21:44:19 +02:00
Héctor Ramón Jiménez
00de9d0c9b Add CloseRequested variant to window::Event 2021-03-30 21:33:57 +02:00
Héctor Ramón
bbb4e4678f
Merge pull request #796 from hecrj/fix/redraw-empty-window
Skip redrawing if window has no surface
2021-03-26 15:16:28 +01:00
Héctor Ramón Jiménez
1207afa7d0 Skip redrawing if window has no surface 2021-03-26 14:46:19 +01:00
Héctor Ramón
bf09f44d56
Merge pull request #789 from Cupnfish/master
Add WGPU_BACKEND environment variable
2021-03-25 11:41:52 +01:00
Héctor Ramón Jiménez
883c7e71ae Introduce internal_backend to iced_wgpu::Settings 2021-03-25 11:29:40 +01:00
Downtime
ab8dcf91bd Support choosing wgpu backend using env var 2021-03-25 11:29:26 +01:00
Héctor Ramón Jiménez
2b520ca098 Convert ScaleFactorChanged into Resized events in iced_glutin
... instead of just dropping them when calling `to_static`.
2021-03-24 05:29:19 +01:00
Héctor Ramón Jiménez
d66a34b272 Convert ScaleFactorChanged into Resized events in iced_winit
... instead of just dropping them when calling `to_static`.
2021-03-24 04:59:13 +01:00
Nicolas Levy
0333a8daff
Overwrite overlay method in Widget implementation for Button (#764)
* Overwrite `overlay` method in Widget implementation for Button

* Overwrite `overlay` method in Widget implementation for Button (cargo fmt)

* Fix button overlay
2021-03-14 23:39:01 +01:00
Héctor Ramón
c1f70f1e92
Merge pull request #773 from hecrj/feature/clipboard-access-in-update
Clipboard access in `Application::update`
2021-03-12 02:54:13 +01:00
Héctor Ramón Jiménez
7da3fb1b22 Implement stub Clipboard in iced_web
We need to figure out browser permissions and use of unstable `web-sys`
APIs
2021-03-11 03:52:41 +01:00
Héctor Ramón Jiménez
a365998264 Expose read and write methods in iced_winit::Clipboard directly 2021-03-11 03:49:15 +01:00
Héctor Ramón Jiménez
ae517b9fa0 Add clipboard argument to Application::update 2021-03-11 03:38:20 +01:00
Héctor Ramón
7eb5127748
Merge pull request #770 from hecrj/feature/clipboard-write
Write clipboard support and `TextInput` copy and cut behavior
2021-03-10 21:13:07 +01:00
Héctor Ramón
939fcfe9db
Merge pull request #771 from hecrj/fix/tooltip-layering
Reposition `Tooltip` inside `viewport` bounds
2021-03-10 03:28:04 +01:00
Héctor Ramón Jiménez
17dcfa8faf Implement copy and cut behavior for TextInput 2021-03-10 01:59:20 +01:00
Héctor Ramón Jiménez
21971e0037 Make Clipboard argument in Widget trait mutable 2021-03-10 01:59:02 +01:00
Héctor Ramón Jiménez
35425001ed Introduce write method to Clipboard trait 2021-03-10 01:18:22 +01:00
Héctor Ramón Jiménez
b22b0dd7ff Update window_clipboard to 0.2 2021-03-10 01:16:26 +01:00
Héctor Ramón Jiménez
bbca5c4bde Call hash_layout for controls in pane_grid::TitleBar 2021-03-09 04:49:17 +01:00
Héctor Ramón
a74974a8e4
Merge pull request #760 from TriedAngle/master
Update: rand in solar_system example
2021-03-05 03:10:55 +01:00
Sebastian
5f27ed4720 Update: rand 2021-03-03 01:23:18 +01:00
Héctor Ramón Jiménez
c51b771519 Reposition Tooltip inside viewport bounds
... only when out of bounds.
2021-02-27 03:47:13 +01:00
Héctor Ramón Jiménez
f52f8c1337 Fix viewport argument in PaneGrid draw calls 2021-02-27 03:36:46 +01:00
Héctor Ramón
a5fddf9ee6
Merge pull request #465 from yusdacra/tooltip-widget
Tooltip widget
2021-02-24 01:42:08 +01:00
Héctor Ramón Jiménez
2736e4ca35 Hide Text as an implementation detail of Tooltip 2021-02-24 00:59:29 +01:00
Héctor Ramón Jiménez
5e2743361b Generate new layers only for clip primitives in Layer::generate 2021-02-23 04:02:55 +01:00
Héctor Ramón Jiménez
4e923290cc Add style and padding to Tooltip 2021-02-23 04:00:35 +01:00
Héctor Ramón Jiménez
9d4996cbab Export Tooltip in iced_glow 2021-02-23 03:18:47 +01:00
Héctor Ramón Jiménez
2f766b7341 Introduce Tooltip::gap to control spacing 2021-02-23 03:16:37 +01:00
Héctor Ramón Jiménez
9f60a256fc Remove viewport from Overlay::draw for now 2021-02-23 03:12:00 +01:00
Héctor Ramón Jiménez
81c75c1524 Change Tooltip to support Text only for now 2021-02-23 03:09:16 +01:00
Héctor Ramón Jiménez
6759a5c56f Log event subscription error as a warning 2021-02-23 02:16:12 +01:00
Héctor Ramón
842d54732b
Merge pull request #739 from hecrj/fix/beta-warnings
Fix warnings in the `beta` toolchain
2021-02-16 00:41:06 +01:00
Héctor Ramón Jiménez
ec20763aef Use string literal as panic message in iced_glow 2021-02-16 00:04:36 +01:00
Héctor Ramón Jiménez
9ba9558429 Allow dead code explicitly in iced_web 2021-02-16 00:03:20 +01:00
Yusuf Bera Ertan
a19f89d3a6
feat(native): add Tooltip widget 2021-02-15 19:37:46 +03:00
Héctor Ramón
4de164dcc7
Merge pull request #392 from unrelentingtech/image-debloat
Add image format options to reduce code bloat
2021-02-13 16:18:40 +01:00
Héctor Ramón Jiménez
e1b1227f0c Fix image feature name in glow and iced 2021-02-13 16:05:51 +01:00
Greg V
8f126c212b Add image format options to reduce code bloat, fixes #376 2021-02-13 15:53:24 +01:00
Folyd
9f5c2eb0c4
Improve download_progress example (#283)
* Add advanced download example

* Rename to task fields and variables

* Cargo fmt advanced_download/src/download.rs

* Add progress bar for advanced download example

* Merge two download examples to single one

* Apply great review suggestions

* Change to url::Url instead of plain String

* Simplify `download_progress` example

* Update `README` of `download_progress` example

Co-authored-by: Héctor Ramón Jiménez <hector0193@gmail.com>
2021-02-12 22:00:52 +01:00
anunge
9e453843b2
Touch support for PaneGrid and PickList (#650)
* touch events properly parsed and converted to logical size, button working

* scrolling with a nice touch

* fixed application state level touch cursor. panel_grid is touchable now.

* format glicthes fixes

* format glitches

* tight format

* fixed pane grid

* fixing with upstream

* Remove unused `touch` module from `iced_core`

* Remove unused `crate::text` import in `iced_native`

* Remove redundant match branch in `iced_winit`

* Keep removed line break in `UserInterface::update`

* Compute `text_size` only when bounds contains cursor in `overlay::menu`

Co-authored-by: Héctor Ramón Jiménez <hector0193@gmail.com>
2021-02-12 20:52:20 +01:00
Héctor Ramón
2f10a1f2a2
Merge pull request #725 from PolyMeilex/wgpu-7.0
Update to wgpu 0.7
2021-02-06 16:18:19 +01:00
Héctor Ramón Jiménez
74b9ea520f Enable filtering in wgpu::image 2021-02-06 16:04:43 +01:00
Héctor Ramón Jiménez
7eefad34fc List color_blend first in wgpu::quad 2021-02-06 15:55:03 +01:00
Héctor Ramón Jiménez
15c4901aba Remove unnecessary line break in triangle::msaa 2021-02-06 15:39:12 +01:00
Héctor Ramón Jiménez
ffdf87fbe2 Use lowercase in wgpu labels for consistency 2021-02-06 15:39:06 +01:00
Héctor Ramón Jiménez
5fc4210270 Use the latest release of wgpu_glyph 2021-02-06 15:27:27 +01:00
Poly
b0d1be69d6 Change PowerPreference from default() to LowPower
There is no reason to hide the fact that this is always in `LowPower` mode
2021-02-03 23:50:03 +01:00
Poly
09a5348740 [wgpu 0.7] Update integration example 2021-02-03 22:38:45 +01:00
Poly
1fb60c5dcb Fix TextureViewDimension for image
wgpu validation helped to find this long standing type error
2021-02-03 22:26:53 +01:00
Poly
bd6b8304bd Fix ScissorRect
- Breaks `TODO: Address anti-aliasing adjustments properly`
2021-02-03 21:51:11 +01:00
Poly
c5d6ddc126 [wgpu 0.7] triangle/msaa disable filtering 2021-02-03 21:13:51 +01:00
Poly
4a6db30d47 [wgpu 0.7] Update image.rs 2021-02-03 20:24:48 +01:00
Poly
e2595ac0aa [wgpu 0.7] Update triangle.rs 2021-02-03 20:06:07 +01:00
Poly
5f935c34fd [wgpu 0.7] Update triangle/msaa.rs
Notes:
- not sure if `filtering sampler` should be used, so for now it is not used
.
2021-02-03 20:05:37 +01:00
Poly
98d108d2b7 [wgpu 0.7] Update window/compositor.rs
Notes:
- wgpu::PowerPreference::Default no longer exists, maybe there is another way to replicate its behavior.
2021-02-03 19:32:30 +01:00
Poly
2d76c7165c [wgpu 0.7] Update quad.rs 2021-02-03 19:21:02 +01:00
Poly
4a727dfb8b The beginning of wgpu 0.7 update
hecrj/iced#723
2021-02-03 19:20:23 +01:00
Héctor Ramón
12c0c18d66
Merge pull request #720 from taiki-e/style-clone
impl Clone and Copy for all Style types in iced_style
2021-01-29 19:56:33 +01:00
Héctor Ramón
c7898a101c
Merge pull request #719 from taiki-e/error-send-sync
Make iced::Error Send + Sync
2021-01-29 19:55:25 +01:00
Taiki Endo
2969558afd impl Clone and Copy for all Style types in iced_style 2021-01-29 14:08:14 +09:00
Taiki Endo
14d900d835 Make iced::Error Send + Sync 2021-01-29 14:03:27 +09:00
GunpowderGuy
8d882d787e
update example description at sandbox's documentation (#710)
* Update sandbox.rs

* Update sandbox.rs

* format

Co-authored-by: Diego Rosario <diegorosario2013@gmail.com>
2021-01-22 01:36:05 +01:00
Héctor Ramón Jiménez
d1c4239ac7 Disable default features of qrcode for iced_graphics 2021-01-21 05:07:41 +01:00
Héctor Ramón Jiménez
9da0a3de54 Use smol::Timer::interval for time::Every 2021-01-15 21:07:37 +01:00
Héctor Ramón Jiménez
803cc88483 Fix consistency in CHANGELOG 2021-01-15 19:05:20 +01:00
Héctor Ramón
2056304e39
Merge pull request #699 from JayceFayne/smol
Add `smol` async runtime support
2021-01-15 19:04:23 +01:00
Héctor Ramón Jiménez
fd2c96c8e3 Fix time::Every implementation for smol runtime 2021-01-15 18:52:12 +01:00
Héctor Ramón Jiménez
bcc54b0831 Add latest release (0.2.0) to CHANGELOG 2021-01-15 18:37:20 +01:00
Héctor Ramón Jiménez
a42a0844c2 Keep old behavior for Executor feature flags 2021-01-15 18:31:30 +01:00
Héctor Ramón Jiménez
984583b0cc Merge pull request #701 from cossonfork/focus_event 2021-01-15 18:23:50 +01:00
Héctor Ramón Jiménez
45dc02e9bd Split window::Event::Focused into two variants 2021-01-15 18:21:44 +01:00
cossonleo
0b140488b4 add focus event 2021-01-15 22:40:16 +08:00
Héctor Ramón Jiménez
0646280d67 Fix Widget::width implementation for PickList 2021-01-15 14:26:23 +01:00
Héctor Ramón
049999b982
Merge pull request #700 from TannerRogalsky/fix_viewport_getter
Fix `physical_width` getter incorrectly returning the height
2021-01-15 02:02:26 +01:00
Tanner Rogalsky
766bb7f5cc Fix physical_width getter incorrectly returning the height 2021-01-14 10:38:12 -05:00
Jayce Fayne
c542224f4b Update CHANGELOG.md 2021-01-14 12:28:06 +01:00
Jayce Fayne
b2415eee61 Add smol async runtime 2021-01-14 12:28:02 +01:00
Héctor Ramón
92d647d1a6
Merge pull request #694 from hecrj/fix/image-viewer-event-capturing
Capture relevant events in `image::Viewer`
2021-01-12 20:06:32 +01:00
Héctor Ramón Jiménez
c7f6b2a5c7 Capture relevant events in image::Viewer 2021-01-11 19:31:34 +01:00
Héctor Ramón Jiménez
e7344d03b4 Use BTreeMap for splits and regions in pane_grid
This preserves ordering between calls to update and draw logic.
2021-01-07 21:07:44 +01:00
Héctor Ramón Jiménez
31522e30aa Remove unnecessary SaveError variant in todos example 2021-01-04 23:38:07 +01:00
Héctor Ramón
80a490ebb9
Merge pull request #672 from yusdacra/tokio-1.0
Update tokio to v1.0
2021-01-04 23:33:09 +01:00
Héctor Ramón Jiménez
09ea73bd2a Use Instant::into_std in futures::time 2021-01-04 23:20:40 +01:00
Yusuf Bera Ertan
50452e62b4 Update tokio to 1.0 2021-01-04 23:20:21 +01:00
Héctor Ramón
2665860b4d
Merge pull request #678 from hecrj/feature/pane-grid-split-highlight
Split highlight for the `PaneGrid` widget
2021-01-04 19:33:26 +01:00
Héctor Ramón Jiménez
a7bb7bb2ea Implement split highlight on hover for PaneGrid 2021-01-01 15:28:38 +01:00
Héctor Ramón
f8aef03456
Merge pull request #657 from clarkmoody/feature/pane-grid-title-contents
Generic Element Content in Pane Grid TitleBar
2020-12-22 14:58:39 +01:00
Héctor Ramón Jiménez
e815c5bbd7 Remove unnecessary lifetime bound in TitleBar 2020-12-22 14:47:18 +01:00
Héctor Ramón Jiménez
f2c2f3fc75 Remove unnecessary text::Renderer bound for PaneGrid
This is no longer necessary, as we do not render text directly anymore.
2020-12-22 14:44:44 +01:00
Héctor Ramón
a366600e73
Merge pull request #669 from hecrj/improvement/update-resvg-and-font-kit
Update `resvg` and `font-kit`
2020-12-20 02:50:38 +01:00
Héctor Ramón Jiménez
4bbfdef14b Update font-kit to 0.10 2020-12-19 01:24:46 +01:00
Greg V
81f37123ad Update resvg to 0.12 2020-12-19 01:24:40 +01:00
Héctor Ramón
86361f003c
Merge pull request #668 from hecrj/fix/propagate-button-events
Propagate `Button` events to contents
2020-12-18 23:30:49 +01:00
Héctor Ramón
0e9f649cb7
Merge pull request #319 from tarkah/image-pane
Add `ImagePane` widget
2020-12-18 23:29:51 +01:00
Héctor Ramón Jiménez
10d6df73e3 Remove max_width and max_height from image::Viewer
The support for these layout constraints is currently not ideal. We
should reintroduce these methods once our layout engine improves.
2020-12-18 11:35:01 +01:00
Héctor Ramón Jiménez
c54a6446a3 Use intra-doc links in image::viewer 2020-12-18 11:35:01 +01:00
Héctor Ramón Jiménez
6a51282933 Simplify cursor_to_center in image::Viewer 2020-12-18 11:34:52 +01:00
Héctor Ramón Jiménez
149098cb68 Remove use of unwrap in image::Viewer 2020-12-18 11:20:48 +01:00
Héctor Ramón Jiménez
43ef85ae5c Rename starting_cursor_pos to cursor_grabbed_at in image::Viewer 2020-12-18 11:20:18 +01:00
Héctor Ramón Jiménez
8245a11766 Negate values instead of multipling by -1.0 in image::Viewer 2020-12-18 11:01:20 +01:00
Héctor Ramón Jiménez
4eb5779542 Remove redundant f32 conversions in image::Viewer 2020-12-18 11:00:32 +01:00
Héctor Ramón Jiménez
cf97982929 Build todos examples instead of pokedex in CI 2020-12-18 10:57:51 +01:00
Héctor Ramón Jiménez
64af860ad2 Remove unnecessary Option in image::viewer::State 2020-12-18 10:55:18 +01:00
Héctor Ramón Jiménez
e6f23e3771 Rename scale_pct to scale_step in image::Viewer 2020-12-18 10:53:38 +01:00
Héctor Ramón Jiménez
ca3e4e9f1b Simplify pattern match in image::Viewer::on_event 2020-12-18 10:49:10 +01:00
Héctor Ramón Jiménez
add167d6a0 Pan image::Viewer even if the cursor is out of bounds 2020-12-18 10:47:29 +01:00
Héctor Ramón Jiménez
21b10dc103 Fix layout of image::Viewer 2020-12-18 10:44:24 +01:00
Héctor Ramón Jiménez
0cdf8d56ee Use image::Viewer in pokedex example 2020-12-18 10:44:06 +01:00
Héctor Ramón Jiménez
71de341684 Turn methods into helper functions in image::viewer 2020-12-18 10:24:04 +01:00
Héctor Ramón Jiménez
74ee7cca81 Format use declarations in image::viewer 2020-12-18 10:18:39 +01:00
Héctor Ramón Jiménez
0b73e5fbfa Merge remote-tracking branch 'tarkah/image-pane' into image-pane 2020-12-18 10:15:30 +01:00
Héctor Ramón Jiménez
8fb0ede72e Propagate Button events to contents 2020-12-18 09:40:10 +01:00
Nils Martel
07b570036a Mention birth year in READMEs 2020-12-17 05:26:28 +01:00
Héctor Ramón
89e604d160
Merge pull request #57 from simlay/ios-support-wip
Touch support
2020-12-17 05:19:14 +01:00
Héctor Ramón Jiménez
277ae74d68 Produce logical coordinates for touch::Event 2020-12-15 06:58:15 +01:00
Héctor Ramón Jiménez
70164f68a6 Fix text selection with touch events in TextInput 2020-12-15 06:48:12 +01:00
Héctor Ramón Jiménez
056f7e6951 Change cursor position on touch event 2020-12-15 06:44:00 +01:00
Héctor Ramón Jiménez
3bdf931925 Turn Touch into a touch::Event enum 2020-12-15 06:38:46 +01:00
Héctor Ramón Jiménez
09110a93b0 Merge branch 'ios-support-wip' into feature/touch-support 2020-12-15 06:13:19 +01:00
Héctor Ramón
a42b3c6998
Merge pull request #658 from hecrj/update-winit-and-glutin
Update `winit` and `glutin`
2020-12-15 03:27:42 +01:00
Héctor Ramón Jiménez
742086e190 Update winit and glutin
We avoid a breaking change in `mouse::Button::Other` for the time being.
2020-12-12 00:54:25 +01:00
Clark Moody
fb478a4014 Update PaneGrid example with more complex TitleBar 2020-12-10 12:18:28 -06:00
Clark Moody
f54590d7ad Replace TitleBar string title with generic Content 2020-12-10 12:17:36 -06:00
Héctor Ramón
d16b9cf7cd
Merge pull request #637 from hecrj/0.2
Release `0.2` — Canvas interactivity, overlay support, a renderer alternative, and more!
2020-11-26 17:05:49 +01:00
Héctor Ramón Jiménez
f78108a514 Bump versions 🎉 2020-11-26 16:47:41 +01:00
Héctor Ramón Jiménez
745aa49025 Update example links to point to 0.2 branch 2020-11-26 16:37:33 +01:00
Héctor Ramón Jiménez
b5c41a8815 Update game_of_life capture 2020-11-26 16:36:56 +01:00
Héctor Ramón
35df13686f
Merge pull request #636 from hecrj/improvement/update-ecosystem-and-roadmap
Update `ECOSYSTEM` and `ROADMAP`
2020-11-26 16:10:20 +01:00
Héctor Ramón Jiménez
29ad61d377 Add missing metadata to iced_graphics subcrate 2020-11-26 15:57:55 +01:00
Héctor Ramón Jiménez
7de72d6a27 Update READMEs of subcrates 2020-11-26 07:40:00 +01:00
Héctor Ramón Jiménez
278d40d8c6 Reduce size of foundations graph in ECOSYSTEM 2020-11-26 07:24:55 +01:00
Héctor Ramón Jiménez
11798dcc03 Update graphs in crate docs 2020-11-26 07:22:03 +01:00
Héctor Ramón Jiménez
0525d76ff9 Reduce size of graphs in README and ECOSYSTEM 2020-11-26 07:10:34 +01:00
Héctor Ramón Jiménez
d8c3982160 Update ROADMAP 2020-11-26 06:49:42 +01:00
Héctor Ramón Jiménez
a9ea921383 Update ECOSYSTEM 2020-11-26 06:43:37 +01:00
Héctor Ramón Jiménez
ebe34b03cb Update README 2020-11-26 06:43:29 +01:00
Héctor Ramón
50a1f78996
Merge pull request #527 from rubik83/master
Account for empty ranges in `Slider` and `ProgressBar`
2020-11-26 03:51:07 +01:00
Héctor Ramón Jiménez
c23136a5df Account for empty ranges in Slider and ProgressBar 2020-11-26 03:33:08 +01:00
rubik83
955b62ea2d fix slider handle_offset formula
change :
let handle_offset = (bounds.width - handle_width) * ((value - range_start) / (range_end - range_start).max(1.0))
to :
let handle_offset = (bounds.width - handle_width) * ((value - range_start) / (range_end - range_start).max(f32::EPSILON)).max(0.0).min(1.0)
2020-11-26 03:09:46 +01:00
rubik83
9991a89428 fix active_progress_width formula
change :
let active_progress_width = bounds.width * ((value - range_start) / (range_end - range_start).max(1.0))
to :
let active_progress_width = bounds.width * ((value - range_start) / (range_end - range_start).max(f32::EPSILON)).max(0.0).min(1.0)
2020-11-26 03:09:46 +01:00
Héctor Ramón
1f7e8b7f3d
Merge pull request #632 from hecrj/improvement/update-docs
Use intra-doc links
2020-11-26 02:52:34 +01:00
Héctor Ramón Jiménez
01322f69a4 Use recently stabilized intra-doc links
See RFC: https://github.com/rust-lang/rfcs/blob/master/text/1946-intra-rustdoc-links.md
2020-11-26 02:05:43 +01:00
Héctor Ramón Jiménez
d612bf5678 Rename keyboard::ModifiersState to Modifiers 2020-11-26 02:05:42 +01:00
Héctor Ramón Jiménez
08e0b9ffbd Fix broken links in API documentation 2020-11-26 01:59:20 +01:00
Héctor Ramón
bffaeed9fd
Merge pull request #634 from myfreeweb/graphics-license
Add license to iced-graphics' Cargo.toml
2020-11-25 21:30:39 +01:00
Greg V
775c1c5873 Add license to iced-graphics' Cargo.toml 2020-11-25 23:02:50 +03:00
Héctor Ramón
18aa14c7bb
Merge pull request #605 from ZakisM/text_input_select_all_fix
This PR fixes a bug with select all (CMD + A on MacOS) when using a text_input.
2020-11-25 19:50:24 +01:00
Héctor Ramón
87c9df294c
Merge pull request #595 from valbendan/master
upgrade tokio to latest version(v0.3)
2020-11-25 04:24:44 +01:00
Héctor Ramón Jiménez
1d23db1866 Track keyboard modifiers in text_input 2020-11-25 04:10:23 +01:00
Zak
d275a4ed32 This PR fixes a bug with select all (CMD + A on MacOS) when using a text_input.
Previous behaviour: when selecting all (CMD + A) would delete the current text inside the input and replace the content with just the letter 'a'.

Now we check if the logo key (modifier key) has been pressed before checking any other key and save it to the state level. This way we can prevent any text being deleted when using the select all shortcut or text being entered at all when a modifier key is pressed (this behaviour matches other text input behaviour i.e text inputs in the browser etc...).
2020-11-25 03:57:18 +01:00
Héctor Ramón Jiménez
782dd2f522 Introduce tokio_old feature
This feature allows users to rely on the `0.2` version of `tokio` while
the async ecosystem upgrades to the latest version.
2020-11-25 03:06:24 +01:00
Héctor Ramón
8f081bad77
Merge pull request #629 from hecrj/wgpu-present-mode-setting
Add `present_mode` field to `iced_wgpu::Settings`
2020-11-24 00:17:59 +01:00
Héctor Ramón
556cf24b95
Merge pull request #627 from hecrj/fix/canvas-rotation
Fix backwards `Frame::rotate` in `canvas`
2020-11-24 00:17:46 +01:00
Héctor Ramón
5615643c52
Merge pull request #628 from hecrj/improvement/float-border-radius
Use `f32` for `border_width` and `border_radius`
2020-11-24 00:17:30 +01:00
Héctor Ramón Jiménez
1916755b6b Limit border radius to max dimension in quad pipeline 2020-11-23 00:43:59 +01:00
Héctor Ramón Jiménez
f41eacc3dc Use f32 for border_width and border_radius 2020-11-23 00:31:50 +01:00
Héctor Ramón Jiménez
0f00d14297 Add present_mode field to iced_wgpu::Settings 2020-11-23 00:00:13 +01:00
Héctor Ramón Jiménez
77c7ad1fef Fix backwards Frame::rotate in canvas
The angle direction has been fixed upstream in `euclid` (see
https://github.com/servo/euclid/pull/413).
2020-11-22 11:05:08 +01:00
Héctor Ramón
ea1a7248d2
Merge pull request #622 from hecrj/feature/qr_code-widget
`QRCode` widget
2020-11-20 23:33:51 +01:00
Héctor Ramón Jiménez
f259d44186 Implement qr_code example 2020-11-20 10:29:40 +01:00
Héctor Ramón Jiménez
3296be845c Implement QRCode widget 2020-11-20 10:29:33 +01:00
Héctor Ramón Jiménez
209056e1cd Fix deprecation warnings from image 2020-11-20 10:29:11 +01:00
Héctor Ramón
1af6fb3ec0
Merge pull request #620 from hecrj/fix/trackpad-text-input-selection
Disable dragging in `TextInput` after double click
2020-11-20 00:33:48 +01:00
Héctor Ramón Jiménez
140bea352e Disable dragging in TextInput after double click
When using a trackpad, mouse move events may happen between the
press/release events. This was incorrectly triggering selection dragging in
the `TextInput` widget.

Eventually, we should implement proper word-aware selection dragging.
2020-11-19 02:45:47 +01:00
Héctor Ramón Jiménez
c03d46719e Remove Focus in pane_grid
Since #608, the `PaneGrid` widget does not handle pane focus.
2020-11-17 06:13:56 +01:00
Héctor Ramón Jiménez
df712f9ccf Implement flexible TextInput::draw helper 2020-11-17 05:13:06 +01:00
Héctor Ramón
62295f554b
Merge pull request #614 from hecrj/feature/event-capturing
Event capturing
2020-11-14 02:17:21 +01:00
Héctor Ramón Jiménez
bf2d2561b8 Batch event processing in UserInterface::update 2020-11-12 02:51:26 +01:00
Héctor Ramón Jiménez
69c50c8511 Introduce event::Status to Subscription 2020-11-12 02:22:22 +01:00
Héctor Ramón Jiménez
33d80b5a0b Return event::Status in UserInterface::update 2020-11-12 02:00:08 +01:00
Héctor Ramón Jiménez
6e9bd0d9d1 Make Overlay::on_event return event::Status 2020-11-12 01:29:11 +01:00
Héctor Ramón Jiménez
3aca177132 Implement event capturing for Canvas 2020-11-12 01:24:59 +01:00
Héctor Ramón Jiménez
bf6c65b5ad Implement event capturing for TextInput 2020-11-12 01:11:09 +01:00
Héctor Ramón Jiménez
c361fe48c7 Implement event capturing for Slider 2020-11-12 00:56:50 +01:00
Héctor Ramón Jiménez
fd275a2fee Implement event capturing for Scrollable 2020-11-12 00:53:39 +01:00
Héctor Ramón Jiménez
451bf8dc84 Implement event capturing for Row 2020-11-12 00:48:40 +01:00
Héctor Ramón Jiménez
18172f80c9 Implement event capturing for Radio 2020-11-12 00:47:58 +01:00
Héctor Ramón Jiménez
7ff95f3a88 Implement event capturing for PickList 2020-11-12 00:47:21 +01:00
Héctor Ramón Jiménez
31c509b206 Implement event capturing for PaneGrid 2020-11-12 00:40:55 +01:00
Héctor Ramón Jiménez
3bcee62beb Implement event capturing for Column 2020-11-12 00:40:52 +01:00
Héctor Ramón Jiménez
04468a7147 Implement event capturing for Checkbox 2020-11-12 00:20:09 +01:00
Héctor Ramón Jiménez
a44cd07212 Implement event capturing for Button 2020-11-12 00:19:12 +01:00
Héctor Ramón Jiménez
3f968b8c87 Make Widget::on_event return an event::Status 2020-11-12 00:09:52 +01:00
Héctor Ramón Jiménez
1db11ba69a Introduce event::Status in iced_native 2020-11-11 23:54:59 +01:00
Héctor Ramón
73811c394a
Merge pull request #610 from hecrj/improvement/update-dependencies
Update dependencies
2020-11-11 18:06:27 +01:00
Héctor Ramón Jiménez
0400f6716b Use directories-next in todos example 2020-11-11 02:34:17 +01:00
Héctor Ramón
2f5a3dacd9
Merge pull request #608 from hecrj/remove-pane-grid-focus
Improve flexibility of `PaneGrid`
2020-11-10 22:57:19 +01:00
Héctor Ramón Jiménez
86fa12229e Introduce is_command_pressed to ModifiersState 2020-11-10 21:18:21 +01:00
Héctor Ramón Jiménez
1475b10dff Update font-kit dependency in iced_graphics 2020-11-10 20:33:45 +01:00
Héctor Ramón Jiménez
6f52eb4d3a Update lyon dependency in iced_graphics 2020-11-10 20:13:47 +01:00
Héctor Ramón Jiménez
fa98dbad16 Update glam dependency in iced_graphics 2020-11-10 20:12:15 +01:00
Héctor Ramón Jiménez
45c749f28c Update bytemuck dependency in iced_graphics 2020-11-10 20:11:23 +01:00
Héctor Ramón Jiménez
9d4f664c94 Update bytemuck and remove zerocopy in iced_wgpu 2020-11-10 20:06:24 +01:00
Héctor Ramón Jiménez
b86accfe1c Update bytemuck dependency in iced_glow 2020-11-10 19:52:43 +01:00
Héctor Ramón Jiménez
fb3cd68dde Update glow and glow_glyph dependencies in iced_glow 2020-11-10 19:48:40 +01:00
Héctor Ramón Jiménez
0c2787eada Update euclid dependency in iced_glow 2020-11-10 19:15:07 +01:00
Héctor Ramón Jiménez
17a4647fce Update directories dependency in todos example 2020-11-10 19:13:40 +01:00
Héctor Ramón Jiménez
860de97b42 Update env_logger in tour and integration examples 2020-11-10 19:12:28 +01:00
Héctor Ramón Jiménez
3b2ed0d6f0 Remove unnecessary move in PaneGrid::new 2020-11-10 03:18:46 +01:00
Héctor Ramón Jiménez
d6d5cf0294 Restore hotkeys in pane_grid example
- Implement `subscription::events_with`
- Remove `pane_grid::KeyPressEvent`
- Return closest sibling in `pane_grid::State::close`
2020-11-10 02:32:57 +01:00
Héctor Ramón Jiménez
c53022e8df Fix typo in documentation of pane_grid::Content 2020-11-10 01:51:27 +01:00
Héctor Ramón Jiménez
8008ea5286 Introduce on_click handler in PaneGrid 2020-11-10 01:48:11 +01:00
Héctor Ramón Jiménez
5681c83d3c Remove focus concept from pane_grid 2020-11-10 01:38:27 +01:00
Héctor Ramón
d0402d072d
Merge pull request #598 from oknozor/input-text-focus
Add textinput focus method
2020-11-09 20:49:42 +01:00
Héctor Ramón Jiménez
1a2cb2f35b Split focus and unfocus methods in text_input 2020-11-09 20:32:58 +01:00
Paul Delafosse
f7d67598cb Add textinput focus method 2020-11-09 20:32:50 +01:00
Héctor Ramón
da1a3eed1e
Merge pull request #597 from hecrj/improvement/reuse-view-in-event-loop
Rebuild widget tree only after an application update
2020-11-08 13:31:08 +01:00
Héctor Ramón
ef76f16900
Merge pull request #600 from aentity/update_guillotiere
update guillotiere to 0.6
2020-11-06 19:10:59 +01:00
aentity
66509784cb update guillotiere to 0.6 2020-11-06 01:49:37 -08:00
dabaichi
a4ad1b297e
Update src/executor.rs
fix executor other than tokio

Co-authored-by: Héctor Ramón <hector0193@gmail.com>
2020-11-06 04:20:19 +00:00
Héctor Ramón Jiménez
631c9e4a21 Write missing documentation in iced_winit 2020-11-06 02:25:56 +01:00
Héctor Ramón Jiménez
e966cd5b59 Remove a bit of code duplication in both shells 2020-11-05 04:50:57 +01:00
valbendan
eacb2e913f upgrade tokio to latest version(v0.3) 2020-11-05 11:36:23 +08:00
Héctor Ramón Jiménez
d5a15419e9 Drop UserInterface manually after exit request 2020-11-05 04:13:09 +01:00
Héctor Ramón Jiménez
6619fab044 Update iced_glutin with new event loop logic 2020-11-05 04:09:40 +01:00
Héctor Ramón Jiménez
88993fb092 Relayout UserInterface on resize in iced_winit 2020-11-05 04:09:40 +01:00
Héctor Ramón Jiménez
86b26f65d6 Handle event loop ControlFlow in iced_winit 2020-11-05 04:09:40 +01:00
Héctor Ramón Jiménez
fee46fd653 Introduce application::State in iced_winit 2020-11-05 04:09:40 +01:00
Héctor Ramón Jiménez
ed2b9a91b4 Initialize runtime values in application::run 2020-11-05 04:09:40 +01:00
Héctor Ramón Jiménez
ee38b04d8b Use static noop Waker in application::run 2020-11-05 04:09:40 +01:00
Héctor Ramón Jiménez
c153d11aa6 Draft strategy to reuse view result in event loop 2020-11-05 04:09:31 +01:00
Héctor Ramón
e6131783e9
Merge pull request #590 from Limeth/master
Fix lifetimes in `Layout::children`
2020-11-02 17:57:56 +01:00
Jakub Hlusička
1d51025993
Take self by value in Layout::children 2020-11-02 16:27:28 +01:00
Héctor Ramón
28280600d7
Merge pull request #592 from sum-elier/fix-window-icon-dimensionsmismatch-error
Fixes #591 DimensionsMismatch error message
2020-11-02 15:06:01 +01:00
sum-elier
2b1d438ad4 Fix DimensionsMismatch error message
The values passed to write! were shifted 1 to the left.

Fixes #591
2020-11-01 09:21:42 -05:00
Jakub Hlusička
4dc93e8138
Fix lifetimes in Layout::children 2020-10-31 16:51:11 +01:00
Héctor Ramón
f46431600c
Merge pull request #586 from hecrj/fix/subscription-map
Accept a function pointer in `Subscription::map`
2020-10-30 08:04:23 +01:00
Héctor Ramón
820abdc98e
Merge pull request #583 from Limeth/master
Add conversion functions to Size and Vector
2020-10-29 16:03:10 +01:00
Jakub Hlusička
16646ffc41
Apply suggestions 2020-10-29 14:57:37 +01:00
Héctor Ramón
b8825966d4
Merge pull request #533 from Kaiden42/gitignore
Fix: ignoring all `target` directories
2020-10-29 10:45:34 +01:00
Héctor Ramón Jiménez
dd1b1cac0a Accept a function pointer in Subscription::map
Instead of a closure, a function pointer can be hashed and used to
uniquely identify a particular `Subscription`.

This should fix a bug where two different instances of `Subscription`
producing the same output were not treated differently by the runtime,
causing one of them to be ignored.
2020-10-29 10:27:18 +01:00
Héctor Ramón
b40775fb74
Merge pull request #584 from hecrj/improvement/viewport-aware-drawing
Viewport aware drawing
2020-10-29 02:00:56 +01:00
Jakub Hlusička
9090fa6a22 Add conversion functions to Size and Vector 2020-10-28 15:33:38 +01:00
Héctor Ramón Jiménez
91b1886968 Fix Widget::draw for Rule 2020-10-28 06:21:07 +01:00
Héctor Ramón Jiménez
7f02765214 Draw only visible options in overlay::Menu 2020-10-28 06:21:07 +01:00
Héctor Ramón Jiménez
d328b07b39 Introduce viewport to Widget::draw
This should eventually allow us to only generate primitives that are
visible.
2020-10-28 06:21:07 +01:00
Héctor Ramón
8a3ce90959
Merge pull request #575 from clarkmoody/scrollable-width
Custom Scrollbar Width
2020-10-27 23:35:52 +01:00
Héctor Ramón Jiménez
2ca05520ba Update screenshot of scrollable example 2020-10-24 10:32:26 +02:00
Héctor Ramón Jiménez
ed458d33d9 Reduce contrast of dark theme in scrollable example 2020-10-24 10:29:19 +02:00
Héctor Ramón Jiménez
7f66345d5a Improve minor details in scrollable example
- Rename `Config` to `Variant`
- Include `State` in `Variant` to avoid `zip`
- Break long string literal
- Split `style` module into its own file
2020-10-24 10:29:12 +02:00
Héctor Ramón Jiménez
d3b04bf892 Introduce Eq requirement to build a PickList 2020-10-24 09:32:23 +02:00
Clark Moody
e43a46952a Add scrollable example program 2020-10-22 16:24:54 -05:00
Clark Moody
f05578c8f8 Update scrollbar logic and introduce outer_bounds 2020-10-22 16:05:44 -05:00
Clark Moody
09e67c5c27 Replace hard-coded params with struct members 2020-10-22 14:07:07 -05:00
Héctor Ramón
23647d2f50
Merge pull request #545 from MonliH/master
Remove outdated `Fill` comment for `Column` and `Row`
2020-10-17 21:12:09 +02:00
Héctor Ramón
befeb32225
Merge pull request #569 from hecrj/improvement/hide-null-executor
Remove `executor::Null` from the root public API
2020-10-17 21:10:59 +02:00
Héctor Ramón
5d0f7ba3fc
Merge pull request #568 from hecrj/improvement/early-clone-message-bounds
Require `Clone` for `Message` early when needed
2020-10-17 21:10:26 +02:00
Jonathan Li
461499dd62 Remove entirely 2020-10-17 10:48:31 -04:00
Héctor Ramón Jiménez
ae5a2502d6 Remove executor::Null from the root public API
Using an `Application` with `executor::Null` does not make sense, as the
whole purpose of an `Application` is to allow executing async actions.

When async actions are not needed, `Sandbox` should be used instead.
2020-10-17 08:46:16 +02:00
Héctor Ramón Jiménez
d7a5e54455 Require Clone for Message early when needed
Prior to this change, the widgets that needed a `Clone` bound on `Message` to
implement the `Widget` trait could be created with a non-cloneable `Message`.

As a consequence, the compiler complained only when actually trying to use the
`Widget` trait. Normally, this happens when trying to `push` the widget in a
container or turn it into an `Element`.

Furthermore, the compiler error in this case does not mention `Message` nor the
`Clone` bound, but instead complains about a missing `From` implementation.
Thus, it can easily cause confusion!

This change introduces `Clone` bounds in the main implementation of the
widgets that need it to properly implement the `Widget` trait. As a
result, the compiler complains early when trying to create one of these widgets
with a non-cloneable `Message` and explicitly mentions that the `Message` needs
to implement `Clone`.
2020-10-17 08:10:30 +02:00
Héctor Ramón
17f0db57c3
Merge pull request #563 from Limeth/master
Fix typo in `Row`'s and `Column`'s `hash_layout`
2020-10-14 02:48:51 +02:00
Jakub Hlusička
4910e03833 Fix typo in Row's and Column's hash_layout 2020-10-13 20:48:53 +02:00
Héctor Ramón
be61d84cae
Merge pull request #558 from Azorlogh/master
Adds From<Point> and From<Size> for [f32; 2]
2020-10-08 15:50:09 +02:00
Azorlogh
e6bcb7211f add From<Point> and From<Size> for [f32; 2] 2020-10-08 09:54:22 +02:00
Héctor Ramón
2e0ba65a20
Merge pull request #542 from aentity/winit_023
update to winit 0.23 api
2020-10-08 03:55:02 +02:00
Héctor Ramón Jiménez
16cd38a198 Keep KeyCode names synchronized with winit 2020-10-08 03:44:22 +02:00
aentity
159e8a6abc update to winit 0.23 api 2020-10-03 12:58:28 -07:00
Jonathan Li
7c24277210 Document better 2020-10-02 21:53:40 -04:00
Jonathan Li
8346209c37 Satify documented behaviour 2020-10-01 17:10:13 -04:00
Héctor Ramón
c393e450a1
Merge pull request #543 from twitchyliquid64/master
Fixes #539: Allow windows to be set always_on_top
2020-09-30 19:03:56 +02:00
Tom
f2247a70dc Fixes #539: Allow windows to be set always_on_top 2020-09-28 21:05:15 -07:00
Kaiden42
7dc762ad9c Fix ignoring all target directories 2020-09-18 09:57:46 +02:00
Héctor Ramón Jiménez
4f2962d73f Move version from Grid to GameOfLife struct 2020-09-12 20:35:51 +02:00
Héctor Ramón
49076c6ac2
Merge pull request #514 from hecrj/feature/error-handling
Error propagation
2020-09-08 20:03:37 +02:00
Cory Forsstrom
c7bb434113
remove re-export on viewer::State 2020-05-27 14:20:42 -07:00
Cory Forsstrom
5dd62bacd5
update docs 2020-05-27 14:16:38 -07:00
Cory Forsstrom
de176beb28
centered image and zoom to cursor 2020-05-27 13:39:26 -07:00
Cory Forsstrom
5d045c2e9a
rename to image::Viewer 2020-05-26 17:16:05 -07:00
Cory Forsstrom
431171f975
Rename and add to iced image module 2020-05-26 16:56:34 -07:00
Cory Forsstrom
6bf459e068
Rebase to master and update for api changes 2020-05-26 16:56:34 -07:00
Cory Forsstrom
7f7e803448
Show idle cursor if image can't be panned 2020-05-26 16:56:34 -07:00
Cory Forsstrom
0d8cefbf2d
Add ImagePane widget 2020-05-26 16:56:34 -07:00
Héctor Ramón Jiménez
36bdc0be1a Remove redundant scroll_to in Scrollable 2020-03-19 12:23:31 +01:00
Héctor Ramón Jiménez
d3572e1b81 Turn Touch into a struct and add finger id 2020-03-19 12:17:16 +01:00
Sebastian Imlay
e19a07d400 Added initial touch events to support iOS 2020-03-18 11:26:53 -07:00
328 changed files with 9255 additions and 5149 deletions

View File

@ -11,6 +11,11 @@ jobs:
- name: Install cargo-deb
run: cargo install cargo-deb
- uses: actions/checkout@master
- name: Install dependencies
run: |
export DEBIAN_FRONTED=noninteractive
sudo apt-get -qq update
sudo apt-get install -y libxkbcommon-dev
- name: Enable Link Time Optimizations
run: |
echo "[profile.release]" >> Cargo.toml

View File

@ -12,6 +12,12 @@ jobs:
with:
rust-version: ${{ matrix.rust }}
- uses: actions/checkout@master
- name: Install dependencies
if: matrix.os == 'ubuntu-latest'
run: |
export DEBIAN_FRONTED=noninteractive
sudo apt-get -qq update
sudo apt-get install -y libxkbcommon-dev
- name: Run tests
run: |
cargo test --verbose --all
@ -29,5 +35,5 @@ jobs:
run: cargo check --package iced --target wasm32-unknown-unknown
- name: Check compilation of `tour` example
run: cargo build --package tour --target wasm32-unknown-unknown
- name: Check compilation of `pokedex` example
run: cargo build --package pokedex --target wasm32-unknown-unknown
- name: Check compilation of `todos` example
run: cargo build --package todos --target wasm32-unknown-unknown

2
.gitignore vendored
View File

@ -1,4 +1,4 @@
/target
target/
pkg/
**/*.rs.bk
Cargo.lock

View File

@ -5,10 +5,115 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
## [Unreleased]
### Added
- `"system_font"` feature gates reading system fonts. [#370]
[#370]: https://github.com/hecrj/iced/pull/370
## [0.3.0] - 2021-03-31
### Added
- Touch support. [#57] [#650] (thanks to @simlay and @discordance!)
- Clipboard write access for
- `TextInput` widget. [#770]
- `Application::update`. [#773]
- `image::Viewer` widget. It allows panning and scaling of an image. [#319] (thanks to @tarkah!)
- `Tooltip` widget. It annotates content with some text on mouse hover. [#465] (thanks to @yusdacra!)
- Support for the [`smol`] async runtime. [#699] (thanks to @JayceFayne!)
- Support for graceful exiting when using the `Application` trait. [#804]
- Image format features in [`iced_wgpu`] to reduce code bloat. [#392] (thanks to @unrelentingtech!)
- `Focused` and `Unfocused` variant to `window::Event`. [#701] (thanks to @cossonleo!)
- `WGPU_BACKEND` environment variable to configure the internal graphics backend of `iced_wgpu`. [#789] (thanks to @Cupnfish!)
### Changed
- The `TitleBar` of a `PaneGrid` now supports generic elements. [#657] (thanks to @clarkmoody!)
- The `Error` type now implements `Send` and `Sync`. [#719] (thanks to @taiki-e!)
- The `Style` types in `iced_style` now implement `Clone` and `Copy`. [#720] (thanks to @taiki-e!)
- The following dependencies have been updated:
- [`font-kit`] → `0.10` [#669]
- [`glutin`] → `0.26` [#658]
- [`resvg`] → `0.12` [#669]
- [`tokio`] → `1.0` [#672] (thanks to @yusdacra!)
- [`winit`] → `0.24` [#658]
- [`wgpu`] → `0.7` [#725] (thanks to @PolyMeilex)
- The following examples were improved:
- `download_progress` now showcases multiple file downloads at once. [#283] (thanks to @Folyd!)
- `solar_system` uses the new `rand` API. [#760] (thanks to @TriedAngle!)
### Fixed
- Button events not being propagated to contents. [#668]
- Incorrect overlay implementation for the `Button` widget. [#764]
- `Viewport::physical_width` returning the wrong value. [#700]
- Outdated documentation for the `Sandbox` trait. [#710]
[#57]: https://github.com/hecrj/iced/pull/57
[#283]: https://github.com/hecrj/iced/pull/283
[#319]: https://github.com/hecrj/iced/pull/319
[#392]: https://github.com/hecrj/iced/pull/392
[#465]: https://github.com/hecrj/iced/pull/465
[#650]: https://github.com/hecrj/iced/pull/650
[#657]: https://github.com/hecrj/iced/pull/657
[#658]: https://github.com/hecrj/iced/pull/658
[#668]: https://github.com/hecrj/iced/pull/668
[#669]: https://github.com/hecrj/iced/pull/669
[#672]: https://github.com/hecrj/iced/pull/672
[#699]: https://github.com/hecrj/iced/pull/699
[#700]: https://github.com/hecrj/iced/pull/700
[#701]: https://github.com/hecrj/iced/pull/701
[#710]: https://github.com/hecrj/iced/pull/710
[#719]: https://github.com/hecrj/iced/pull/719
[#720]: https://github.com/hecrj/iced/pull/720
[#725]: https://github.com/hecrj/iced/pull/725
[#760]: https://github.com/hecrj/iced/pull/760
[#764]: https://github.com/hecrj/iced/pull/764
[#770]: https://github.com/hecrj/iced/pull/770
[#773]: https://github.com/hecrj/iced/pull/773
[#789]: https://github.com/hecrj/iced/pull/789
[#804]: https://github.com/hecrj/iced/pull/804
[`smol`]: https://github.com/smol-rs/smol
[`winit`]: https://github.com/rust-windowing/winit
[`glutin`]: https://github.com/rust-windowing/glutin
[`font-kit`]: https://github.com/servo/font-kit
## [0.2.0] - 2020-11-26
### Added
- __[`Canvas` interactivity][canvas]__ (#325)
A trait-based approach to react to mouse and keyboard interactions in [the `Canvas` widget][#193].
- __[`iced_graphics` subcrate][opengl]__ (#354)
A backend-agnostic graphics subcrate that can be leveraged to build new renderers.
- __[OpenGL renderer][opengl]__ (#354)
An OpenGL renderer powered by [`iced_graphics`], [`glow`], and [`glutin`]. It is an alternative to the default [`wgpu`] renderer.
- __[Overlay support][pick_list]__ (#444)
Basic support for superpositioning interactive widgets on top of other widgets.
- __[Faster event loop][view]__ (#597)
The event loop now takes advantage of the data dependencies in [The Elm Architecture] and leverages the borrow checker to keep the widget tree alive between iterations, avoiding unnecessary rebuilds.
- __[Event capturing][event]__ (#614)
The runtime now can tell whether a widget has handled an event or not, easing [integration with existing applications].
- __[`PickList` widget][pick_list]__ (#444)
A drop-down selector widget built on top of the new overlay support.
- __[`QRCode` widget][qr_code]__ (#622)
A widget that displays a QR code, powered by [the `qrcode` crate].
[canvas]: https://github.com/hecrj/iced/pull/325
[opengl]: https://github.com/hecrj/iced/pull/354
[`iced_graphics`]: https://github.com/hecrj/iced/pull/354
[pane_grid]: https://github.com/hecrj/iced/pull/397
[pick_list]: https://github.com/hecrj/iced/pull/444
[error]: https://github.com/hecrj/iced/pull/514
[view]: https://github.com/hecrj/iced/pull/597
[event]: https://github.com/hecrj/iced/pull/614
[color]: https://github.com/hecrj/iced/pull/200
[qr_code]: https://github.com/hecrj/iced/pull/622
[#193]: https://github.com/hecrj/iced/pull/193
[`glutin`]: https://github.com/rust-windowing/glutin
[`wgpu`]: https://github.com/gfx-rs/wgpu-rs
[`glow`]: https://github.com/grovesNL/glow
[the `qrcode` crate]: https://docs.rs/qrcode/0.12.0/qrcode/
[integration with existing applications]: https://github.com/hecrj/iced/pull/183
[The Elm Architecture]: https://guide.elm-lang.org/architecture/
## [0.1.1] - 2020-04-15
### Added
@ -102,7 +207,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
[`wasm-bindgen-futures`]: https://github.com/rustwasm/wasm-bindgen/tree/master/crates/futures
[`resvg`]: https://github.com/RazrFalcon/resvg
[`raqote`]: https://github.com/jrmuizel/raqote
[`iced_wgpu`]: https://github.com/hecrj/iced/tree/0.1/wgpu
[`iced_wgpu`]: https://github.com/hecrj/iced/tree/master/wgpu
## [0.1.0-beta] - 2019-11-25
@ -114,7 +219,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
### Added
- First release! :tada:
[Unreleased]: https://github.com/hecrj/iced/compare/0.1.1...HEAD
[Unreleased]: https://github.com/hecrj/iced/compare/0.3.0...HEAD
[0.3.0]: https://github.com/hecrj/iced/compare/0.2.0...0.3.0
[0.2.0]: https://github.com/hecrj/iced/compare/0.1.1...0.2.0
[0.1.1]: https://github.com/hecrj/iced/compare/0.1.0...0.1.1
[0.1.0]: https://github.com/hecrj/iced/compare/0.1.0-beta...0.1.0
[0.1.0-beta]: https://github.com/hecrj/iced/compare/0.1.0-alpha...0.1.0-beta

View File

@ -1,6 +1,6 @@
[package]
name = "iced"
version = "0.1.1"
version = "0.3.0"
authors = ["Héctor Ramón Jiménez <hector0193@gmail.com>"]
edition = "2018"
description = "A cross-platform GUI library inspired by Elm"
@ -21,20 +21,28 @@ image = ["iced_wgpu/image"]
svg = ["iced_wgpu/svg"]
# Enables the `Canvas` widget
canvas = ["iced_wgpu/canvas"]
# Enables using system fonts.
# Enables the `QRCode` widget
qr_code = ["iced_wgpu/qr_code"]
# Enables using system fonts
default_system_font = ["iced_wgpu/default_system_font"]
# Enables the `iced_glow` renderer. Overrides `iced_wgpu`
glow = ["iced_glow", "iced_glutin"]
# Enables the `Canvas` widget for `iced_glow`
glow_canvas = ["iced_glow/canvas"]
# Enables using system fonts for `iced_glow`.
# Enables the `QRCode` widget for `iced_glow`
glow_qr_code = ["iced_glow/qr_code"]
# Enables using system fonts for `iced_glow`
glow_default_system_font = ["iced_glow/default_system_font"]
# Enables a debug view in native platforms (press F12)
debug = ["iced_winit/debug"]
# Enables `tokio` as the `executor::Default` on native platforms
tokio = ["iced_futures/tokio"]
# Enables old `tokio` (0.2) as the `executor::Default` on native platforms
tokio_old = ["iced_futures/tokio_old"]
# Enables `async-std` as the `executor::Default` on native platforms
async-std = ["iced_futures/async-std"]
# Enables `smol` as the `executor::Default` on native platforms
smol = ["iced_futures/smol"]
# Enables advanced color conversion via `palette`
palette = ["iced_core/palette"]
@ -67,28 +75,33 @@ members = [
"examples/pick_list",
"examples/pokedex",
"examples/progress_bar",
"examples/qr_code",
"examples/scrollable",
"examples/solar_system",
"examples/stopwatch",
"examples/styling",
"examples/svg",
"examples/todos",
"examples/tour",
"examples/tour_glow",
"examples/tooltip",
"examples/url_handler",
]
[dependencies]
iced_core = { version = "0.2", path = "core" }
iced_futures = { version = "0.1", path = "futures" }
iced_core = { version = "0.4", path = "core" }
iced_futures = { version = "0.3", path = "futures" }
thiserror = "1.0"
[target.'cfg(not(target_arch = "wasm32"))'.dependencies]
iced_winit = { version = "0.1", path = "winit" }
iced_glutin = { version = "0.1", path = "glutin", optional = true }
iced_wgpu = { version = "0.2", path = "wgpu", optional = true }
iced_glow = { version = "0.1", path = "glow", optional = true}
iced_winit = { version = "0.3", path = "winit" }
iced_glutin = { version = "0.2", path = "glutin", optional = true }
iced_wgpu = { version = "0.4", path = "wgpu", optional = true }
iced_glow = { version = "0.2", path = "glow", optional = true}
[target.'cfg(target_arch = "wasm32")'.dependencies]
iced_web = { version = "0.2", path = "web" }
iced_web = { version = "0.4", path = "web" }
[package.metadata.docs.rs]
rustdoc-args = ["--cfg", "docsrs"]
features = ["image", "svg", "canvas"]
features = ["image", "svg", "canvas", "qr_code"]

View File

@ -1,10 +1,7 @@
# Ecosystem
This document describes the Iced ecosystem.
It quickly lists the different audiences of the library and explains how the different crates relate to each other.
## Users
This document describes the Iced ecosystem and explains how the different crates relate to each other.
## Overview
Iced is meant to be used by 2 different types of users:
- __End-users__. They should be able to:
@ -18,71 +15,81 @@ Iced is meant to be used by 2 different types of users:
- integrate existing runtimes in their own system (like game engines),
- and create their own custom renderers.
## Crates
Iced consists of different crates which offer different layers of abstractions for our users. This modular architecture helps us keep implementation details hidden and decoupled, which should allow us to rewrite or change strategies in the future.
![Ecosystem graph](docs/graphs/ecosystem.png)
<p align="center">
<img alt="The Iced Ecosystem" src="docs/graphs/ecosystem.png" width="60%">
</p>
### [`iced_core`]
[`iced_core`] holds basic reusable types of the public API. For instance, basic data types like `Point`, `Rectangle`, `Length`, etc.
## The foundations
There are a bunch of concepts that permeate the whole ecosystem. These concepts are considered __the foundations__, and they are provided by three different crates:
This crate is meant to be a starting point for an Iced runtime.
- [`iced_core`] contains many lightweight, reusable primitives (e.g. `Point`, `Rectangle`, `Color`).
- [`iced_futures`] implements the concurrent concepts of [The Elm Architecture] on top of the [`futures`] ecosystem.
- [`iced_style`] defines the default styling capabilities of built-in widgets.
### [`iced_native`]
[`iced_native`] takes [`iced_core`] and builds a native runtime on top of it, featuring:
- A custom layout engine, greatly inspired by [`druid`]
- Event handling for all the built-in widgets
- A renderer-agnostic API
<p align="center">
<img alt="The foundations" src="docs/graphs/foundations.png" width="50%">
</p>
To achieve this, it introduces a bunch of reusable interfaces:
- A `Widget` trait, which is used to implement new widgets: from layout requirements to event and drawing logic.
- A bunch of `Renderer` traits, meant to keep the crate renderer-agnostic.
- A `Backend` trait, leveraging [`raw-window-handle`], which can be implemented by graphical renderers that target _windows_. Window-based shells (like [`iced_winit`]) can use this trait to stay renderer-agnostic.
## The native target
The native side of the ecosystem is split into two different groups: __renderers__ and __shells__.
[`druid`]: https://github.com/xi-editor/druid
[`raw-window-handle`]: https://github.com/rust-windowing/raw-window-handle
<p align="center">
<img alt="The native target" src="docs/graphs/native.png" width="80%">
</p>
### [`iced_web`]
[`iced_web`] takes [`iced_core`] and builds a WebAssembly runtime on top. It achieves this by introducing a `Widget` trait that can be used to produce VDOM nodes.
### Renderers
The widgets of a _graphical_ user interface produce some primitives that eventually need to be drawn on screen. __Renderers__ take care of this task, potentially leveraging GPU acceleration.
The crate is currently a simple abstraction layer over [`dodrio`].
Currently, there are two different official renderers:
[`dodrio`]: https://github.com/fitzgen/dodrio
- [`iced_wgpu`] is powered by [`wgpu`] and supports Vulkan, DirectX 12, and Metal.
- [`iced_glow`] is powered by [`glow`] and supports OpenGL 3.3+.
### [`iced_wgpu`]
[`iced_wgpu`] is a [`wgpu`] renderer for [`iced_native`]. For now, it is the default renderer of Iced in native platforms.
Additionally, the [`iced_graphics`] subcrate contains a bunch of backend-agnostic types that can be leveraged to build renderers. Both of the renderers rely on the graphical foundations provided by this crate.
[`wgpu`] supports most modern graphics backends: Vulkan, Metal, DX11, and DX12 (OpenGL and WebGL are still WIP). Additionally, it will support the incoming [WebGPU API].
### Shells
The widgets of a graphical user _interface_ are interactive. __Shells__ gather and process user interactions in an event loop.
Currently, [`iced_wgpu`] supports the following primitives:
- Text, which is rendered using [`wgpu_glyph`]. No shaping at all.
- Quads or rectangles, with rounded borders and a solid background color.
- Clip areas, useful to implement scrollables or hide overflowing content.
- Images and SVG, loaded from memory or the file system.
- Meshes of triangles, useful to draw geometry freely.
Normally, a shell will be responsible of creating a window and managing the lifecycle of a user interface, implementing a runtime of [The Elm Architecture].
[`wgpu`]: https://github.com/gfx-rs/wgpu-rs
[WebGPU API]: https://gpuweb.github.io/gpuweb/
[`wgpu_glyph`]: https://github.com/hecrj/wgpu_glyph
As of now, there are two official shells:
### [`iced_winit`]
[`iced_winit`] offers some convenient abstractions on top of [`iced_native`] to quickstart development when using [`winit`].
- [`iced_winit`] implements a shell runtime on top of [`winit`].
- [`iced_glutin`] is similar to [`iced_winit`], but it also deals with [OpenGL context creation].
It exposes a renderer-agnostic `Application` trait that can be implemented and then run with a simple call. The use of this trait is optional. A `conversion` module is provided for users that decide to implement a custom event loop.
## The web target
The Web platform provides all the abstractions necessary to draw widgets and gather users interactions.
[`winit`]: https://github.com/rust-windowing/winit
Therefore, unlike the native path, the web side of the ecosystem does not need to split renderers and shells. Instead, [`iced_web`] leverages [`dodrio`] to both render widgets and implement a proper runtime.
### [`iced`]
## Iced
Finally, [`iced`] unifies everything into a simple abstraction to create cross-platform applications:
- On native, it uses [`iced_winit`] and [`iced_wgpu`].
- On native, it uses __[shells](#shells)__ and __[renderers](#renderers)__.
- On the web, it uses [`iced_web`].
This is the crate meant to be used by __end-users__.
<p align="center">
<img alt="Iced" src="docs/graphs/iced.png" width="80%">
</p>
[`iced_core`]: core
[`iced_futures`]: futures
[`iced_style`]: style
[`iced_native`]: native
[`iced_web`]: web
[`iced_graphics`]: graphics
[`iced_wgpu`]: wgpu
[`iced_glow`]: glow
[`iced_winit`]: winit
[`iced_glutin`]: glutin
[`iced`]: ..
[`futures`]: https://github.com/rust-lang/futures-rs
[`glow`]: https://github.com/grovesNL/glow
[`wgpu`]: https://github.com/gfx-rs/wgpu-rs
[`winit`]: https://github.com/rust-windowing/winit
[`glutin`]: https://github.com/rust-windowing/glutin
[`dodrio`]: https://github.com/fitzgen/dodrio
[OpenGL context creation]: https://www.khronos.org/opengl/wiki/Creating_an_OpenGL_Context
[The Elm Architecture]: https://guide.elm-lang.org/architecture/

View File

@ -55,7 +55,7 @@ __Iced is currently experimental software.__ [Take a look at the roadmap],
Add `iced` as a dependency in your `Cargo.toml`:
```toml
iced = "0.1"
iced = "0.3"
```
__Iced moves fast and the `master` branch can contain breaking changes!__ If
@ -168,26 +168,19 @@ Browse the [documentation] and the [examples] to learn more!
Iced was originally born as an attempt at bringing the simplicity of [Elm] and
[The Elm Architecture] into [Coffee], a 2D game engine I am working on.
The core of the library was implemented during May in [this pull request].
The core of the library was implemented during May 2019 in [this pull request].
[The first alpha version] was eventually released as
[a renderer-agnostic GUI library]. The library did not provide a renderer and
implemented the current [tour example] on top of [`ggez`], a game library.
Since then, the focus has shifted towards providing a batteries-included,
end-user-oriented GUI library, while keeping [the ecosystem] modular.
end-user-oriented GUI library, while keeping [the ecosystem] modular:
Currently, Iced is a cross-platform GUI library built on top of smaller crates:
- [`iced_core`], a bunch of basic types that can be reused in different runtimes.
- [`iced_native`], a renderer-agnostic native runtime implementing widget
logic and a layout engine inspired by [`druid`].
- [`iced_web`], an experimental web runtime that targets the DOM thanks to
[`dodrio`].
- [`iced_wgpu`], a renderer leveraging [`wgpu`], [`wgpu_glyph`], and
[`font-kit`].
- [`iced_winit`], a windowing shell on top of [`winit`].
[![Iced ecosystem](docs/graphs/ecosystem.png)](https://github.com/hecrj/iced/blob/master/ECOSYSTEM.md)
<p align="center">
<a href="https://github.com/hecrj/iced/blob/master/ECOSYSTEM.md">
<img alt="Iced Ecosystem" src="docs/graphs/ecosystem.png" width="80%">
</a>
</p>
[this pull request]: https://github.com/hecrj/coffee/pull/35
[The first alpha version]: https://github.com/hecrj/iced/tree/0.1.0-alpha
@ -195,15 +188,6 @@ Currently, Iced is a cross-platform GUI library built on top of smaller crates:
[tour example]: https://github.com/hecrj/iced/blob/master/examples/README.md#tour
[`ggez`]: https://github.com/ggez/ggez
[the ecosystem]: https://github.com/hecrj/iced/blob/master/ECOSYSTEM.md
[`iced_core`]: https://github.com/hecrj/iced/tree/master/core
[`iced_native`]: https://github.com/hecrj/iced/tree/master/native
[`iced_web`]: https://github.com/hecrj/iced/tree/master/web
[`iced_wgpu`]: https://github.com/hecrj/iced/tree/master/wgpu
[`iced_winit`]: https://github.com/hecrj/iced/tree/master/winit
[`druid`]: https://github.com/xi-editor/druid
[`wgpu_glyph`]: https://github.com/hecrj/wgpu_glyph
[`font-kit`]: https://github.com/servo/font-kit
[`winit`]: https://github.com/rust-windowing/winit
## Contributing / Feedback
Contributions are greatly appreciated! If you want to contribute, please

View File

@ -6,7 +6,7 @@ Before diving into the roadmap, check out [the ecosystem overview] to get an ide
[the ecosystem overview]: ECOSYSTEM.md
## Next steps
Most of the work related to these features needs to happen in the `iced_native` path of the ecosystem, as the web already supports many of them.
Most of the work related to these features needs to happen in the __native__ path of the ecosystem, as the web already supports many of them.
Once a step is completed, it is collapsed and added to this list:
@ -17,6 +17,8 @@ Once a step is completed, it is collapsed and added to this list:
* [x] Custom layout engine ([#52])
* [x] Event subscriptions ([#122])
* [x] Custom styling ([#146])
* [x] Canvas for 2D graphics ([#193])
* [x] Basic overlay support ([#444])
[#24]: https://github.com/hecrj/iced/issues/24
[#25]: https://github.com/hecrj/iced/issues/25
@ -25,6 +27,8 @@ Once a step is completed, it is collapsed and added to this list:
[#52]: https://github.com/hecrj/iced/pull/52
[#122]: https://github.com/hecrj/iced/pull/122
[#146]: https://github.com/hecrj/iced/pull/146
[#193]: https://github.com/hecrj/iced/pull/193
[#444]: https://github.com/hecrj/iced/pull/444
### Multi-window support ([#27])
Open and control multiple windows at runtime.
@ -35,17 +39,6 @@ This approach should also allow us to perform custom optimizations for this part
[#27]: https://github.com/hecrj/iced/issues/27
### Layers ([#30])
Currently, Iced assumes widgets cannot be laid out on top of each other. We should implement support for multiple layers of widgets.
This is a necessary feature to implement many kinds of interactables, like dropdown menus, select fields, etc.
`iced_native` will need to group widgets to perform layouting and process some events first for widgets positioned on top.
`iced_wgpu` will also need to process the scene graph and sort draw calls based on the different layers.
[#30]: https://github.com/hecrj/iced/issues/30
### Animations ([#31])
Allow widgets to request a redraw at a specific time.
@ -55,8 +48,8 @@ This is a necessary feature to render loading spinners, a blinking text cursor,
[#31]: https://github.com/hecrj/iced/issues/31
### Canvas widget ([#32])
A widget to draw freely in 2D or 3D. It could be used to draw charts, implement a Paint clone, a CAD application, etc.
### Canvas widget for 3D graphics ([#32])
A widget to draw freely in 3D. It could be used to draw charts, implement a Paint clone, a CAD application, etc.
As a first approach, we could expose the underlying renderer directly here, and couple this widget with it ([`wgpu`] for now). Once [`wgpu`] gets WebGL or WebGPU support, this widget will be able to run on the web too. The renderer primitive could be a simple texture that the widget draws to.

View File

@ -1,6 +1,6 @@
[package]
name = "iced_core"
version = "0.2.1"
version = "0.4.0"
authors = ["Héctor Ramón Jiménez <hector0193@gmail.com>"]
edition = "2018"
description = "The essential concepts of Iced"

View File

@ -8,7 +8,9 @@
This crate is meant to be a starting point for an Iced runtime.
![iced_core](../docs/graphs/core.png)
<p align="center">
<img alt="The foundations" src="../docs/graphs/foundations.png" width="50%">
</p>
[documentation]: https://docs.rs/iced_core
@ -16,7 +18,7 @@ This crate is meant to be a starting point for an Iced runtime.
Add `iced_core` as a dependency in your `Cargo.toml`:
```toml
iced_core = "0.2"
iced_core = "0.4"
```
__Iced moves fast and the `master` branch can contain breaking changes!__ If

View File

@ -43,8 +43,6 @@ impl Color {
///
/// In debug mode, it will panic if the values are not in the correct
/// range: 0.0 - 1.0
///
/// [`Color`]: struct.Color.html
pub fn new(r: f32, g: f32, b: f32, a: f32) -> Color {
debug_assert!(
(0.0..=1.0).contains(&r),
@ -67,29 +65,21 @@ impl Color {
}
/// Creates a [`Color`] from its RGB components.
///
/// [`Color`]: struct.Color.html
pub const fn from_rgb(r: f32, g: f32, b: f32) -> Color {
Color::from_rgba(r, g, b, 1.0f32)
}
/// Creates a [`Color`] from its RGBA components.
///
/// [`Color`]: struct.Color.html
pub const fn from_rgba(r: f32, g: f32, b: f32, a: f32) -> Color {
Color { r, g, b, a }
}
/// Creates a [`Color`] from its RGB8 components.
///
/// [`Color`]: struct.Color.html
pub fn from_rgb8(r: u8, g: u8, b: u8) -> Color {
Color::from_rgba8(r, g, b, 1.0)
}
/// Creates a [`Color`] from its RGB8 components and an alpha value.
///
/// [`Color`]: struct.Color.html
pub fn from_rgba8(r: u8, g: u8, b: u8, a: f32) -> Color {
Color {
r: f32::from(r) / 255.0,
@ -100,8 +90,6 @@ impl Color {
}
/// Converts the [`Color`] into its linear values.
///
/// [`Color`]: struct.Color.html
pub fn into_linear(self) -> [f32; 4] {
// As described in:
// https://en.wikipedia.org/wiki/SRGB#The_reverse_transformation
@ -122,8 +110,6 @@ impl Color {
}
/// Inverts the [`Color`] in-place.
///
/// [`Color`]: struct.Color.html
pub fn invert(&mut self) {
self.r = 1.0f32 - self.r;
self.b = 1.0f32 - self.g;
@ -131,8 +117,6 @@ impl Color {
}
/// Returns the inverted [`Color`].
///
/// [`Color`]: struct.Color.html
pub fn inverse(self) -> Color {
Color::new(1.0f32 - self.r, 1.0f32 - self.g, 1.0f32 - self.b, self.a)
}
@ -152,8 +136,6 @@ impl From<[f32; 4]> for Color {
#[cfg(feature = "palette")]
/// Converts from palette's `Srgba` type to a [`Color`].
///
/// [`Color`]: struct.Color.html
impl From<Srgba> for Color {
fn from(srgba: Srgba) -> Self {
Color::new(srgba.red, srgba.green, srgba.blue, srgba.alpha)
@ -162,8 +144,6 @@ impl From<Srgba> for Color {
#[cfg(feature = "palette")]
/// Converts from [`Color`] to palette's `Srgba` type.
///
/// [`Color`]: struct.Color.html
impl From<Color> for Srgba {
fn from(c: Color) -> Self {
Srgba::new(c.r, c.g, c.b, c.a)
@ -172,8 +152,6 @@ impl From<Color> for Srgba {
#[cfg(feature = "palette")]
/// Converts from palette's `Srgb` type to a [`Color`].
///
/// [`Color`]: struct.Color.html
impl From<Srgb> for Color {
fn from(srgb: Srgb) -> Self {
Color::new(srgb.red, srgb.green, srgb.blue, 1.0)
@ -182,9 +160,6 @@ impl From<Srgb> for Color {
#[cfg(feature = "palette")]
/// Converts from [`Color`] to palette's `Srgb` type.
///
/// [`Color`]: struct.Color.html
/// [`Srgb`]: ../palette/rgb/type.Srgb.html
impl From<Color> for Srgb {
fn from(c: Color) -> Self {
Srgb::new(c.r, c.g, c.b)

View File

@ -1,8 +1,8 @@
//! Reuse basic keyboard types.
mod event;
mod key_code;
mod modifiers_state;
mod modifiers;
pub use event::Event;
pub use key_code::KeyCode;
pub use modifiers_state::ModifiersState;
pub use modifiers::Modifiers;

View File

@ -1,4 +1,4 @@
use super::{KeyCode, ModifiersState};
use super::{KeyCode, Modifiers};
/// A keyboard event.
///
@ -14,7 +14,7 @@ pub enum Event {
key_code: KeyCode,
/// The state of the modifier keys
modifiers: ModifiersState,
modifiers: Modifiers,
},
/// A keyboard key was released.
@ -23,12 +23,12 @@ pub enum Event {
key_code: KeyCode,
/// The state of the modifier keys
modifiers: ModifiersState,
modifiers: Modifiers,
},
/// A unicode character was received.
CharacterReceived(char),
/// The keyboard modifiers have changed.
ModifiersChanged(ModifiersState),
ModifiersChanged(Modifiers),
}

View File

@ -55,7 +55,7 @@ pub enum KeyCode {
Y,
Z,
/// The Escape key, next to F1
/// The Escape key, next to F1.
Escape,
F1,
@ -83,14 +83,14 @@ pub enum KeyCode {
F23,
F24,
/// Print Screen/SysRq
/// Print Screen/SysRq.
Snapshot,
/// Scroll Lock
/// Scroll Lock.
Scroll,
/// Pause/Break key, next to Scroll lock
/// Pause/Break key, next to Scroll lock.
Pause,
/// `Insert`, next to Backspace
/// `Insert`, next to Backspace.
Insert,
Home,
Delete,
@ -103,11 +103,14 @@ pub enum KeyCode {
Right,
Down,
/// The Backspace key, right over Enter.
Backspace,
/// The Enter key.
Enter,
/// The space bar.
Space,
/// The "Compose" key on Linux
/// The "Compose" key on Linux.
Compose,
Caret,
@ -123,12 +126,20 @@ pub enum KeyCode {
Numpad7,
Numpad8,
Numpad9,
NumpadAdd,
NumpadDivide,
NumpadDecimal,
NumpadComma,
NumpadEnter,
NumpadEquals,
NumpadMultiply,
NumpadSubtract,
AbntC1,
AbntC2,
Add,
Apostrophe,
Apps,
Asterisk,
At,
Ax,
Backslash,
@ -137,8 +148,6 @@ pub enum KeyCode {
Colon,
Comma,
Convert,
Decimal,
Divide,
Equals,
Grave,
Kana,
@ -152,19 +161,16 @@ pub enum KeyCode {
MediaSelect,
MediaStop,
Minus,
Multiply,
Mute,
MyComputer,
NavigateForward, // also called "Prior"
NavigateBackward, // also called "Next"
NavigateForward, // also called "Next"
NavigateBackward, // also called "Prior"
NextTrack,
NoConvert,
NumpadComma,
NumpadEnter,
NumpadEquals,
OEM102,
Period,
PlayPause,
Plus,
Power,
PrevTrack,
RAlt,
@ -176,7 +182,6 @@ pub enum KeyCode {
Slash,
Sleep,
Stop,
Subtract,
Sysrq,
Tab,
Underline,

View File

@ -0,0 +1,45 @@
/// The current state of the keyboard modifiers.
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
pub struct Modifiers {
/// Whether a shift key is pressed
pub shift: bool,
/// Whether a control key is pressed
pub control: bool,
/// Whether an alt key is pressed
pub alt: bool,
/// Whether a logo key is pressed (e.g. windows key, command key...)
pub logo: bool,
}
impl Modifiers {
/// Returns true if a "command key" is pressed in the [`Modifiers`].
///
/// The "command key" is the main modifier key used to issue commands in the
/// current platform. Specifically:
///
/// - It is the `logo` or command key (⌘) on macOS
/// - It is the `control` key on other platforms
pub fn is_command_pressed(self) -> bool {
#[cfg(target_os = "macos")]
let is_pressed = self.logo;
#[cfg(not(target_os = "macos"))]
let is_pressed = self.control;
is_pressed
}
/// Returns true if the current [`Modifiers`] have at least the same
/// keys pressed as the provided ones, and false otherwise.
pub fn matches(&self, modifiers: Self) -> bool {
let shift = !modifiers.shift || self.shift;
let control = !modifiers.control || self.control;
let alt = !modifiers.alt || self.alt;
let logo = !modifiers.logo || self.logo;
shift && control && alt && logo
}
}

View File

@ -1,30 +0,0 @@
/// The current state of the keyboard modifiers.
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
pub struct ModifiersState {
/// Whether a shift key is pressed
pub shift: bool,
/// Whether a control key is pressed
pub control: bool,
/// Whether an alt key is pressed
pub alt: bool,
/// Whether a logo key is pressed (e.g. windows key, command key...)
pub logo: bool,
}
impl ModifiersState {
/// Returns true if the current [`ModifiersState`] has at least the same
/// modifiers enabled as the given value, and false otherwise.
///
/// [`ModifiersState`]: struct.ModifiersState.html
pub fn matches(&self, modifiers: ModifiersState) -> bool {
let shift = !modifiers.shift || self.shift;
let control = !modifiers.control || self.control;
let alt = !modifiers.alt || self.alt;
let logo = !modifiers.logo || self.logo;
shift && control && alt && logo
}
}

View File

@ -26,8 +26,6 @@ impl Length {
/// The _fill factor_ is a relative unit describing how much of the
/// remaining space should be filled when compared to other elements. It
/// is only meant to be used by layout engines.
///
/// [`Length`]: enum.Length.html
pub fn fill_factor(&self) -> u16 {
match self {
Length::Fill => 1,

View File

@ -1,11 +1,11 @@
//! The core library of [Iced].
//!
//! ![`iced_core` crate graph](https://github.com/hecrj/iced/blob/cae26cb7bc627f4a5b3bcf1cd023a0c552e8c65e/docs/graphs/core.png?raw=true)
//!
//! This library holds basic types that can be reused and re-exported in
//! different runtime implementations. For instance, both [`iced_native`] and
//! [`iced_web`] are built on top of `iced_core`.
//!
//! ![The foundations of the Iced ecosystem](https://github.com/hecrj/iced/blob/0525d76ff94e828b7b21634fa94a747022001c83/docs/graphs/foundations.png?raw=true)
//!
//! [Iced]: https://github.com/hecrj/iced
//! [`iced_native`]: https://github.com/hecrj/iced/tree/master/native
//! [`iced_web`]: https://github.com/hecrj/iced/tree/master/web
@ -22,6 +22,7 @@ mod background;
mod color;
mod font;
mod length;
mod padding;
mod point;
mod rectangle;
mod size;
@ -32,6 +33,7 @@ pub use background::Background;
pub use color::Color;
pub use font::Font;
pub use length::Length;
pub use padding::Padding;
pub use point::Point;
pub use rectangle::Rectangle;
pub use size::Size;

View File

@ -1,3 +1,5 @@
use crate::Point;
use super::Button;
/// A mouse event.
@ -16,11 +18,8 @@ pub enum Event {
/// The mouse cursor was moved
CursorMoved {
/// The X coordinate of the mouse position
x: f32,
/// The Y coordinate of the mouse position
y: f32,
/// The new position of the mouse cursor
position: Point,
},
/// A mouse button was pressed.

107
core/src/padding.rs Normal file
View File

@ -0,0 +1,107 @@
/// An amount of space to pad for each side of a box
///
/// You can leverage the `From` trait to build [`Padding`] conveniently:
///
/// ```
/// # use iced_core::Padding;
/// #
/// let padding = Padding::from(20); // 20px on all sides
/// let padding = Padding::from([10, 20]); // top/bottom, left/right
/// let padding = Padding::from([5, 10, 15, 20]); // top, right, bottom, left
/// ```
///
/// Normally, the `padding` method of a widget will ask for an `Into<Padding>`,
/// so you can easily write:
///
/// ```
/// # use iced_core::Padding;
/// #
/// # struct Widget;
/// #
/// impl Widget {
/// # pub fn new() -> Self { Self }
/// #
/// pub fn padding(mut self, padding: impl Into<Padding>) -> Self {
/// // ...
/// self
/// }
/// }
///
/// let widget = Widget::new().padding(20); // 20px on all sides
/// let widget = Widget::new().padding([10, 20]); // top/bottom, left/right
/// let widget = Widget::new().padding([5, 10, 15, 20]); // top, right, bottom, left
/// ```
#[derive(Debug, Hash, Copy, Clone)]
pub struct Padding {
/// Top padding
pub top: u16,
/// Right padding
pub right: u16,
/// Bottom padding
pub bottom: u16,
/// Left padding
pub left: u16,
}
impl Padding {
/// Padding of zero
pub const ZERO: Padding = Padding {
top: 0,
right: 0,
bottom: 0,
left: 0,
};
/// Create a Padding that is equal on all sides
pub const fn new(padding: u16) -> Padding {
Padding {
top: padding,
right: padding,
bottom: padding,
left: padding,
}
}
/// Returns the total amount of vertical [`Padding`].
pub fn vertical(self) -> u16 {
self.top + self.bottom
}
/// Returns the total amount of horizontal [`Padding`].
pub fn horizontal(self) -> u16 {
self.left + self.right
}
}
impl std::convert::From<u16> for Padding {
fn from(p: u16) -> Self {
Padding {
top: p,
right: p,
bottom: p,
left: p,
}
}
}
impl std::convert::From<[u16; 2]> for Padding {
fn from(p: [u16; 2]) -> Self {
Padding {
top: p[0],
right: p[1],
bottom: p[0],
left: p[1],
}
}
}
impl std::convert::From<[u16; 4]> for Padding {
fn from(p: [u16; 4]) -> Self {
Padding {
top: p[0],
right: p[1],
bottom: p[2],
left: p[3],
}
}
}

View File

@ -12,20 +12,14 @@ pub struct Point {
impl Point {
/// The origin (i.e. a [`Point`] at (0, 0)).
///
/// [`Point`]: struct.Point.html
pub const ORIGIN: Point = Point::new(0.0, 0.0);
/// Creates a new [`Point`] with the given coordinates.
///
/// [`Point`]: struct.Point.html
pub const fn new(x: f32, y: f32) -> Self {
Self { x, y }
}
/// Computes the distance to another [`Point`].
///
/// [`Point`]: struct.Point.html
pub fn distance(&self, to: Point) -> f32 {
let a = self.x - to.x;
let b = self.y - to.y;
@ -46,6 +40,12 @@ impl From<[u16; 2]> for Point {
}
}
impl From<Point> for [f32; 2] {
fn from(point: Point) -> [f32; 2] {
[point.x, point.y]
}
}
impl std::ops::Add<Vector> for Point {
type Output = Self;

View File

@ -19,10 +19,6 @@ pub struct Rectangle<T = f32> {
impl Rectangle<f32> {
/// Creates a new [`Rectangle`] with its top-left corner in the given
/// [`Point`] and with the provided [`Size`].
///
/// [`Rectangle`]: struct.Rectangle.html
/// [`Point`]: struct.Point.html
/// [`Size`]: struct.Size.html
pub fn new(top_left: Point, size: Size) -> Self {
Self {
x: top_left.x,
@ -34,9 +30,6 @@ impl Rectangle<f32> {
/// Creates a new [`Rectangle`] with its top-left corner at the origin
/// and with the provided [`Size`].
///
/// [`Rectangle`]: struct.Rectangle.html
/// [`Size`]: struct.Size.html
pub fn with_size(size: Size) -> Self {
Self {
x: 0.0,
@ -47,50 +40,33 @@ impl Rectangle<f32> {
}
/// Returns the [`Point`] at the center of the [`Rectangle`].
///
/// [`Point`]: struct.Point.html
/// [`Rectangle`]: struct.Rectangle.html
pub fn center(&self) -> Point {
Point::new(self.center_x(), self.center_y())
}
/// Returns the X coordinate of the [`Point`] at the center of the
/// [`Rectangle`].
///
/// [`Point`]: struct.Point.html
/// [`Rectangle`]: struct.Rectangle.html
pub fn center_x(&self) -> f32 {
self.x + self.width / 2.0
}
/// Returns the Y coordinate of the [`Point`] at the center of the
/// [`Rectangle`].
///
/// [`Point`]: struct.Point.html
/// [`Rectangle`]: struct.Rectangle.html
pub fn center_y(&self) -> f32 {
self.y + self.height / 2.0
}
/// Returns the position of the top left corner of the [`Rectangle`].
///
/// [`Rectangle`]: struct.Rectangle.html
pub fn position(&self) -> Point {
Point::new(self.x, self.y)
}
/// Returns the [`Size`] of the [`Rectangle`].
///
/// [`Size`]: struct.Size.html
/// [`Rectangle`]: struct.Rectangle.html
pub fn size(&self) -> Size {
Size::new(self.width, self.height)
}
/// Returns true if the given [`Point`] is contained in the [`Rectangle`].
///
/// [`Point`]: struct.Point.html
/// [`Rectangle`]: struct.Rectangle.html
pub fn contains(&self, point: Point) -> bool {
self.x <= point.x
&& point.x <= self.x + self.width
@ -99,8 +75,6 @@ impl Rectangle<f32> {
}
/// Computes the intersection with the given [`Rectangle`].
///
/// [`Rectangle`]: struct.Rectangle.html
pub fn intersection(
&self,
other: &Rectangle<f32>,
@ -127,14 +101,12 @@ impl Rectangle<f32> {
}
/// Snaps the [`Rectangle`] to __unsigned__ integer coordinates.
///
/// [`Rectangle`]: struct.Rectangle.html
pub fn snap(self) -> Rectangle<u32> {
Rectangle {
x: self.x as u32,
y: self.y as u32,
width: self.width.ceil() as u32,
height: self.height.ceil() as u32,
width: self.width as u32,
height: self.height as u32,
}
}
}

View File

@ -1,3 +1,4 @@
use crate::{Padding, Vector};
use std::f32;
/// An amount of space in 2 dimensions.
@ -11,8 +12,6 @@ pub struct Size<T = f32> {
impl<T> Size<T> {
/// Creates a new [`Size`] with the given width and height.
///
/// [`Size`]: struct.Size.html
pub const fn new(width: T, height: T) -> Self {
Size { width, height }
}
@ -20,27 +19,19 @@ impl<T> Size<T> {
impl Size {
/// A [`Size`] with zero width and height.
///
/// [`Size`]: struct.Size.html
pub const ZERO: Size = Size::new(0., 0.);
/// A [`Size`] with a width and height of 1 unit.
///
/// [`Size`]: struct.Size.html
pub const UNIT: Size = Size::new(1., 1.);
/// A [`Size`] with infinite width and height.
///
/// [`Size`]: struct.Size.html
pub const INFINITY: Size = Size::new(f32::INFINITY, f32::INFINITY);
/// Increments the [`Size`] to account for the given padding.
///
/// [`Size`]: struct.Size.html
pub fn pad(&self, padding: f32) -> Self {
pub fn pad(&self, padding: Padding) -> Self {
Size {
width: self.width + padding * 2.0,
height: self.height + padding * 2.0,
width: self.width + padding.horizontal() as f32,
height: self.height + padding.vertical() as f32,
}
}
}
@ -56,3 +47,24 @@ impl From<[u16; 2]> for Size {
Size::new(width.into(), height.into())
}
}
impl From<Vector<f32>> for Size {
fn from(vector: Vector<f32>) -> Self {
Size {
width: vector.x,
height: vector.y,
}
}
}
impl From<Size> for [f32; 2] {
fn from(size: Size) -> [f32; 2] {
[size.width, size.height]
}
}
impl From<Size> for Vector<f32> {
fn from(size: Size) -> Self {
Vector::new(size.width, size.height)
}
}

View File

@ -2,20 +2,14 @@
#[derive(Debug, Clone, Copy, PartialEq)]
pub struct Vector<T = f32> {
/// The X component of the [`Vector`]
///
/// [`Vector`]: struct.Vector.html
pub x: T,
/// The Y component of the [`Vector`]
///
/// [`Vector`]: struct.Vector.html
pub y: T,
}
impl<T> Vector<T> {
/// Creates a new [`Vector`] with the given components.
///
/// [`Vector`]: struct.Vector.html
pub const fn new(x: T, y: T) -> Self {
Self { x, y }
}
@ -65,3 +59,18 @@ where
}
}
}
impl<T> From<[T; 2]> for Vector<T> {
fn from([x, y]: [T; 2]) -> Self {
Self::new(x, y)
}
}
impl<T> From<Vector<T>> for [T; 2]
where
T: Copy,
{
fn from(other: Vector<T>) -> Self {
[other.x, other.y]
}
}

View File

@ -1,13 +0,0 @@
digraph G {
fontname = "Roboto";
newrank=true;
node[fontname = "Roboto", style="filled", fontcolor="#333333", fillcolor=white, color="#333333"];
edge[color="#333333"];
{ rank = same; iced_native iced_web }
iced_core -> iced_native [style=dashed];
iced_core -> iced_web [style=dashed];
iced_core [style=dashed];
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 13 KiB

View File

@ -1,56 +0,0 @@
digraph G {
fontname = "Roboto";
newrank=true;
node[fontname = "Roboto", style="filled", fontcolor="#333333", fillcolor=white, color="#333333"];
edge[color="#333333"];
subgraph cluster_1 {
label = "renderers ";
labelloc = "b";
labeljust = "r";
fontcolor = "#0366d6";
color="#f6f8fa";
bgcolor="#f6f8fa";
style=rounded;
etc_1 [label="...", style=solid, shape=none];
iced_wgpu;
}
subgraph cluster_2 {
label = "shells ";
labelloc = "b";
labeljust = "r";
fontcolor = "#0366d6";
color="#f6f8fa";
bgcolor="#f6f8fa";
style=rounded;
etc_2 [label="...", style=solid, shape=none];
iced_winit;
}
subgraph cluster_3 {
style=invis;
margin=20;
iced;
}
{ rank = same; iced_native iced_web }
{ rank = same; iced_wgpu iced_winit etc_1 etc_2 }
iced_core -> iced_native [style=dashed];
iced_core -> iced_web [style=dashed];
iced_native -> iced_wgpu;
iced_native -> iced_winit;
iced_winit -> iced;
iced_wgpu -> iced;
iced_web -> iced;
iced -> "cross-platform application";
iced_core [style=dashed];
"cross-platform application" [shape=box, width=2.8, height=0.6];
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 44 KiB

After

Width:  |  Height:  |  Size: 124 KiB

BIN
docs/graphs/foundations.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

View File

@ -1,6 +0,0 @@
#!/usr/bin/env bash
for file in *.dot
do
dot -Tpng ${file} -o ${file%.*}.png
done

View File

@ -1,46 +0,0 @@
digraph G {
fontname = "Roboto";
newrank=true;
node[fontname = "Roboto", style="filled", fontcolor="#333333", fillcolor=white, color="#333333"];
edge[color="#333333"];
subgraph cluster_1 {
label = "renderers ";
labelloc = "b";
labeljust = "r";
fontcolor = "#0366d6";
color="#f6f8fa";
bgcolor="#f6f8fa";
style=rounded;
etc_1 [label="...", style=solid, shape=none];
iced_wgpu;
}
subgraph cluster_2 {
label = "shells ";
labelloc = "b";
labeljust = "r";
fontcolor = "#0366d6";
color="#f6f8fa";
bgcolor="#f6f8fa";
style=rounded;
etc_2 [label="...", style=solid, shape=none];
iced_winit;
}
subgraph cluster_3 {
style=invis;
margin=20;
iced;
}
{ rank = same; iced_wgpu iced_winit etc_1 etc_2 }
iced_winit -> iced;
iced_wgpu -> iced;
iced_web -> iced;
iced;
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 26 KiB

After

Width:  |  Height:  |  Size: 98 KiB

View File

@ -1,41 +0,0 @@
digraph G {
fontname = "Roboto";
newrank=true;
node[fontname = "Roboto", style="filled", fontcolor="#333333", fillcolor=white, color="#333333"];
edge[color="#333333"];
subgraph cluster_1 {
label = "renderers ";
labelloc = "b";
labeljust = "r";
fontcolor = "#0366d6";
color="#f6f8fa";
bgcolor="#f6f8fa";
style=rounded;
etc_1 [label="...", style=solid, shape=none];
iced_wgpu;
}
subgraph cluster_2 {
label = "shells ";
labelloc = "b";
labeljust = "r";
fontcolor = "#0366d6";
color="#f6f8fa";
bgcolor="#f6f8fa";
style=rounded;
etc_2 [label="...", style=solid, shape=none];
iced_winit;
}
{ rank = same; iced_wgpu iced_winit etc_1 etc_2 }
iced_core -> iced_native [style=dashed];
iced_native -> iced_wgpu;
iced_native -> iced_winit;
iced_core [style=dashed];
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 24 KiB

After

Width:  |  Height:  |  Size: 58 KiB

View File

@ -1,12 +0,0 @@
digraph G {
fontname = "Roboto";
newrank=true;
node[fontname = "Roboto", style="filled", fontcolor="#333333", fillcolor=white, color="#333333"];
edge[color="#333333"];
iced_core -> iced_web [style=dashed];
iced_web -> iced;
iced_core [style=dashed];
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 11 KiB

View File

@ -1,31 +0,0 @@
digraph G {
fontname = "Roboto";
newrank=true;
node[fontname = "Roboto", style="filled", fontcolor="#333333", fillcolor=white, color="#333333"];
edge[color="#333333"];
subgraph cluster_1 {
label = "renderers ";
labelloc = "b";
labeljust = "r";
fontcolor = "#0366d6";
color="#f6f8fa";
bgcolor="#f6f8fa";
style=rounded;
etc_1 [label="...", style=solid, shape=none];
iced_wgpu;
}
subgraph cluster_3 {
style=invis;
margin=20;
iced;
}
{ rank = same; iced_wgpu etc_1 }
iced_native -> iced_wgpu;
iced_wgpu -> iced;
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 16 KiB

View File

@ -1,31 +0,0 @@
digraph G {
fontname = "Roboto";
newrank=true;
node[fontname = "Roboto", style="filled", fontcolor="#333333", fillcolor=white, color="#333333"];
edge[color="#333333"];
subgraph cluster_2 {
label = "shells ";
labelloc = "b";
labeljust = "r";
fontcolor = "#0366d6";
color="#f6f8fa";
bgcolor="#f6f8fa";
style=rounded;
etc_2 [label="...", style=solid, shape=none];
iced_winit;
}
subgraph cluster_3 {
style=invis;
margin=20;
iced;
}
{ rank = same; iced_winit etc_2 }
iced_native -> iced_winit;
iced_winit -> iced;
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 16 KiB

View File

@ -103,6 +103,7 @@ A bunch of simpler examples exist:
- [`pick_list`](pick_list), a dropdown list of selectable options.
- [`pokedex`](pokedex), an application that displays a random Pokédex entry (sprite included!) by using the [PokéAPI].
- [`progress_bar`](progress_bar), a simple progress bar that can be filled by using a slider.
- [`scrollable`](scrollable), a showcase of the various scrollbar width options.
- [`solar_system`](solar_system), an animated solar system drawn using the `Canvas` widget and showcasing how to compose different transforms.
- [`stopwatch`](stopwatch), a watch with start/stop and reset buttons showcasing how to listen to time.
- [`svg`](svg), an application that renders the [Ghostscript Tiger] by leveraging the `Svg` widget.
@ -117,7 +118,7 @@ cargo run --package <example>
[Ghostscript Tiger]: https://commons.wikimedia.org/wiki/File:Ghostscript_Tiger.svg
## [Coffee]
Since [Iced was born in May], it has been powering the user interfaces in
Since [Iced was born in May 2019], it has been powering the user interfaces in
[Coffee], an experimental 2D game engine.
@ -127,6 +128,6 @@ Since [Iced was born in May], it has been powering the user interfaces in
</a>
</div>
[Iced was born in May]: https://github.com/hecrj/coffee/pull/35
[Iced was born in May 2019]: https://github.com/hecrj/coffee/pull/35
[`ui` module]: https://docs.rs/coffee/0.3.2/coffee/ui/index.html
[Coffee]: https://github.com/hecrj/coffee

View File

@ -69,7 +69,8 @@ impl Sandbox for Example {
mod bezier {
use iced::{
canvas::{self, Canvas, Cursor, Event, Frame, Geometry, Path, Stroke},
canvas::event::{self, Event},
canvas::{self, Canvas, Cursor, Frame, Geometry, Path, Stroke},
mouse, Element, Length, Point, Rectangle,
};
@ -109,41 +110,51 @@ mod bezier {
event: Event,
bounds: Rectangle,
cursor: Cursor,
) -> Option<Curve> {
let cursor_position = cursor.position_in(&bounds)?;
) -> (event::Status, Option<Curve>) {
let cursor_position =
if let Some(position) = cursor.position_in(&bounds) {
position
} else {
return (event::Status::Ignored, None);
};
match event {
Event::Mouse(mouse_event) => match mouse_event {
mouse::Event::ButtonPressed(mouse::Button::Left) => {
match self.state.pending {
None => {
self.state.pending = Some(Pending::One {
from: cursor_position,
});
None
}
Some(Pending::One { from }) => {
self.state.pending = Some(Pending::Two {
from,
to: cursor_position,
});
Event::Mouse(mouse_event) => {
let message = match mouse_event {
mouse::Event::ButtonPressed(mouse::Button::Left) => {
match self.state.pending {
None => {
self.state.pending = Some(Pending::One {
from: cursor_position,
});
None
}
Some(Pending::Two { from, to }) => {
self.state.pending = None;
None
}
Some(Pending::One { from }) => {
self.state.pending = Some(Pending::Two {
from,
to: cursor_position,
});
Some(Curve {
from,
to,
control: cursor_position,
})
None
}
Some(Pending::Two { from, to }) => {
self.state.pending = None;
Some(Curve {
from,
to,
control: cursor_position,
})
}
}
}
}
_ => None,
},
_ => None,
_ => None,
};
(event::Status::Captured, message)
}
_ => (event::Status::Ignored, None),
}
}

View File

@ -1,7 +1,7 @@
use iced::{
canvas::{self, Cache, Canvas, Cursor, Geometry, LineCap, Path, Stroke},
executor, time, Application, Color, Command, Container, Element, Length,
Point, Rectangle, Settings, Subscription, Vector,
executor, time, Application, Clipboard, Color, Command, Container, Element,
Length, Point, Rectangle, Settings, Subscription, Vector,
};
pub fn main() -> iced::Result {
@ -40,7 +40,11 @@ impl Application for Clock {
String::from("Clock - Iced")
}
fn update(&mut self, message: Message) -> Command<Message> {
fn update(
&mut self,
message: Message,
_clipboard: &mut Clipboard,
) -> Command<Message> {
match message {
Message::Tick(local_time) => {
let now = local_time;

View File

@ -284,7 +284,7 @@ impl<C: 'static + ColorSpace + Copy> ColorPicker<C> {
let [s1, s2, s3] = &mut self.sliders;
let [cr1, cr2, cr3] = C::COMPONENT_RANGES;
fn slider<C>(
fn slider<C: Clone>(
state: &mut slider::State,
range: RangeInclusive<f64>,
component: f32,

View File

@ -16,11 +16,11 @@ mod circle {
};
pub struct Circle {
radius: u16,
radius: f32,
}
impl Circle {
pub fn new(radius: u16) -> Self {
pub fn new(radius: f32) -> Self {
Self { radius }
}
}
@ -42,16 +42,13 @@ mod circle {
_renderer: &Renderer<B>,
_limits: &layout::Limits,
) -> layout::Node {
layout::Node::new(Size::new(
f32::from(self.radius) * 2.0,
f32::from(self.radius) * 2.0,
))
layout::Node::new(Size::new(self.radius * 2.0, self.radius * 2.0))
}
fn hash_layout(&self, state: &mut Hasher) {
use std::hash::Hash;
self.radius.hash(state);
self.radius.to_bits().hash(state);
}
fn draw(
@ -67,7 +64,7 @@ mod circle {
bounds: layout.bounds(),
background: Background::Color(Color::BLACK),
border_radius: self.radius,
border_width: 0,
border_width: 0.0,
border_color: Color::TRANSPARENT,
},
mouse::Interaction::default(),
@ -96,7 +93,7 @@ pub fn main() -> iced::Result {
}
struct Example {
radius: u16,
radius: f32,
slider: slider::State,
}
@ -110,7 +107,7 @@ impl Sandbox for Example {
fn new() -> Self {
Example {
radius: 50,
radius: 50.0,
slider: slider::State::new(),
}
}
@ -122,7 +119,7 @@ impl Sandbox for Example {
fn update(&mut self, message: Message) {
match message {
Message::RadiusChanged(radius) => {
self.radius = radius.round() as u16;
self.radius = radius;
}
}
}
@ -134,13 +131,16 @@ impl Sandbox for Example {
.max_width(500)
.align_items(Align::Center)
.push(Circle::new(self.radius))
.push(Text::new(format!("Radius: {}", self.radius.to_string())))
.push(Slider::new(
&mut self.slider,
1.0..=100.0,
f32::from(self.radius),
Message::RadiusChanged,
));
.push(Text::new(format!("Radius: {:.2}", self.radius)))
.push(
Slider::new(
&mut self.slider,
1.0..=100.0,
self.radius,
Message::RadiusChanged,
)
.step(0.01),
);
Container::new(content)
.width(Length::Fill)

View File

@ -1,7 +1,7 @@
[package]
name = "download_progress"
version = "0.1.0"
authors = ["Songtronix <contact@songtronix.com>"]
authors = ["Songtronix <contact@songtronix.com>", "Folyd <lyshuhow@gmail.com>"]
edition = "2018"
publish = false
@ -9,4 +9,4 @@ publish = false
iced = { path = "../..", features = ["tokio"] }
iced_native = { path = "../../native" }
iced_futures = { path = "../../futures" }
reqwest = "0.10"
reqwest = "0.11"

View File

@ -1,6 +1,6 @@
## Download progress
A basic application that asynchronously downloads a dummy file of 100 MB and tracks the download progress.
A basic application that asynchronously downloads multiple dummy files of 100 MB and tracks the download progress.
The example implements a custom `Subscription` in the __[`download`](src/download.rs)__ module. This subscription downloads and produces messages that can be used to keep track of its progress.

View File

@ -1,37 +1,46 @@
use iced_futures::futures;
use std::hash::{Hash, Hasher};
// Just a little utility function
pub fn file<T: ToString>(url: T) -> iced::Subscription<Progress> {
pub fn file<I: 'static + Hash + Copy + Send, T: ToString>(
id: I,
url: T,
) -> iced::Subscription<(I, Progress)> {
iced::Subscription::from_recipe(Download {
id,
url: url.to_string(),
})
}
pub struct Download {
pub struct Download<I> {
id: I,
url: String,
}
// Make sure iced can use our download stream
impl<H, I> iced_native::subscription::Recipe<H, I> for Download
impl<H, I, T> iced_native::subscription::Recipe<H, I> for Download<T>
where
H: std::hash::Hasher,
T: 'static + Hash + Copy + Send,
H: Hasher,
{
type Output = Progress;
type Output = (T, Progress);
fn hash(&self, state: &mut H) {
use std::hash::Hash;
struct Marker;
std::any::TypeId::of::<Marker>().hash(state);
std::any::TypeId::of::<Self>().hash(state);
self.url.hash(state);
self.id.hash(state);
}
fn stream(
self: Box<Self>,
_input: futures::stream::BoxStream<'static, I>,
) -> futures::stream::BoxStream<'static, Self::Output> {
let id = self.id;
Box::pin(futures::stream::unfold(
State::Ready(self.url),
|state| async move {
move |state| async move {
match state {
State::Ready(url) => {
let response = reqwest::get(&url).await;
@ -40,7 +49,7 @@ where
Ok(response) => {
if let Some(total) = response.content_length() {
Some((
Progress::Started,
(id, Progress::Started),
State::Downloading {
response,
total,
@ -48,11 +57,14 @@ where
},
))
} else {
Some((Progress::Errored, State::Finished))
Some((
(id, Progress::Errored),
State::Finished,
))
}
}
Err(_) => {
Some((Progress::Errored, State::Finished))
Some(((id, Progress::Errored), State::Finished))
}
}
}
@ -68,7 +80,7 @@ where
(downloaded as f32 / total as f32) * 100.0;
Some((
Progress::Advanced(percentage),
(id, Progress::Advanced(percentage)),
State::Downloading {
response,
total,
@ -76,8 +88,12 @@ where
},
))
}
Ok(None) => Some((Progress::Finished, State::Finished)),
Err(_) => Some((Progress::Errored, State::Finished)),
Ok(None) => {
Some(((id, Progress::Finished), State::Finished))
}
Err(_) => {
Some(((id, Progress::Errored), State::Finished))
}
},
State::Finished => {
// We do not let the stream die, as it would start a

View File

@ -1,6 +1,6 @@
use iced::{
button, executor, Align, Application, Button, Column, Command, Container,
Element, Length, ProgressBar, Settings, Subscription, Text,
button, executor, Align, Application, Button, Clipboard, Column, Command,
Container, Element, Length, ProgressBar, Settings, Subscription, Text,
};
mod download;
@ -10,17 +10,17 @@ pub fn main() -> iced::Result {
}
#[derive(Debug)]
enum Example {
Idle { button: button::State },
Downloading { progress: f32 },
Finished { button: button::State },
Errored { button: button::State },
struct Example {
downloads: Vec<Download>,
last_id: usize,
add: button::State,
}
#[derive(Debug, Clone)]
pub enum Message {
Download,
DownloadProgressed(download::Progress),
Add,
Download(usize),
DownloadProgressed((usize, download::Progress)),
}
impl Application for Example {
@ -30,8 +30,10 @@ impl Application for Example {
fn new(_flags: ()) -> (Example, Command<Message>) {
(
Example::Idle {
button: button::State::new(),
Example {
downloads: vec![Download::new(0)],
last_id: 0,
add: button::State::new(),
},
Command::none(),
)
@ -41,104 +43,177 @@ impl Application for Example {
String::from("Download progress - Iced")
}
fn update(&mut self, message: Message) -> Command<Message> {
fn update(
&mut self,
message: Message,
_clipboard: &mut Clipboard,
) -> Command<Message> {
match message {
Message::Download => match self {
Example::Idle { .. }
| Example::Finished { .. }
| Example::Errored { .. } => {
*self = Example::Downloading { progress: 0.0 };
Message::Add => {
self.last_id = self.last_id + 1;
self.downloads.push(Download::new(self.last_id));
}
Message::Download(index) => {
if let Some(download) = self.downloads.get_mut(index) {
download.start();
}
_ => {}
},
Message::DownloadProgressed(message) => match self {
Example::Downloading { progress } => match message {
download::Progress::Started => {
*progress = 0.0;
}
download::Progress::Advanced(percentage) => {
*progress = percentage;
}
download::Progress::Finished => {
*self = Example::Finished {
button: button::State::new(),
}
}
download::Progress::Errored => {
*self = Example::Errored {
button: button::State::new(),
};
}
},
_ => {}
},
}
Message::DownloadProgressed((id, progress)) => {
if let Some(download) =
self.downloads.iter_mut().find(|download| download.id == id)
{
download.progress(progress);
}
}
};
Command::none()
}
fn subscription(&self) -> Subscription<Message> {
match self {
Example::Downloading { .. } => {
download::file("https://speed.hetzner.de/100MB.bin")
Subscription::batch(self.downloads.iter().map(Download::subscription))
}
fn view(&mut self) -> Element<Message> {
let downloads = self
.downloads
.iter_mut()
.fold(Column::new().spacing(20), |column, download| {
column.push(download.view())
})
.push(
Button::new(&mut self.add, Text::new("Add another download"))
.on_press(Message::Add)
.padding(10),
)
.align_items(Align::End);
Container::new(downloads)
.width(Length::Fill)
.height(Length::Fill)
.center_x()
.center_y()
.padding(20)
.into()
}
}
#[derive(Debug)]
struct Download {
id: usize,
state: State,
}
#[derive(Debug)]
enum State {
Idle { button: button::State },
Downloading { progress: f32 },
Finished { button: button::State },
Errored { button: button::State },
}
impl Download {
pub fn new(id: usize) -> Self {
Download {
id,
state: State::Idle {
button: button::State::new(),
},
}
}
pub fn start(&mut self) {
match self.state {
State::Idle { .. }
| State::Finished { .. }
| State::Errored { .. } => {
self.state = State::Downloading { progress: 0.0 };
}
_ => {}
}
}
pub fn progress(&mut self, new_progress: download::Progress) {
match &mut self.state {
State::Downloading { progress } => match new_progress {
download::Progress::Started => {
*progress = 0.0;
}
download::Progress::Advanced(percentage) => {
*progress = percentage;
}
download::Progress::Finished => {
self.state = State::Finished {
button: button::State::new(),
}
}
download::Progress::Errored => {
self.state = State::Errored {
button: button::State::new(),
};
}
},
_ => {}
}
}
pub fn subscription(&self) -> Subscription<Message> {
match self.state {
State::Downloading { .. } => {
download::file(self.id, "https://speed.hetzner.de/100MB.bin?")
.map(Message::DownloadProgressed)
}
_ => Subscription::none(),
}
}
fn view(&mut self) -> Element<Message> {
let current_progress = match self {
Example::Idle { .. } => 0.0,
Example::Downloading { progress } => *progress,
Example::Finished { .. } => 100.0,
Example::Errored { .. } => 0.0,
pub fn view(&mut self) -> Element<Message> {
let current_progress = match &self.state {
State::Idle { .. } => 0.0,
State::Downloading { progress } => *progress,
State::Finished { .. } => 100.0,
State::Errored { .. } => 0.0,
};
let progress_bar = ProgressBar::new(0.0..=100.0, current_progress);
let control: Element<_> = match self {
Example::Idle { button } => {
let control: Element<_> = match &mut self.state {
State::Idle { button } => {
Button::new(button, Text::new("Start the download!"))
.on_press(Message::Download)
.on_press(Message::Download(self.id))
.into()
}
Example::Finished { button } => Column::new()
State::Finished { button } => Column::new()
.spacing(10)
.align_items(Align::Center)
.push(Text::new("Download finished!"))
.push(
Button::new(button, Text::new("Start again"))
.on_press(Message::Download),
.on_press(Message::Download(self.id)),
)
.into(),
Example::Downloading { .. } => {
State::Downloading { .. } => {
Text::new(format!("Downloading... {:.2}%", current_progress))
.into()
}
Example::Errored { button } => Column::new()
State::Errored { button } => Column::new()
.spacing(10)
.align_items(Align::Center)
.push(Text::new("Something went wrong :("))
.push(
Button::new(button, Text::new("Try again"))
.on_press(Message::Download),
.on_press(Message::Download(self.id)),
)
.into(),
};
let content = Column::new()
Column::new()
.spacing(10)
.padding(10)
.align_items(Align::Center)
.push(progress_bar)
.push(control);
Container::new(content)
.width(Length::Fill)
.height(Length::Fill)
.center_x()
.center_y()
.push(control)
.into()
}
}

View File

@ -1,22 +1,30 @@
use iced::{
executor, Align, Application, Checkbox, Column, Command, Container,
Element, Length, Settings, Subscription, Text,
button, executor, Align, Application, Button, Checkbox, Clipboard, Column,
Command, Container, Element, HorizontalAlignment, Length, Settings,
Subscription, Text,
};
use iced_native::{window, Event};
pub fn main() -> iced::Result {
Events::run(Settings::default())
Events::run(Settings {
exit_on_close_request: false,
..Settings::default()
})
}
#[derive(Debug, Default)]
struct Events {
last: Vec<iced_native::Event>,
enabled: bool,
exit: button::State,
should_exit: bool,
}
#[derive(Debug, Clone)]
enum Message {
EventOccurred(iced_native::Event),
Toggled(bool),
Exit,
}
impl Application for Events {
@ -32,29 +40,41 @@ impl Application for Events {
String::from("Events - Iced")
}
fn update(&mut self, message: Message) -> Command<Message> {
fn update(
&mut self,
message: Message,
_clipboard: &mut Clipboard,
) -> Command<Message> {
match message {
Message::EventOccurred(event) => {
Message::EventOccurred(event) if self.enabled => {
self.last.push(event);
if self.last.len() > 5 {
let _ = self.last.remove(0);
}
}
Message::EventOccurred(event) => {
if let Event::Window(window::Event::CloseRequested) = event {
self.should_exit = true;
}
}
Message::Toggled(enabled) => {
self.enabled = enabled;
}
Message::Exit => {
self.should_exit = true;
}
};
Command::none()
}
fn subscription(&self) -> Subscription<Message> {
if self.enabled {
iced_native::subscription::events().map(Message::EventOccurred)
} else {
Subscription::none()
}
iced_native::subscription::events().map(Message::EventOccurred)
}
fn should_exit(&self) -> bool {
self.should_exit
}
fn view(&mut self) -> Element<Message> {
@ -71,11 +91,22 @@ impl Application for Events {
Message::Toggled,
);
let exit = Button::new(
&mut self.exit,
Text::new("Exit")
.width(Length::Fill)
.horizontal_alignment(HorizontalAlignment::Center),
)
.width(Length::Units(100))
.padding(10)
.on_press(Message::Exit);
let content = Column::new()
.align_items(Align::Center)
.spacing(20)
.push(events)
.push(toggle);
.push(toggle)
.push(exit);
Container::new(content)
.width(Length::Fill)

View File

@ -7,6 +7,6 @@ publish = false
[dependencies]
iced = { path = "../..", features = ["canvas", "tokio", "debug"] }
tokio = { version = "0.2", features = ["blocking"] }
tokio = { version = "1.0", features = ["sync"] }
itertools = "0.9"
rustc-hash = "1.1"

View File

@ -7,8 +7,8 @@ It runs a simulation in a background thread while allowing interaction with a `C
The __[`main`]__ file contains the relevant code of the example.
<div align="center">
<a href="https://gfycat.com/briefaccurateaardvark">
<img src="https://thumbs.gfycat.com/BriefAccurateAardvark-size_restricted.gif">
<a href="https://gfycat.com/WhichPaltryChick">
<img src="https://thumbs.gfycat.com/WhichPaltryChick-size_restricted.gif">
</a>
</div>

View File

@ -10,8 +10,8 @@ use iced::pick_list::{self, PickList};
use iced::slider::{self, Slider};
use iced::time;
use iced::{
Align, Application, Checkbox, Column, Command, Container, Element, Length,
Row, Settings, Subscription, Text,
Align, Application, Checkbox, Clipboard, Column, Command, Container,
Element, Length, Row, Settings, Subscription, Text,
};
use preset::Preset;
use std::time::{Duration, Instant};
@ -31,11 +31,12 @@ struct GameOfLife {
queued_ticks: usize,
speed: usize,
next_speed: Option<usize>,
version: usize,
}
#[derive(Debug, Clone)]
enum Message {
Grid(grid::Message),
Grid(grid::Message, usize),
Tick(Instant),
TogglePlayback,
ToggleGrid(bool),
@ -64,10 +65,16 @@ impl Application for GameOfLife {
String::from("Game of Life - Iced")
}
fn update(&mut self, message: Message) -> Command<Message> {
fn update(
&mut self,
message: Message,
_clipboard: &mut Clipboard,
) -> Command<Message> {
match message {
Message::Grid(message) => {
self.grid.update(message);
Message::Grid(message, version) => {
if version == self.version {
self.grid.update(message);
}
}
Message::Tick(_) | Message::Next => {
self.queued_ticks = (self.queued_ticks + 1).min(self.speed);
@ -79,7 +86,11 @@ impl Application for GameOfLife {
self.queued_ticks = 0;
return Command::perform(task, Message::Grid);
let version = self.version;
return Command::perform(task, move |message| {
Message::Grid(message, version)
});
}
}
Message::TogglePlayback => {
@ -90,6 +101,7 @@ impl Application for GameOfLife {
}
Message::Clear => {
self.grid.clear();
self.version += 1;
}
Message::SpeedChanged(speed) => {
if self.is_playing {
@ -100,6 +112,7 @@ impl Application for GameOfLife {
}
Message::PresetPicked(new_preset) => {
self.grid = Grid::from_preset(new_preset);
self.version += 1;
}
}
@ -116,6 +129,7 @@ impl Application for GameOfLife {
}
fn view(&mut self) -> Element<Message> {
let version = self.version;
let selected_speed = self.next_speed.unwrap_or(self.speed);
let controls = self.controls.view(
self.is_playing,
@ -125,7 +139,11 @@ impl Application for GameOfLife {
);
let content = Column::new()
.push(self.grid.view().map(Message::Grid))
.push(
self.grid
.view()
.map(move |message| Message::Grid(message, version)),
)
.push(controls);
Container::new(content)
@ -139,9 +157,8 @@ impl Application for GameOfLife {
mod grid {
use crate::Preset;
use iced::{
canvas::{
self, Cache, Canvas, Cursor, Event, Frame, Geometry, Path, Text,
},
canvas::event::{self, Event},
canvas::{self, Cache, Canvas, Cursor, Frame, Geometry, Path, Text},
mouse, Color, Element, HorizontalAlignment, Length, Point, Rectangle,
Size, Vector, VerticalAlignment,
};
@ -161,7 +178,6 @@ mod grid {
show_lines: bool,
last_tick_duration: Duration,
last_queued_ticks: usize,
version: usize,
}
#[derive(Debug, Clone)]
@ -171,7 +187,6 @@ mod grid {
Ticked {
result: Result<Life, TickError>,
tick_duration: Duration,
version: usize,
},
}
@ -208,7 +223,6 @@ mod grid {
show_lines: true,
last_tick_duration: Duration::default(),
last_queued_ticks: 0,
version: 0,
}
}
@ -216,7 +230,6 @@ mod grid {
&mut self,
amount: usize,
) -> Option<impl Future<Output = Message>> {
let version = self.version;
let tick = self.state.tick(amount)?;
self.last_queued_ticks = amount;
@ -228,7 +241,6 @@ mod grid {
Message::Ticked {
result,
version,
tick_duration,
}
})
@ -250,13 +262,11 @@ mod grid {
}
Message::Ticked {
result: Ok(life),
version,
tick_duration,
} if version == self.version => {
} => {
self.state.update(life);
self.life_cache.clear();
self.version += 1;
self.last_tick_duration = tick_duration;
}
Message::Ticked {
@ -264,7 +274,6 @@ mod grid {
} => {
dbg!(error);
}
Message::Ticked { .. } => {}
}
}
@ -278,7 +287,6 @@ mod grid {
pub fn clear(&mut self) {
self.state = State::default();
self.preset = Preset::Custom;
self.version += 1;
self.life_cache.clear();
}
@ -323,12 +331,18 @@ mod grid {
event: Event,
bounds: Rectangle,
cursor: Cursor,
) -> Option<Message> {
) -> (event::Status, Option<Message>) {
if let Event::Mouse(mouse::Event::ButtonReleased(_)) = event {
self.interaction = Interaction::None;
}
let cursor_position = cursor.position_in(&bounds)?;
let cursor_position =
if let Some(position) = cursor.position_in(&bounds) {
position
} else {
return (event::Status::Ignored, None);
};
let cell = Cell::at(self.project(cursor_position, bounds.size()));
let is_populated = self.state.contains(&cell);
@ -340,28 +354,32 @@ mod grid {
match event {
Event::Mouse(mouse_event) => match mouse_event {
mouse::Event::ButtonPressed(button) => match button {
mouse::Button::Left => {
self.interaction = if is_populated {
Interaction::Erasing
} else {
Interaction::Drawing
};
mouse::Event::ButtonPressed(button) => {
let message = match button {
mouse::Button::Left => {
self.interaction = if is_populated {
Interaction::Erasing
} else {
Interaction::Drawing
};
populate.or(unpopulate)
}
mouse::Button::Right => {
self.interaction = Interaction::Panning {
translation: self.translation,
start: cursor_position,
};
populate.or(unpopulate)
}
mouse::Button::Right => {
self.interaction = Interaction::Panning {
translation: self.translation,
start: cursor_position,
};
None
}
_ => None,
},
None
}
_ => None,
};
(event::Status::Captured, message)
}
mouse::Event::CursorMoved { .. } => {
match self.interaction {
let message = match self.interaction {
Interaction::Drawing => populate,
Interaction::Erasing => unpopulate,
Interaction::Panning { translation, start } => {
@ -375,7 +393,14 @@ mod grid {
None
}
_ => None,
}
};
let event_status = match self.interaction {
Interaction::None => event::Status::Ignored,
_ => event::Status::Captured,
};
(event_status, message)
}
mouse::Event::WheelScrolled { delta } => match delta {
mouse::ScrollDelta::Lines { y, .. }
@ -408,12 +433,12 @@ mod grid {
self.grid_cache.clear();
}
None
(event::Status::Captured, None)
}
},
_ => None,
_ => (event::Status::Ignored, None),
},
_ => None,
_ => (event::Status::Ignored, None),
}
}

View File

@ -44,7 +44,7 @@ impl button::StyleSheet for Button {
fn active(&self) -> button::Style {
button::Style {
background: Some(Background::Color(ACTIVE)),
border_radius: 3,
border_radius: 3.0,
text_color: Color::WHITE,
..button::Style::default()
}
@ -60,7 +60,7 @@ impl button::StyleSheet for Button {
fn pressed(&self) -> button::Style {
button::Style {
border_width: 1,
border_width: 1.0,
border_color: Color::WHITE,
..self.hovered()
}
@ -73,7 +73,7 @@ impl button::StyleSheet for Clear {
fn active(&self) -> button::Style {
button::Style {
background: Some(Background::Color(DESTRUCTIVE)),
border_radius: 3,
border_radius: 3.0,
text_color: Color::WHITE,
..button::Style::default()
}
@ -92,7 +92,7 @@ impl button::StyleSheet for Clear {
fn pressed(&self) -> button::Style {
button::Style {
border_width: 1,
border_width: 1.0,
border_color: Color::WHITE,
..self.hovered()
}
@ -106,9 +106,9 @@ impl slider::StyleSheet for Slider {
slider::Style {
rail_colors: (ACTIVE, Color { a: 0.1, ..ACTIVE }),
handle: slider::Handle {
shape: slider::HandleShape::Circle { radius: 9 },
shape: slider::HandleShape::Circle { radius: 9.0 },
color: ACTIVE,
border_width: 0,
border_width: 0.0,
border_color: Color::TRANSPARENT,
},
}
@ -146,7 +146,7 @@ impl pick_list::StyleSheet for PickList {
pick_list::Menu {
text_color: Color::WHITE,
background: BACKGROUND.into(),
border_width: 1,
border_width: 1.0,
border_color: Color {
a: 0.7,
..Color::BLACK
@ -164,12 +164,12 @@ impl pick_list::StyleSheet for PickList {
pick_list::Style {
text_color: Color::WHITE,
background: BACKGROUND.into(),
border_width: 1,
border_width: 1.0,
border_color: Color {
a: 0.6,
..Color::BLACK
},
border_radius: 2,
border_radius: 2.0,
icon_size: 0.5,
}
}

View File

@ -8,4 +8,4 @@ publish = false
[dependencies]
iced_winit = { path = "../../winit" }
iced_wgpu = { path = "../../wgpu" }
env_logger = "0.7"
env_logger = "0.8"

View File

@ -1,7 +1,7 @@
use iced_wgpu::Renderer;
use iced_winit::{
slider, Align, Color, Column, Command, Element, Length, Program, Row,
Slider, Text,
slider, Align, Clipboard, Color, Column, Command, Element, Length, Program,
Row, Slider, Text,
};
pub struct Controls {
@ -30,8 +30,13 @@ impl Controls {
impl Program for Controls {
type Renderer = Renderer;
type Message = Message;
type Clipboard = Clipboard;
fn update(&mut self, message: Message) -> Command<Message> {
fn update(
&mut self,
message: Message,
_clipboard: &mut Clipboard,
) -> Command<Message> {
match message {
Message::BackgroundColorChanged(color) => {
self.background_color = color;

View File

@ -5,7 +5,7 @@ use controls::Controls;
use scene::Scene;
use iced_wgpu::{wgpu, Backend, Renderer, Settings, Viewport};
use iced_winit::{conversion, futures, program, winit, Debug, Size};
use iced_winit::{conversion, futures, program, winit, Clipboard, Debug, Size};
use futures::task::SpawnExt;
use winit::{
@ -28,6 +28,7 @@ pub fn main() {
);
let mut cursor_position = PhysicalPosition::new(-1.0, -1.0);
let mut modifiers = ModifiersState::default();
let mut clipboard = Clipboard::connect(&window);
// Initialize wgpu
let instance = wgpu::Instance::new(wgpu::BackendBit::PRIMARY);
@ -36,7 +37,7 @@ pub fn main() {
let (mut device, queue) = futures::executor::block_on(async {
let adapter = instance
.request_adapter(&wgpu::RequestAdapterOptions {
power_preference: wgpu::PowerPreference::Default,
power_preference: wgpu::PowerPreference::HighPerformance,
compatible_surface: Some(&surface),
})
.await
@ -45,9 +46,9 @@ pub fn main() {
adapter
.request_device(
&wgpu::DeviceDescriptor {
label: None,
features: wgpu::Features::empty(),
limits: wgpu::Limits::default(),
shader_validation: false,
},
None,
)
@ -63,7 +64,7 @@ pub fn main() {
device.create_swap_chain(
&surface,
&wgpu::SwapChainDescriptor {
usage: wgpu::TextureUsage::OUTPUT_ATTACHMENT,
usage: wgpu::TextureUsage::RENDER_ATTACHMENT,
format: format,
width: size.width,
height: size.height,
@ -141,8 +142,8 @@ pub fn main() {
cursor_position,
viewport.scale_factor(),
),
None,
&mut renderer,
&mut clipboard,
&mut debug,
);
@ -157,7 +158,7 @@ pub fn main() {
swap_chain = device.create_swap_chain(
&surface,
&wgpu::SwapChainDescriptor {
usage: wgpu::TextureUsage::OUTPUT_ATTACHMENT,
usage: wgpu::TextureUsage::RENDER_ATTACHMENT,
format: format,
width: size.width,
height: size.height,

View File

@ -19,8 +19,9 @@ impl Scene {
background_color: Color,
) -> wgpu::RenderPass<'a> {
encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
color_attachments: &[wgpu::RenderPassColorAttachmentDescriptor {
attachment: target,
label: None,
color_attachments: &[wgpu::RenderPassColorAttachment {
view: target,
resolve_target: None,
ops: wgpu::Operations {
load: wgpu::LoadOp::Clear({
@ -48,10 +49,10 @@ impl Scene {
fn build_pipeline(device: &wgpu::Device) -> wgpu::RenderPipeline {
let vs_module =
device.create_shader_module(wgpu::include_spirv!("shader/vert.spv"));
device.create_shader_module(&wgpu::include_spirv!("shader/vert.spv"));
let fs_module =
device.create_shader_module(wgpu::include_spirv!("shader/frag.spv"));
device.create_shader_module(&wgpu::include_spirv!("shader/frag.spv"));
let pipeline_layout =
device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor {
@ -64,34 +65,34 @@ fn build_pipeline(device: &wgpu::Device) -> wgpu::RenderPipeline {
device.create_render_pipeline(&wgpu::RenderPipelineDescriptor {
label: None,
layout: Some(&pipeline_layout),
vertex_stage: wgpu::ProgrammableStageDescriptor {
vertex: wgpu::VertexState {
module: &vs_module,
entry_point: "main",
buffers: &[],
},
fragment_stage: Some(wgpu::ProgrammableStageDescriptor {
fragment: Some(wgpu::FragmentState {
module: &fs_module,
entry_point: "main",
targets: &[wgpu::ColorTargetState {
format: wgpu::TextureFormat::Bgra8UnormSrgb,
blend: Some(wgpu::BlendState {
color: wgpu::BlendComponent::REPLACE,
alpha: wgpu::BlendComponent::REPLACE,
}),
write_mask: wgpu::ColorWrite::ALL,
}],
}),
rasterization_state: Some(wgpu::RasterizationStateDescriptor {
primitive: wgpu::PrimitiveState {
topology: wgpu::PrimitiveTopology::TriangleList,
front_face: wgpu::FrontFace::Ccw,
cull_mode: wgpu::CullMode::None,
..Default::default()
}),
primitive_topology: wgpu::PrimitiveTopology::TriangleList,
color_states: &[wgpu::ColorStateDescriptor {
format: wgpu::TextureFormat::Bgra8UnormSrgb,
color_blend: wgpu::BlendDescriptor::REPLACE,
alpha_blend: wgpu::BlendDescriptor::REPLACE,
write_mask: wgpu::ColorWrite::ALL,
}],
depth_stencil_state: None,
vertex_state: wgpu::VertexStateDescriptor {
index_format: wgpu::IndexFormat::Uint16,
vertex_buffers: &[],
},
sample_count: 1,
sample_mask: !0,
alpha_to_coverage_enabled: false,
depth_stencil: None,
multisample: wgpu::MultisampleState {
count: 1,
mask: !0,
alpha_to_coverage_enabled: false,
},
});
pipeline

View File

@ -6,4 +6,5 @@ edition = "2018"
publish = false
[dependencies]
iced = { path = "../.." }
iced = { path = "../..", features = ["debug"] }
iced_native = { path = "../../native" }

View File

@ -1,16 +1,19 @@
use iced::{
button, keyboard, pane_grid, scrollable, Align, Button, Column, Container,
Element, HorizontalAlignment, Length, PaneGrid, Sandbox, Scrollable,
Settings, Text,
button, executor, keyboard, pane_grid, scrollable, Align, Application,
Button, Clipboard, Color, Column, Command, Container, Element,
HorizontalAlignment, Length, PaneGrid, Row, Scrollable, Settings,
Subscription, Text,
};
use iced_native::{event, subscription, Event};
pub fn main() -> iced::Result {
Example::run(Settings::default())
}
struct Example {
panes: pane_grid::State<Content>,
panes: pane_grid::State<Pane>,
panes_created: usize,
focus: Option<pane_grid::Pane>,
}
#[derive(Debug, Clone, Copy)]
@ -18,59 +21,82 @@ enum Message {
Split(pane_grid::Axis, pane_grid::Pane),
SplitFocused(pane_grid::Axis),
FocusAdjacent(pane_grid::Direction),
Clicked(pane_grid::Pane),
Dragged(pane_grid::DragEvent),
Resized(pane_grid::ResizeEvent),
TogglePin(pane_grid::Pane),
Close(pane_grid::Pane),
CloseFocused,
}
impl Sandbox for Example {
impl Application for Example {
type Message = Message;
type Executor = executor::Default;
type Flags = ();
fn new() -> Self {
let (panes, _) = pane_grid::State::new(Content::new(0));
fn new(_flags: ()) -> (Self, Command<Message>) {
let (panes, _) = pane_grid::State::new(Pane::new(0));
Example {
panes,
panes_created: 1,
}
(
Example {
panes,
panes_created: 1,
focus: None,
},
Command::none(),
)
}
fn title(&self) -> String {
String::from("Pane grid - Iced")
}
fn update(&mut self, message: Message) {
fn update(
&mut self,
message: Message,
_clipboard: &mut Clipboard,
) -> Command<Message> {
match message {
Message::Split(axis, pane) => {
let _ = self.panes.split(
let result = self.panes.split(
axis,
&pane,
Content::new(self.panes_created),
Pane::new(self.panes_created),
);
if let Some((pane, _)) = result {
self.focus = Some(pane);
}
self.panes_created += 1;
}
Message::SplitFocused(axis) => {
if let Some(pane) = self.panes.active() {
let _ = self.panes.split(
if let Some(pane) = self.focus {
let result = self.panes.split(
axis,
&pane,
Content::new(self.panes_created),
Pane::new(self.panes_created),
);
if let Some((pane, _)) = result {
self.focus = Some(pane);
}
self.panes_created += 1;
}
}
Message::FocusAdjacent(direction) => {
if let Some(pane) = self.panes.active() {
if let Some(pane) = self.focus {
if let Some(adjacent) =
self.panes.adjacent(&pane, direction)
{
self.panes.focus(&adjacent);
self.focus = Some(adjacent);
}
}
}
Message::Clicked(pane) => {
self.focus = Some(pane);
}
Message::Resized(pane_grid::ResizeEvent { split, ratio }) => {
self.panes.resize(&split, ratio);
}
@ -81,38 +107,97 @@ impl Sandbox for Example {
self.panes.swap(&pane, &target);
}
Message::Dragged(_) => {}
Message::TogglePin(pane) => {
if let Some(Pane { is_pinned, .. }) = self.panes.get_mut(&pane)
{
*is_pinned = !*is_pinned;
}
}
Message::Close(pane) => {
let _ = self.panes.close(&pane);
if let Some((_, sibling)) = self.panes.close(&pane) {
self.focus = Some(sibling);
}
}
Message::CloseFocused => {
if let Some(pane) = self.panes.active() {
let _ = self.panes.close(&pane);
if let Some(pane) = self.focus {
if let Some(Pane { is_pinned, .. }) = self.panes.get(&pane)
{
if !is_pinned {
if let Some((_, sibling)) = self.panes.close(&pane)
{
self.focus = Some(sibling);
}
}
}
}
}
}
Command::none()
}
fn subscription(&self) -> Subscription<Message> {
subscription::events_with(|event, status| {
if let event::Status::Captured = status {
return None;
}
match event {
Event::Keyboard(keyboard::Event::KeyPressed {
modifiers,
key_code,
}) if modifiers.is_command_pressed() => handle_hotkey(key_code),
_ => None,
}
})
}
fn view(&mut self) -> Element<Message> {
let focus = self.focus;
let total_panes = self.panes.len();
let pane_grid =
PaneGrid::new(&mut self.panes, |pane, content, focus| {
let is_focused = focus.is_some();
let title_bar =
pane_grid::TitleBar::new(format!("Pane {}", content.id))
.padding(10)
.style(style::TitleBar { is_focused });
let pane_grid = PaneGrid::new(&mut self.panes, |id, pane| {
let is_focused = focus == Some(id);
pane_grid::Content::new(content.view(pane, total_panes))
.title_bar(title_bar)
.style(style::Pane { is_focused })
})
.width(Length::Fill)
.height(Length::Fill)
.spacing(10)
.on_drag(Message::Dragged)
.on_resize(10, Message::Resized)
.on_key_press(handle_hotkey);
let text = if pane.is_pinned { "Unpin" } else { "Pin" };
let pin_button =
Button::new(&mut pane.pin_button, Text::new(text).size(14))
.on_press(Message::TogglePin(id))
.style(style::Button::Pin)
.padding(3);
let title = Row::with_children(vec![
pin_button.into(),
Text::new("Pane").into(),
Text::new(pane.content.id.to_string())
.color(if is_focused {
PANE_ID_COLOR_FOCUSED
} else {
PANE_ID_COLOR_UNFOCUSED
})
.into(),
])
.spacing(5);
let title_bar = pane_grid::TitleBar::new(title)
.controls(pane.controls.view(id, total_panes, pane.is_pinned))
.padding(10)
.style(style::TitleBar { is_focused });
pane_grid::Content::new(pane.content.view(
id,
total_panes,
pane.is_pinned,
))
.title_bar(title_bar)
.style(style::Pane { is_focused })
})
.width(Length::Fill)
.height(Length::Fill)
.spacing(10)
.on_click(Message::Clicked)
.on_drag(Message::Dragged)
.on_resize(10, Message::Resized);
Container::new(pane_grid)
.width(Length::Fill)
@ -122,11 +207,22 @@ impl Sandbox for Example {
}
}
fn handle_hotkey(event: pane_grid::KeyPressEvent) -> Option<Message> {
const PANE_ID_COLOR_UNFOCUSED: Color = Color::from_rgb(
0xFF as f32 / 255.0,
0xC7 as f32 / 255.0,
0xC7 as f32 / 255.0,
);
const PANE_ID_COLOR_FOCUSED: Color = Color::from_rgb(
0xFF as f32 / 255.0,
0x47 as f32 / 255.0,
0x47 as f32 / 255.0,
);
fn handle_hotkey(key_code: keyboard::KeyCode) -> Option<Message> {
use keyboard::KeyCode;
use pane_grid::{Axis, Direction};
let direction = match event.key_code {
let direction = match key_code {
KeyCode::Up => Some(Direction::Up),
KeyCode::Down => Some(Direction::Down),
KeyCode::Left => Some(Direction::Left),
@ -134,7 +230,7 @@ fn handle_hotkey(event: pane_grid::KeyPressEvent) -> Option<Message> {
_ => None,
};
match event.key_code {
match key_code {
KeyCode::V => Some(Message::SplitFocused(Axis::Vertical)),
KeyCode::H => Some(Message::SplitFocused(Axis::Horizontal)),
KeyCode::W => Some(Message::CloseFocused),
@ -142,6 +238,13 @@ fn handle_hotkey(event: pane_grid::KeyPressEvent) -> Option<Message> {
}
}
struct Pane {
pub is_pinned: bool,
pub pin_button: button::State,
pub content: Content,
pub controls: Controls,
}
struct Content {
id: usize,
scroll: scrollable::State,
@ -150,6 +253,21 @@ struct Content {
close: button::State,
}
struct Controls {
close: button::State,
}
impl Pane {
fn new(id: usize) -> Self {
Self {
is_pinned: false,
pin_button: button::State::new(),
content: Content::new(id),
controls: Controls::new(),
}
}
}
impl Content {
fn new(id: usize) -> Self {
Content {
@ -164,6 +282,7 @@ impl Content {
&mut self,
pane: pane_grid::Pane,
total_panes: usize,
is_pinned: bool,
) -> Element<Message> {
let Content {
scroll,
@ -203,7 +322,7 @@ impl Content {
style::Button::Primary,
));
if total_panes > 1 {
if total_panes > 1 && !is_pinned {
controls = controls.push(button(
close,
"Close",
@ -227,7 +346,32 @@ impl Content {
}
}
impl Controls {
fn new() -> Self {
Self {
close: button::State::new(),
}
}
pub fn view(
&mut self,
pane: pane_grid::Pane,
total_panes: usize,
is_pinned: bool,
) -> Element<Message> {
let mut button =
Button::new(&mut self.close, Text::new("Close").size(14))
.style(style::Button::Control)
.padding(3);
if total_panes > 1 && !is_pinned {
button = button.on_press(Message::Close(pane));
}
button.into()
}
}
mod style {
use crate::PANE_ID_COLOR_FOCUSED;
use iced::{button, container, Background, Color, Vector};
const SURFACE: Color = Color::from_rgb(
@ -275,7 +419,7 @@ mod style {
fn style(&self) -> container::Style {
container::Style {
background: Some(Background::Color(SURFACE)),
border_width: 2,
border_width: 2.0,
border_color: if self.is_focused {
Color::BLACK
} else {
@ -289,6 +433,8 @@ mod style {
pub enum Button {
Primary,
Destructive,
Control,
Pin,
}
impl button::StyleSheet for Button {
@ -298,12 +444,14 @@ mod style {
Button::Destructive => {
(None, Color::from_rgb8(0xFF, 0x47, 0x47))
}
Button::Control => (Some(PANE_ID_COLOR_FOCUSED), Color::WHITE),
Button::Pin => (Some(ACTIVE), Color::WHITE),
};
button::Style {
text_color,
background: background.map(Background::Color),
border_radius: 5,
border_radius: 5.0,
shadow_offset: Vector::new(0.0, 0.0),
..button::Style::default()
}
@ -318,6 +466,8 @@ mod style {
a: 0.2,
..active.text_color
}),
Button::Control => Some(PANE_ID_COLOR_FOCUSED),
Button::Pin => Some(HOVERED),
};
button::Style {

View File

@ -6,7 +6,7 @@ edition = "2018"
publish = false
[dependencies]
iced = { path = "../..", features = ["image", "debug", "tokio"] }
iced = { path = "../..", features = ["image", "debug", "tokio_old"] }
serde_json = "1.0"
[dependencies.serde]

View File

@ -1,6 +1,6 @@
use iced::{
button, futures, image, Align, Application, Button, Column, Command,
Container, Element, Image, Length, Row, Settings, Text,
button, futures, image, Align, Application, Button, Clipboard, Column,
Command, Container, Element, Length, Row, Settings, Text,
};
pub fn main() -> iced::Result {
@ -48,7 +48,11 @@ impl Application for Pokedex {
format!("{} - Pokédex", subtitle)
}
fn update(&mut self, message: Message) -> Command<Message> {
fn update(
&mut self,
message: Message,
_clipboard: &mut Clipboard,
) -> Command<Message> {
match message {
Message::PokemonFound(Ok(pokemon)) => {
*self = Pokedex::Loaded {
@ -112,16 +116,20 @@ struct Pokemon {
name: String,
description: String,
image: image::Handle,
image_viewer: image::viewer::State,
}
impl Pokemon {
const TOTAL: u16 = 807;
fn view(&self) -> Element<Message> {
fn view(&mut self) -> Element<Message> {
Row::new()
.spacing(20)
.align_items(Align::Center)
.push(Image::new(self.image.clone()))
.push(image::Viewer::new(
&mut self.image_viewer,
self.image.clone(),
))
.push(
Column::new()
.spacing(20)
@ -200,11 +208,15 @@ impl Pokemon {
.map(|c| if c.is_control() { ' ' } else { c })
.collect(),
image,
image_viewer: image::viewer::State::new(),
})
}
async fn fetch_image(id: u16) -> Result<image::Handle, reqwest::Error> {
let url = format!("https://raw.githubusercontent.com/PokeAPI/sprites/master/sprites/pokemon/{}.png", id);
let url = format!(
"https://raw.githubusercontent.com/PokeAPI/sprites/master/sprites/pokemon/{}.png",
id
);
#[cfg(not(target_arch = "wasm32"))]
{
@ -251,7 +263,7 @@ mod style {
background: Some(Background::Color(match self {
Button::Primary => Color::from_rgb(0.11, 0.42, 0.87),
})),
border_radius: 12,
border_radius: 12.0,
shadow_offset: Vector::new(1.0, 1.0),
text_color: Color::WHITE,
..button::Style::default()

View File

@ -0,0 +1,9 @@
[package]
name = "qr_code"
version = "0.1.0"
authors = ["Héctor Ramón Jiménez <hector0193@gmail.com>"]
edition = "2018"
publish = false
[dependencies]
iced = { path = "../..", features = ["qr_code"] }

View File

@ -0,0 +1,18 @@
## QR Code Generator
A basic QR code generator that showcases the `QRCode` widget.
The __[`main`]__ file contains all the code of the example.
<div align="center">
<a href="https://gfycat.com/heavyexhaustedaracari">
<img src="https://thumbs.gfycat.com/HeavyExhaustedAracari-size_restricted.gif">
</a>
</div>
You can run it with `cargo run`:
```
cargo run --package qr_code
```
[`main`]: src/main.rs

View File

@ -0,0 +1,81 @@
use iced::qr_code::{self, QRCode};
use iced::text_input::{self, TextInput};
use iced::{
Align, Column, Container, Element, Length, Sandbox, Settings, Text,
};
pub fn main() -> iced::Result {
QRGenerator::run(Settings::default())
}
#[derive(Default)]
struct QRGenerator {
data: String,
input: text_input::State,
qr_code: Option<qr_code::State>,
}
#[derive(Debug, Clone)]
enum Message {
DataChanged(String),
}
impl Sandbox for QRGenerator {
type Message = Message;
fn new() -> Self {
QRGenerator {
qr_code: qr_code::State::new("").ok(),
..Self::default()
}
}
fn title(&self) -> String {
String::from("QR Code Generator - Iced")
}
fn update(&mut self, message: Message) {
match message {
Message::DataChanged(mut data) => {
data.truncate(100);
self.qr_code = qr_code::State::new(&data).ok();
self.data = data;
}
}
}
fn view(&mut self) -> Element<Message> {
let title = Text::new("QR Code Generator")
.size(70)
.color([0.5, 0.5, 0.5]);
let input = TextInput::new(
&mut self.input,
"Type the data of your QR code here...",
&self.data,
Message::DataChanged,
)
.size(30)
.padding(15);
let mut content = Column::new()
.width(Length::Units(700))
.spacing(20)
.align_items(Align::Center)
.push(title)
.push(input);
if let Some(qr_code) = self.qr_code.as_mut() {
content = content.push(QRCode::new(qr_code).cell_size(10));
}
Container::new(content)
.width(Length::Fill)
.height(Length::Fill)
.padding(20)
.center_x()
.center_y()
.into()
}
}

View File

@ -0,0 +1,9 @@
[package]
name = "scrollable"
version = "0.1.0"
authors = ["Clark Moody <clark@clarkmoody.com>"]
edition = "2018"
publish = false
[dependencies]
iced = { path = "../..", features = ["debug"] }

View File

@ -0,0 +1,15 @@
# Scrollable
An example showcasing the various size and style options for the Scrollable.
All the example code is located in the __[`main`](src/main.rs)__ file.
<div align="center">
<a href="./screenshot.png">
<img src="./screenshot.png" height="640px">
</a>
</div>
You can run it with `cargo run`:
```
cargo run --package scrollable
```

Binary file not shown.

After

Width:  |  Height:  |  Size: 145 KiB

View File

@ -0,0 +1,255 @@
mod style;
use iced::{
button, scrollable, Button, Column, Container, Element, Length,
ProgressBar, Radio, Row, Rule, Sandbox, Scrollable, Settings, Space, Text,
};
pub fn main() -> iced::Result {
ScrollableDemo::run(Settings::default())
}
struct ScrollableDemo {
theme: style::Theme,
variants: Vec<Variant>,
}
#[derive(Debug, Clone)]
enum Message {
ThemeChanged(style::Theme),
ScrollToTop(usize),
ScrollToBottom(usize),
Scrolled(usize, f32),
}
impl Sandbox for ScrollableDemo {
type Message = Message;
fn new() -> Self {
ScrollableDemo {
theme: Default::default(),
variants: Variant::all(),
}
}
fn title(&self) -> String {
String::from("Scrollable - Iced")
}
fn update(&mut self, message: Message) {
match message {
Message::ThemeChanged(theme) => self.theme = theme,
Message::ScrollToTop(i) => {
if let Some(variant) = self.variants.get_mut(i) {
variant.scrollable.snap_to(0.0);
variant.latest_offset = 0.0;
}
}
Message::ScrollToBottom(i) => {
if let Some(variant) = self.variants.get_mut(i) {
variant.scrollable.snap_to(1.0);
variant.latest_offset = 1.0;
}
}
Message::Scrolled(i, offset) => {
if let Some(variant) = self.variants.get_mut(i) {
variant.latest_offset = offset;
}
}
}
}
fn view(&mut self) -> Element<Message> {
let ScrollableDemo {
theme, variants, ..
} = self;
let choose_theme = style::Theme::ALL.iter().fold(
Column::new().spacing(10).push(Text::new("Choose a theme:")),
|column, option| {
column.push(
Radio::new(
*option,
&format!("{:?}", option),
Some(*theme),
Message::ThemeChanged,
)
.style(*theme),
)
},
);
let scrollable_row = Row::with_children(
variants
.iter_mut()
.enumerate()
.map(|(i, variant)| {
let mut scrollable =
Scrollable::new(&mut variant.scrollable)
.padding(10)
.spacing(10)
.width(Length::Fill)
.height(Length::Fill)
.on_scroll(move |offset| {
Message::Scrolled(i, offset)
})
.style(*theme)
.push(Text::new(variant.title))
.push(
Button::new(
&mut variant.scroll_to_bottom,
Text::new("Scroll to bottom"),
)
.width(Length::Fill)
.padding(10)
.on_press(Message::ScrollToBottom(i)),
);
if let Some(scrollbar_width) = variant.scrollbar_width {
scrollable = scrollable
.scrollbar_width(scrollbar_width)
.push(Text::new(format!(
"scrollbar_width: {:?}",
scrollbar_width
)));
}
if let Some(scrollbar_margin) = variant.scrollbar_margin {
scrollable = scrollable
.scrollbar_margin(scrollbar_margin)
.push(Text::new(format!(
"scrollbar_margin: {:?}",
scrollbar_margin
)));
}
if let Some(scroller_width) = variant.scroller_width {
scrollable = scrollable
.scroller_width(scroller_width)
.push(Text::new(format!(
"scroller_width: {:?}",
scroller_width
)));
}
scrollable = scrollable
.push(Space::with_height(Length::Units(100)))
.push(Text::new(
"Some content that should wrap within the \
scrollable. Let's output a lot of short words, so \
that we'll make sure to see how wrapping works \
with these scrollbars.",
))
.push(Space::with_height(Length::Units(1200)))
.push(Text::new("Middle"))
.push(Space::with_height(Length::Units(1200)))
.push(Text::new("The End."))
.push(
Button::new(
&mut variant.scroll_to_top,
Text::new("Scroll to top"),
)
.width(Length::Fill)
.padding(10)
.on_press(Message::ScrollToTop(i)),
);
Column::new()
.width(Length::Fill)
.height(Length::Fill)
.spacing(10)
.push(
Container::new(scrollable)
.width(Length::Fill)
.height(Length::Fill)
.style(*theme),
)
.push(ProgressBar::new(
0.0..=1.0,
variant.latest_offset,
))
.into()
})
.collect(),
)
.spacing(20)
.width(Length::Fill)
.height(Length::Fill);
let content = Column::new()
.spacing(20)
.padding(20)
.push(choose_theme)
.push(Rule::horizontal(20).style(self.theme))
.push(scrollable_row);
Container::new(content)
.width(Length::Fill)
.height(Length::Fill)
.center_x()
.center_y()
.style(self.theme)
.into()
}
}
/// A version of a scrollable
struct Variant {
title: &'static str,
scrollable: scrollable::State,
scroll_to_top: button::State,
scroll_to_bottom: button::State,
scrollbar_width: Option<u16>,
scrollbar_margin: Option<u16>,
scroller_width: Option<u16>,
latest_offset: f32,
}
impl Variant {
pub fn all() -> Vec<Self> {
vec![
Self {
title: "Default Scrollbar",
scrollable: scrollable::State::new(),
scroll_to_top: button::State::new(),
scroll_to_bottom: button::State::new(),
scrollbar_width: None,
scrollbar_margin: None,
scroller_width: None,
latest_offset: 0.0,
},
Self {
title: "Slimmed & Margin",
scrollable: scrollable::State::new(),
scroll_to_top: button::State::new(),
scroll_to_bottom: button::State::new(),
scrollbar_width: Some(4),
scrollbar_margin: Some(3),
scroller_width: Some(4),
latest_offset: 0.0,
},
Self {
title: "Wide Scroller",
scrollable: scrollable::State::new(),
scroll_to_top: button::State::new(),
scroll_to_bottom: button::State::new(),
scrollbar_width: Some(4),
scrollbar_margin: None,
scroller_width: Some(10),
latest_offset: 0.0,
},
Self {
title: "Narrow Scroller",
scrollable: scrollable::State::new(),
scroll_to_top: button::State::new(),
scroll_to_bottom: button::State::new(),
scrollbar_width: Some(10),
scrollbar_margin: None,
scroller_width: Some(4),
latest_offset: 0.0,
},
]
}
}

View File

@ -0,0 +1,190 @@
use iced::{container, radio, rule, scrollable};
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum Theme {
Light,
Dark,
}
impl Theme {
pub const ALL: [Theme; 2] = [Theme::Light, Theme::Dark];
}
impl Default for Theme {
fn default() -> Theme {
Theme::Light
}
}
impl From<Theme> for Box<dyn container::StyleSheet> {
fn from(theme: Theme) -> Self {
match theme {
Theme::Light => Default::default(),
Theme::Dark => dark::Container.into(),
}
}
}
impl From<Theme> for Box<dyn radio::StyleSheet> {
fn from(theme: Theme) -> Self {
match theme {
Theme::Light => Default::default(),
Theme::Dark => dark::Radio.into(),
}
}
}
impl From<Theme> for Box<dyn scrollable::StyleSheet> {
fn from(theme: Theme) -> Self {
match theme {
Theme::Light => Default::default(),
Theme::Dark => dark::Scrollable.into(),
}
}
}
impl From<Theme> for Box<dyn rule::StyleSheet> {
fn from(theme: Theme) -> Self {
match theme {
Theme::Light => Default::default(),
Theme::Dark => dark::Rule.into(),
}
}
}
mod dark {
use iced::{container, radio, rule, scrollable, Color};
const BACKGROUND: Color = Color::from_rgb(
0x36 as f32 / 255.0,
0x39 as f32 / 255.0,
0x3F as f32 / 255.0,
);
const SURFACE: Color = Color::from_rgb(
0x40 as f32 / 255.0,
0x44 as f32 / 255.0,
0x4B as f32 / 255.0,
);
const ACCENT: Color = Color::from_rgb(
0x6F as f32 / 255.0,
0xFF as f32 / 255.0,
0xE9 as f32 / 255.0,
);
const ACTIVE: Color = Color::from_rgb(
0x72 as f32 / 255.0,
0x89 as f32 / 255.0,
0xDA as f32 / 255.0,
);
const SCROLLBAR: Color = Color::from_rgb(
0x2E as f32 / 255.0,
0x33 as f32 / 255.0,
0x38 as f32 / 255.0,
);
const SCROLLER: Color = Color::from_rgb(
0x20 as f32 / 255.0,
0x22 as f32 / 255.0,
0x25 as f32 / 255.0,
);
pub struct Container;
impl container::StyleSheet for Container {
fn style(&self) -> container::Style {
container::Style {
background: Color {
a: 0.99,
..BACKGROUND
}
.into(),
text_color: Color::WHITE.into(),
..container::Style::default()
}
}
}
pub struct Radio;
impl radio::StyleSheet for Radio {
fn active(&self) -> radio::Style {
radio::Style {
background: SURFACE.into(),
dot_color: ACTIVE,
border_width: 1.0,
border_color: ACTIVE,
}
}
fn hovered(&self) -> radio::Style {
radio::Style {
background: Color { a: 0.5, ..SURFACE }.into(),
..self.active()
}
}
}
pub struct Scrollable;
impl scrollable::StyleSheet for Scrollable {
fn active(&self) -> scrollable::Scrollbar {
scrollable::Scrollbar {
background: Color {
a: 0.8,
..SCROLLBAR
}
.into(),
border_radius: 2.0,
border_width: 0.0,
border_color: Color::TRANSPARENT,
scroller: scrollable::Scroller {
color: Color { a: 0.7, ..SCROLLER },
border_radius: 2.0,
border_width: 0.0,
border_color: Color::TRANSPARENT,
},
}
}
fn hovered(&self) -> scrollable::Scrollbar {
let active = self.active();
scrollable::Scrollbar {
background: SCROLLBAR.into(),
scroller: scrollable::Scroller {
color: SCROLLER,
..active.scroller
},
..active
}
}
fn dragging(&self) -> scrollable::Scrollbar {
let hovered = self.hovered();
scrollable::Scrollbar {
scroller: scrollable::Scroller {
color: ACCENT,
..hovered.scroller
},
..hovered
}
}
}
pub struct Rule;
impl rule::StyleSheet for Rule {
fn style(&self) -> rule::Style {
rule::Style {
color: SURFACE,
width: 2,
radius: 1.0,
fill_mode: rule::FillMode::Percent(30.0),
}
}
}
}

View File

@ -7,4 +7,4 @@ publish = false
[dependencies]
iced = { path = "../..", features = ["canvas", "tokio", "debug"] }
rand = "0.7"
rand = "0.8.3"

View File

@ -8,8 +8,8 @@
//! [1]: https://developer.mozilla.org/en-US/docs/Web/API/Canvas_API/Tutorial/Basic_animations#An_animated_solar_system
use iced::{
canvas::{self, Cursor, Path, Stroke},
executor, time, window, Application, Canvas, Color, Command, Element,
Length, Point, Rectangle, Settings, Size, Subscription, Vector,
executor, time, window, Application, Canvas, Clipboard, Color, Command,
Element, Length, Point, Rectangle, Settings, Size, Subscription, Vector,
};
use std::time::Instant;
@ -48,7 +48,11 @@ impl Application for SolarSystem {
String::from("Solar system - Iced")
}
fn update(&mut self, message: Message) -> Command<Message> {
fn update(
&mut self,
message: Message,
_clipboard: &mut Clipboard,
) -> Command<Message> {
match message {
Message::Tick(instant) => {
self.state.update(instant);
@ -117,15 +121,13 @@ impl State {
(
Point::new(
rng.gen_range(
-(width as f32) / 2.0,
width as f32 / 2.0,
(-(width as f32) / 2.0)..(width as f32 / 2.0),
),
rng.gen_range(
-(height as f32) / 2.0,
height as f32 / 2.0,
(-(height as f32) / 2.0)..(height as f32 / 2.0),
),
),
rng.gen_range(0.5, 1.0),
rng.gen_range(0.5..1.0),
)
})
.collect()

View File

@ -6,4 +6,4 @@ edition = "2018"
publish = false
[dependencies]
iced = { path = "../..", features = ["tokio"] }
iced = { path = "../..", features = ["smol"] }

View File

@ -1,6 +1,6 @@
use iced::{
button, executor, time, Align, Application, Button, Column, Command,
Container, Element, HorizontalAlignment, Length, Row, Settings,
button, executor, time, Align, Application, Button, Clipboard, Column,
Command, Container, Element, HorizontalAlignment, Length, Row, Settings,
Subscription, Text,
};
use std::time::{Duration, Instant};
@ -49,7 +49,11 @@ impl Application for Stopwatch {
String::from("Stopwatch - Iced")
}
fn update(&mut self, message: Message) -> Command<Message> {
fn update(
&mut self,
message: Message,
_clipboard: &mut Clipboard,
) -> Command<Message> {
match message {
Message::Toggle => match self.state {
State::Idle => {
@ -161,7 +165,7 @@ mod style {
Button::Secondary => Color::from_rgb(0.5, 0.5, 0.5),
Button::Destructive => Color::from_rgb(0.8, 0.2, 0.2),
})),
border_radius: 12,
border_radius: 12.0,
shadow_offset: Vector::new(1.0, 1.0),
text_color: Color::WHITE,
..button::Style::default()

View File

@ -1,7 +1,7 @@
use iced::{
button, scrollable, slider, text_input, Align, Button, Checkbox, Column,
Container, Element, Length, ProgressBar, Radio, Row, Rule, Sandbox,
Scrollable, Settings, Slider, Space, Text, TextInput,
Scrollable, Settings, Slider, Space, Text, TextInput, Toggler,
};
pub fn main() -> iced::Result {
@ -17,7 +17,8 @@ struct Styling {
button: button::State,
slider: slider::State,
slider_value: f32,
toggle_value: bool,
checkbox_value: bool,
toggler_value: bool,
}
#[derive(Debug, Clone)]
@ -27,6 +28,7 @@ enum Message {
ButtonPressed,
SliderChanged(f32),
CheckboxToggled(bool),
TogglerToggled(bool),
}
impl Sandbox for Styling {
@ -44,9 +46,10 @@ impl Sandbox for Styling {
match message {
Message::ThemeChanged(theme) => self.theme = theme,
Message::InputChanged(value) => self.input_value = value,
Message::ButtonPressed => (),
Message::ButtonPressed => {}
Message::SliderChanged(value) => self.slider_value = value,
Message::CheckboxToggled(value) => self.toggle_value = value,
Message::CheckboxToggled(value) => self.checkbox_value = value,
Message::TogglerToggled(value) => self.toggler_value = value,
}
}
@ -101,11 +104,19 @@ impl Sandbox for Styling {
.push(Text::new("You did it!"));
let checkbox = Checkbox::new(
self.toggle_value,
"Toggle me!",
self.checkbox_value,
"Check me!",
Message::CheckboxToggled,
)
.width(Length::Fill)
.style(self.theme);
let toggler = Toggler::new(
self.toggler_value,
String::from("Toggle me!"),
Message::TogglerToggled,
)
.width(Length::Shrink)
.spacing(10)
.style(self.theme);
let content = Column::new()
@ -124,7 +135,13 @@ impl Sandbox for Styling {
.align_items(Align::Center)
.push(scrollable)
.push(Rule::vertical(38).style(self.theme))
.push(checkbox),
.push(
Column::new()
.width(Length::Shrink)
.spacing(20)
.push(checkbox)
.push(toggler),
),
);
Container::new(content)
@ -140,7 +157,7 @@ impl Sandbox for Styling {
mod style {
use iced::{
button, checkbox, container, progress_bar, radio, rule, scrollable,
slider, text_input,
slider, text_input, toggler,
};
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
@ -231,6 +248,15 @@ mod style {
}
}
impl From<Theme> for Box<dyn toggler::StyleSheet> {
fn from(theme: Theme) -> Self {
match theme {
Theme::Light => Default::default(),
Theme::Dark => dark::Toggler.into(),
}
}
}
impl From<Theme> for Box<dyn rule::StyleSheet> {
fn from(theme: Theme) -> Self {
match theme {
@ -249,7 +275,7 @@ mod style {
fn active(&self) -> button::Style {
button::Style {
background: Color::from_rgb(0.11, 0.42, 0.87).into(),
border_radius: 12,
border_radius: 12.0,
shadow_offset: Vector::new(1.0, 1.0),
text_color: Color::from_rgb8(0xEE, 0xEE, 0xEE),
..button::Style::default()
@ -269,7 +295,7 @@ mod style {
mod dark {
use iced::{
button, checkbox, container, progress_bar, radio, rule, scrollable,
slider, text_input, Color,
slider, text_input, toggler, Color,
};
const SURFACE: Color = Color::from_rgb(
@ -315,7 +341,7 @@ mod style {
radio::Style {
background: SURFACE.into(),
dot_color: ACTIVE,
border_width: 1,
border_width: 1.0,
border_color: ACTIVE,
}
}
@ -334,15 +360,15 @@ mod style {
fn active(&self) -> text_input::Style {
text_input::Style {
background: SURFACE.into(),
border_radius: 2,
border_width: 0,
border_radius: 2.0,
border_width: 0.0,
border_color: Color::TRANSPARENT,
}
}
fn focused(&self) -> text_input::Style {
text_input::Style {
border_width: 1,
border_width: 1.0,
border_color: ACCENT,
..self.active()
}
@ -350,7 +376,7 @@ mod style {
fn hovered(&self) -> text_input::Style {
text_input::Style {
border_width: 1,
border_width: 1.0,
border_color: Color { a: 0.3, ..ACCENT },
..self.focused()
}
@ -375,7 +401,7 @@ mod style {
fn active(&self) -> button::Style {
button::Style {
background: ACTIVE.into(),
border_radius: 3,
border_radius: 3.0,
text_color: Color::WHITE,
..button::Style::default()
}
@ -391,7 +417,7 @@ mod style {
fn pressed(&self) -> button::Style {
button::Style {
border_width: 1,
border_width: 1.0,
border_color: Color::WHITE,
..self.hovered()
}
@ -404,13 +430,13 @@ mod style {
fn active(&self) -> scrollable::Scrollbar {
scrollable::Scrollbar {
background: SURFACE.into(),
border_radius: 2,
border_width: 0,
border_radius: 2.0,
border_width: 0.0,
border_color: Color::TRANSPARENT,
scroller: scrollable::Scroller {
color: ACTIVE,
border_radius: 2,
border_width: 0,
border_radius: 2.0,
border_width: 0.0,
border_color: Color::TRANSPARENT,
},
}
@ -449,9 +475,9 @@ mod style {
slider::Style {
rail_colors: (ACTIVE, Color { a: 0.1, ..ACTIVE }),
handle: slider::Handle {
shape: slider::HandleShape::Circle { radius: 9 },
shape: slider::HandleShape::Circle { radius: 9.0 },
color: ACTIVE,
border_width: 0,
border_width: 0.0,
border_color: Color::TRANSPARENT,
},
}
@ -489,7 +515,7 @@ mod style {
progress_bar::Style {
background: SURFACE.into(),
bar: ACTIVE.into(),
border_radius: 10,
border_radius: 10.0,
}
}
}
@ -502,8 +528,8 @@ mod style {
background: if is_checked { ACTIVE } else { SURFACE }
.into(),
checkmark_color: Color::WHITE,
border_radius: 2,
border_width: 1,
border_radius: 2.0,
border_width: 1.0,
border_color: ACTIVE,
}
}
@ -520,6 +546,35 @@ mod style {
}
}
pub struct Toggler;
impl toggler::StyleSheet for Toggler {
fn active(&self, is_active: bool) -> toggler::Style {
toggler::Style {
background: if is_active { ACTIVE } else { SURFACE },
background_border: None,
foreground: if is_active { Color::WHITE } else { ACTIVE },
foreground_border: None,
}
}
fn hovered(&self, is_active: bool) -> toggler::Style {
toggler::Style {
background: if is_active { ACTIVE } else { SURFACE },
background_border: None,
foreground: if is_active {
Color {
a: 0.5,
..Color::WHITE
}
} else {
Color { a: 0.5, ..ACTIVE }
},
foreground_border: None,
}
}
}
pub struct Rule;
impl rule::StyleSheet for Rule {
@ -527,7 +582,7 @@ mod style {
rule::Style {
color: SURFACE,
width: 2,
radius: 1,
radius: 1.0,
fill_mode: rule::FillMode::Padded(15),
}
}

View File

@ -12,7 +12,7 @@ serde_json = "1.0"
[target.'cfg(not(target_arch = "wasm32"))'.dependencies]
async-std = "1.0"
directories = "2.0"
directories-next = "2.0"
[target.'cfg(target_arch = "wasm32")'.dependencies]
web-sys = { version = "0.3", features = ["Window", "Storage"] }

View File

@ -1,7 +1,7 @@
use iced::{
button, scrollable, text_input, Align, Application, Button, Checkbox,
Column, Command, Container, Element, Font, HorizontalAlignment, Length,
Row, Scrollable, Settings, Text, TextInput,
Clipboard, Column, Command, Container, Element, Font, HorizontalAlignment,
Length, Row, Scrollable, Settings, Text, TextInput,
};
use serde::{Deserialize, Serialize};
@ -58,7 +58,11 @@ impl Application for Todos {
format!("Todos{} - Iced", if dirty { "*" } else { "" })
}
fn update(&mut self, message: Message) -> Command<Message> {
fn update(
&mut self,
message: Message,
_clipboard: &mut Clipboard,
) -> Command<Message> {
match self {
Todos::Loading => {
match message {
@ -489,7 +493,6 @@ enum LoadError {
#[derive(Debug, Clone)]
enum SaveError {
DirectoryError,
FileError,
WriteError,
FormatError,
@ -499,7 +502,7 @@ enum SaveError {
impl SavedState {
fn path() -> std::path::PathBuf {
let mut path = if let Some(project_dirs) =
directories::ProjectDirs::from("rs", "Iced", "Todos")
directories_next::ProjectDirs::from("rs", "Iced", "Todos")
{
project_dirs.data_dir().into()
} else {
@ -538,7 +541,7 @@ impl SavedState {
if let Some(dir) = path.parent() {
async_std::fs::create_dir_all(dir)
.await
.map_err(|_| SaveError::DirectoryError)?;
.map_err(|_| SaveError::FileError)?;
}
{
@ -611,7 +614,7 @@ mod style {
background: Some(Background::Color(
Color::from_rgb(0.2, 0.2, 0.7),
)),
border_radius: 10,
border_radius: 10.0,
text_color: Color::WHITE,
..button::Style::default()
}
@ -627,7 +630,7 @@ mod style {
background: Some(Background::Color(Color::from_rgb(
0.8, 0.2, 0.2,
))),
border_radius: 5,
border_radius: 5.0,
text_color: Color::WHITE,
shadow_offset: Vector::new(1.0, 1.0),
..button::Style::default()

View File

@ -0,0 +1,9 @@
[package]
name = "tooltip"
version = "0.1.0"
authors = ["Yusuf Bera Ertan <y.bera003.06@protonmail.com>"]
edition = "2018"
publish = false
[dependencies]
iced = { path = "../..", features = ["debug"] }

View File

@ -0,0 +1,14 @@
## Tooltip
A tooltip.
It displays and positions a widget on another based on cursor position.
The __[`main`]__ file contains all the code of the example.
You can run it with `cargo run`:
```
cargo run --package tooltip
```
[`main`]: src/main.rs

View File

@ -0,0 +1,138 @@
use iced::tooltip::{self, Tooltip};
use iced::{
button, Button, Column, Container, Element, HorizontalAlignment, Length,
Row, Sandbox, Settings, Text, VerticalAlignment,
};
pub fn main() {
Example::run(Settings::default()).unwrap()
}
#[derive(Default)]
struct Example {
top: button::State,
bottom: button::State,
right: button::State,
left: button::State,
follow_cursor: button::State,
}
#[derive(Debug, Clone, Copy)]
struct Message;
impl Sandbox for Example {
type Message = Message;
fn new() -> Self {
Self::default()
}
fn title(&self) -> String {
String::from("Tooltip - Iced")
}
fn update(&mut self, _message: Message) {}
fn view(&mut self) -> Element<Message> {
let top =
tooltip("Tooltip at top", &mut self.top, tooltip::Position::Top);
let bottom = tooltip(
"Tooltip at bottom",
&mut self.bottom,
tooltip::Position::Bottom,
);
let left =
tooltip("Tooltip at left", &mut self.left, tooltip::Position::Left);
let right = tooltip(
"Tooltip at right",
&mut self.right,
tooltip::Position::Right,
);
let fixed_tooltips = Row::with_children(vec![
top.into(),
bottom.into(),
left.into(),
right.into(),
])
.width(Length::Fill)
.height(Length::Fill)
.align_items(iced::Align::Center)
.spacing(50);
let follow_cursor = tooltip(
"Tooltip follows cursor",
&mut self.follow_cursor,
tooltip::Position::FollowCursor,
);
let content = Column::with_children(vec![
Container::new(fixed_tooltips)
.width(Length::Fill)
.height(Length::Fill)
.center_x()
.center_y()
.into(),
follow_cursor.into(),
])
.width(Length::Fill)
.height(Length::Fill)
.spacing(50);
Container::new(content)
.width(Length::Fill)
.height(Length::Fill)
.center_x()
.center_y()
.padding(50)
.into()
}
}
fn tooltip<'a>(
label: &str,
button_state: &'a mut button::State,
position: tooltip::Position,
) -> Element<'a, Message> {
Tooltip::new(
Button::new(
button_state,
Text::new(label)
.size(40)
.width(Length::Fill)
.height(Length::Fill)
.horizontal_alignment(HorizontalAlignment::Center)
.vertical_alignment(VerticalAlignment::Center),
)
.on_press(Message)
.width(Length::Fill)
.height(Length::Fill),
"Tooltip",
position,
)
.gap(5)
.padding(10)
.style(style::Tooltip)
.into()
}
mod style {
use iced::container;
use iced::Color;
pub struct Tooltip;
impl container::StyleSheet for Tooltip {
fn style(&self) -> container::Style {
container::Style {
text_color: Some(Color::from_rgb8(0xEE, 0xEE, 0xEE)),
background: Some(Color::from_rgb(0.11, 0.42, 0.87).into()),
border_radius: 12.0,
..container::Style::default()
}
}
}
}

View File

@ -7,4 +7,4 @@ publish = false
[dependencies]
iced = { path = "../..", features = ["image", "debug"] }
env_logger = "0.7"
env_logger = "0.8"

View File

@ -1,7 +1,7 @@
use iced::{
button, scrollable, slider, text_input, Button, Checkbox, Color, Column,
Container, Element, HorizontalAlignment, Image, Length, Radio, Row,
Sandbox, Scrollable, Settings, Slider, Space, Text, TextInput,
Sandbox, Scrollable, Settings, Slider, Space, Text, TextInput, Toggler,
};
pub fn main() -> iced::Result {
@ -135,6 +135,9 @@ impl Steps {
color: Color::BLACK,
},
Step::Radio { selection: None },
Step::Toggler {
can_continue: false,
},
Step::Image {
width: 300,
slider: slider::State::new(),
@ -206,6 +209,9 @@ enum Step {
Radio {
selection: Option<Language>,
},
Toggler {
can_continue: bool,
},
Image {
width: u16,
slider: slider::State,
@ -232,6 +238,7 @@ pub enum StepMessage {
InputChanged(String),
ToggleSecureInput(bool),
DebugToggled(bool),
TogglerChanged(bool),
}
impl<'a> Step {
@ -287,6 +294,11 @@ impl<'a> Step {
*is_secure = toggle;
}
}
StepMessage::TogglerChanged(value) => {
if let Step::Toggler { can_continue, .. } = self {
*can_continue = value;
}
}
};
}
@ -294,6 +306,7 @@ impl<'a> Step {
match self {
Step::Welcome => "Welcome",
Step::Radio { .. } => "Radio button",
Step::Toggler { .. } => "Toggler",
Step::Slider { .. } => "Slider",
Step::Text { .. } => "Text",
Step::Image { .. } => "Image",
@ -309,6 +322,7 @@ impl<'a> Step {
match self {
Step::Welcome => true,
Step::Radio { selection } => *selection == Some(Language::Rust),
Step::Toggler { can_continue } => *can_continue,
Step::Slider { .. } => true,
Step::Text { .. } => true,
Step::Image { .. } => true,
@ -324,6 +338,7 @@ impl<'a> Step {
match self {
Step::Welcome => Self::welcome(),
Step::Radio { selection } => Self::radio(*selection),
Step::Toggler { can_continue } => Self::toggler(*can_continue),
Step::Slider { state, value } => Self::slider(state, *value),
Step::Text {
size_slider,
@ -545,6 +560,21 @@ impl<'a> Step {
))
}
fn toggler(can_continue: bool) -> Column<'a, StepMessage> {
Self::container("Toggler")
.push(Text::new(
"A toggler is mostly used to enable or disable something.",
))
.push(
Container::new(Toggler::new(
can_continue,
String::from("Toggle me to continue..."),
StepMessage::TogglerChanged,
))
.padding([0, 40]),
)
}
fn image(
width: u16,
slider: &'a mut slider::State,
@ -689,7 +719,7 @@ fn ferris<'a>(width: u16) -> Container<'a, StepMessage> {
.center_x()
}
fn button<'a, Message>(
fn button<'a, Message: Clone>(
state: &'a mut button::State,
label: &str,
) -> Button<'a, Message> {
@ -769,7 +799,7 @@ mod style {
Button::Primary => Color::from_rgb(0.11, 0.42, 0.87),
Button::Secondary => Color::from_rgb(0.5, 0.5, 0.5),
})),
border_radius: 12,
border_radius: 12.0,
shadow_offset: Vector::new(1.0, 1.0),
text_color: Color::from_rgb8(0xEE, 0xEE, 0xEE),
..button::Style::default()

View File

@ -0,0 +1,10 @@
[package]
name = "tour_glow"
version = "0.1.0"
authors = ["Héctor Ramón Jiménez <hector0193@gmail.com>"]
edition = "2018"
publish = false
[dependencies]
iced = { path = "../..", features = ["glow", "debug"] }
env_logger = "0.8"

View File

@ -0,0 +1,28 @@
## Tour
A simple UI tour that can run both on native platforms and the web! It showcases different widgets that can be built using Iced.
The __[`main`]__ file contains all the code of the example! All the cross-platform GUI is defined in terms of __state__, __messages__, __update logic__ and __view logic__.
<div align="center">
<a href="https://gfycat.com/politeadorableiberianmole">
<img src="https://thumbs.gfycat.com/PoliteAdorableIberianmole-small.gif">
</a>
</div>
[`main`]: src/main.rs
[`iced_winit`]: ../../winit
[`iced_native`]: ../../native
[`iced_wgpu`]: ../../wgpu
[`iced_web`]: ../../web
[`winit`]: https://github.com/rust-windowing/winit
[`wgpu`]: https://github.com/gfx-rs/wgpu-rs
You can run the native version with `cargo run`:
```
cargo run --package tour
```
The web version can be run by following [the usage instructions of `iced_web`] or by accessing [iced.rs](https://iced.rs/)!
[the usage instructions of `iced_web`]: ../../web#usage

View File

@ -0,0 +1,809 @@
use iced::{
button, scrollable, slider, text_input, Button, Checkbox, Color, Column,
Container, Element, HorizontalAlignment, Length, Radio, Row, Sandbox,
Scrollable, Settings, Slider, Space, Text, TextInput, Toggler,
};
pub fn main() -> iced::Result {
env_logger::init();
Tour::run(Settings::default())
}
pub struct Tour {
steps: Steps,
scroll: scrollable::State,
back_button: button::State,
next_button: button::State,
debug: bool,
}
impl Sandbox for Tour {
type Message = Message;
fn new() -> Tour {
Tour {
steps: Steps::new(),
scroll: scrollable::State::new(),
back_button: button::State::new(),
next_button: button::State::new(),
debug: false,
}
}
fn title(&self) -> String {
format!("{} - Iced", self.steps.title())
}
fn update(&mut self, event: Message) {
match event {
Message::BackPressed => {
self.steps.go_back();
}
Message::NextPressed => {
self.steps.advance();
}
Message::StepMessage(step_msg) => {
self.steps.update(step_msg, &mut self.debug);
}
}
}
fn view(&mut self) -> Element<Message> {
let Tour {
steps,
scroll,
back_button,
next_button,
..
} = self;
let mut controls = Row::new();
if steps.has_previous() {
controls = controls.push(
button(back_button, "Back")
.on_press(Message::BackPressed)
.style(style::Button::Secondary),
);
}
controls = controls.push(Space::with_width(Length::Fill));
if steps.can_continue() {
controls = controls.push(
button(next_button, "Next")
.on_press(Message::NextPressed)
.style(style::Button::Primary),
);
}
let content: Element<_> = Column::new()
.max_width(540)
.spacing(20)
.padding(20)
.push(steps.view(self.debug).map(Message::StepMessage))
.push(controls)
.into();
let content = if self.debug {
content.explain(Color::BLACK)
} else {
content
};
let scrollable = Scrollable::new(scroll)
.push(Container::new(content).width(Length::Fill).center_x());
Container::new(scrollable)
.height(Length::Fill)
.center_y()
.into()
}
}
#[derive(Debug, Clone)]
pub enum Message {
BackPressed,
NextPressed,
StepMessage(StepMessage),
}
struct Steps {
steps: Vec<Step>,
current: usize,
}
impl Steps {
fn new() -> Steps {
Steps {
steps: vec![
Step::Welcome,
Step::Slider {
state: slider::State::new(),
value: 50,
},
Step::RowsAndColumns {
layout: Layout::Row,
spacing_slider: slider::State::new(),
spacing: 20,
},
Step::Text {
size_slider: slider::State::new(),
size: 30,
color_sliders: [slider::State::new(); 3],
color: Color::BLACK,
},
Step::Radio { selection: None },
Step::Toggler {
can_continue: false,
},
Step::Image {
width: 300,
slider: slider::State::new(),
},
Step::Scrollable,
Step::TextInput {
value: String::new(),
is_secure: false,
state: text_input::State::new(),
},
Step::Debugger,
Step::End,
],
current: 0,
}
}
fn update(&mut self, msg: StepMessage, debug: &mut bool) {
self.steps[self.current].update(msg, debug);
}
fn view(&mut self, debug: bool) -> Element<StepMessage> {
self.steps[self.current].view(debug)
}
fn advance(&mut self) {
if self.can_continue() {
self.current += 1;
}
}
fn go_back(&mut self) {
if self.has_previous() {
self.current -= 1;
}
}
fn has_previous(&self) -> bool {
self.current > 0
}
fn can_continue(&self) -> bool {
self.current + 1 < self.steps.len()
&& self.steps[self.current].can_continue()
}
fn title(&self) -> &str {
self.steps[self.current].title()
}
}
enum Step {
Welcome,
Slider {
state: slider::State,
value: u8,
},
RowsAndColumns {
layout: Layout,
spacing_slider: slider::State,
spacing: u16,
},
Text {
size_slider: slider::State,
size: u16,
color_sliders: [slider::State; 3],
color: Color,
},
Radio {
selection: Option<Language>,
},
Toggler {
can_continue: bool,
},
Image {
width: u16,
slider: slider::State,
},
Scrollable,
TextInput {
value: String,
is_secure: bool,
state: text_input::State,
},
Debugger,
End,
}
#[derive(Debug, Clone)]
pub enum StepMessage {
SliderChanged(u8),
LayoutChanged(Layout),
SpacingChanged(u16),
TextSizeChanged(u16),
TextColorChanged(Color),
LanguageSelected(Language),
ImageWidthChanged(u16),
InputChanged(String),
ToggleSecureInput(bool),
DebugToggled(bool),
TogglerChanged(bool),
}
impl<'a> Step {
fn update(&mut self, msg: StepMessage, debug: &mut bool) {
match msg {
StepMessage::DebugToggled(value) => {
if let Step::Debugger = self {
*debug = value;
}
}
StepMessage::LanguageSelected(language) => {
if let Step::Radio { selection } = self {
*selection = Some(language);
}
}
StepMessage::SliderChanged(new_value) => {
if let Step::Slider { value, .. } = self {
*value = new_value;
}
}
StepMessage::TextSizeChanged(new_size) => {
if let Step::Text { size, .. } = self {
*size = new_size;
}
}
StepMessage::TextColorChanged(new_color) => {
if let Step::Text { color, .. } = self {
*color = new_color;
}
}
StepMessage::LayoutChanged(new_layout) => {
if let Step::RowsAndColumns { layout, .. } = self {
*layout = new_layout;
}
}
StepMessage::SpacingChanged(new_spacing) => {
if let Step::RowsAndColumns { spacing, .. } = self {
*spacing = new_spacing;
}
}
StepMessage::ImageWidthChanged(new_width) => {
if let Step::Image { width, .. } = self {
*width = new_width;
}
}
StepMessage::InputChanged(new_value) => {
if let Step::TextInput { value, .. } = self {
*value = new_value;
}
}
StepMessage::ToggleSecureInput(toggle) => {
if let Step::TextInput { is_secure, .. } = self {
*is_secure = toggle;
}
}
StepMessage::TogglerChanged(value) => {
if let Step::Toggler { can_continue, .. } = self {
*can_continue = value;
}
}
};
}
fn title(&self) -> &str {
match self {
Step::Welcome => "Welcome",
Step::Radio { .. } => "Radio button",
Step::Toggler { .. } => "Toggler",
Step::Slider { .. } => "Slider",
Step::Text { .. } => "Text",
Step::Image { .. } => "Image",
Step::RowsAndColumns { .. } => "Rows and columns",
Step::Scrollable => "Scrollable",
Step::TextInput { .. } => "Text input",
Step::Debugger => "Debugger",
Step::End => "End",
}
}
fn can_continue(&self) -> bool {
match self {
Step::Welcome => true,
Step::Radio { selection } => *selection == Some(Language::Rust),
Step::Toggler { can_continue } => *can_continue,
Step::Slider { .. } => true,
Step::Text { .. } => true,
Step::Image { .. } => true,
Step::RowsAndColumns { .. } => true,
Step::Scrollable => true,
Step::TextInput { value, .. } => !value.is_empty(),
Step::Debugger => true,
Step::End => false,
}
}
fn view(&mut self, debug: bool) -> Element<StepMessage> {
match self {
Step::Welcome => Self::welcome(),
Step::Radio { selection } => Self::radio(*selection),
Step::Toggler { can_continue } => Self::toggler(*can_continue),
Step::Slider { state, value } => Self::slider(state, *value),
Step::Text {
size_slider,
size,
color_sliders,
color,
} => Self::text(size_slider, *size, color_sliders, *color),
Step::Image { width, slider } => Self::image(*width, slider),
Step::RowsAndColumns {
layout,
spacing_slider,
spacing,
} => Self::rows_and_columns(*layout, spacing_slider, *spacing),
Step::Scrollable => Self::scrollable(),
Step::TextInput {
value,
is_secure,
state,
} => Self::text_input(value, *is_secure, state),
Step::Debugger => Self::debugger(debug),
Step::End => Self::end(),
}
.into()
}
fn container(title: &str) -> Column<'a, StepMessage> {
Column::new().spacing(20).push(Text::new(title).size(50))
}
fn welcome() -> Column<'a, StepMessage> {
Self::container("Welcome!")
.push(Text::new(
"This is a simple tour meant to showcase a bunch of widgets \
that can be easily implemented on top of Iced.",
))
.push(Text::new(
"Iced is a cross-platform GUI library for Rust focused on \
simplicity and type-safety. It is heavily inspired by Elm.",
))
.push(Text::new(
"It was originally born as part of Coffee, an opinionated \
2D game engine for Rust.",
))
.push(Text::new(
"On native platforms, Iced provides by default a renderer \
built on top of wgpu, a graphics library supporting Vulkan, \
Metal, DX11, and DX12.",
))
.push(Text::new(
"Additionally, this tour can also run on WebAssembly thanks \
to dodrio, an experimental VDOM library for Rust.",
))
.push(Text::new(
"You will need to interact with the UI in order to reach the \
end!",
))
}
fn slider(
state: &'a mut slider::State,
value: u8,
) -> Column<'a, StepMessage> {
Self::container("Slider")
.push(Text::new(
"A slider allows you to smoothly select a value from a range \
of values.",
))
.push(Text::new(
"The following slider lets you choose an integer from \
0 to 100:",
))
.push(Slider::new(
state,
0..=100,
value,
StepMessage::SliderChanged,
))
.push(
Text::new(&value.to_string())
.width(Length::Fill)
.horizontal_alignment(HorizontalAlignment::Center),
)
}
fn rows_and_columns(
layout: Layout,
spacing_slider: &'a mut slider::State,
spacing: u16,
) -> Column<'a, StepMessage> {
let row_radio = Radio::new(
Layout::Row,
"Row",
Some(layout),
StepMessage::LayoutChanged,
);
let column_radio = Radio::new(
Layout::Column,
"Column",
Some(layout),
StepMessage::LayoutChanged,
);
let layout_section: Element<_> = match layout {
Layout::Row => Row::new()
.spacing(spacing)
.push(row_radio)
.push(column_radio)
.into(),
Layout::Column => Column::new()
.spacing(spacing)
.push(row_radio)
.push(column_radio)
.into(),
};
let spacing_section = Column::new()
.spacing(10)
.push(Slider::new(
spacing_slider,
0..=80,
spacing,
StepMessage::SpacingChanged,
))
.push(
Text::new(&format!("{} px", spacing))
.width(Length::Fill)
.horizontal_alignment(HorizontalAlignment::Center),
);
Self::container("Rows and columns")
.spacing(spacing)
.push(Text::new(
"Iced uses a layout model based on flexbox to position UI \
elements.",
))
.push(Text::new(
"Rows and columns can be used to distribute content \
horizontally or vertically, respectively.",
))
.push(layout_section)
.push(Text::new(
"You can also easily change the spacing between elements:",
))
.push(spacing_section)
}
fn text(
size_slider: &'a mut slider::State,
size: u16,
color_sliders: &'a mut [slider::State; 3],
color: Color,
) -> Column<'a, StepMessage> {
let size_section = Column::new()
.padding(20)
.spacing(20)
.push(Text::new("You can change its size:"))
.push(
Text::new(&format!("This text is {} pixels", size)).size(size),
)
.push(Slider::new(
size_slider,
10..=70,
size,
StepMessage::TextSizeChanged,
));
let [red, green, blue] = color_sliders;
let color_sliders = Row::new()
.spacing(10)
.push(color_slider(red, color.r, move |r| Color { r, ..color }))
.push(color_slider(green, color.g, move |g| Color { g, ..color }))
.push(color_slider(blue, color.b, move |b| Color { b, ..color }));
let color_section = Column::new()
.padding(20)
.spacing(20)
.push(Text::new("And its color:"))
.push(Text::new(&format!("{:?}", color)).color(color))
.push(color_sliders);
Self::container("Text")
.push(Text::new(
"Text is probably the most essential widget for your UI. \
It will try to adapt to the dimensions of its container.",
))
.push(size_section)
.push(color_section)
}
fn radio(selection: Option<Language>) -> Column<'a, StepMessage> {
let question = Column::new()
.padding(20)
.spacing(10)
.push(Text::new("Iced is written in...").size(24))
.push(Language::all().iter().cloned().fold(
Column::new().padding(10).spacing(20),
|choices, language| {
choices.push(Radio::new(
language,
language,
selection,
StepMessage::LanguageSelected,
))
},
));
Self::container("Radio button")
.push(Text::new(
"A radio button is normally used to represent a choice... \
Surprise test!",
))
.push(question)
.push(Text::new(
"Iced works very well with iterators! The list above is \
basically created by folding a column over the different \
choices, creating a radio button for each one of them!",
))
}
fn toggler(can_continue: bool) -> Column<'a, StepMessage> {
Self::container("Toggler")
.push(Text::new(
"A toggler is mostly used to enable or disable something.",
))
.push(
Container::new(Toggler::new(
can_continue,
String::from("Toggle me to continue..."),
StepMessage::TogglerChanged,
))
.padding([0, 40]),
)
}
fn image(
width: u16,
slider: &'a mut slider::State,
) -> Column<'a, StepMessage> {
Self::container("Image")
.push(Text::new("An image that tries to keep its aspect ratio."))
.push(ferris(width))
.push(Slider::new(
slider,
100..=500,
width,
StepMessage::ImageWidthChanged,
))
.push(
Text::new(&format!("Width: {} px", width.to_string()))
.width(Length::Fill)
.horizontal_alignment(HorizontalAlignment::Center),
)
}
fn scrollable() -> Column<'a, StepMessage> {
Self::container("Scrollable")
.push(Text::new(
"Iced supports scrollable content. Try it out! Find the \
button further below.",
))
.push(
Text::new(
"Tip: You can use the scrollbar to scroll down faster!",
)
.size(16),
)
.push(Column::new().height(Length::Units(4096)))
.push(
Text::new("You are halfway there!")
.width(Length::Fill)
.size(30)
.horizontal_alignment(HorizontalAlignment::Center),
)
.push(Column::new().height(Length::Units(4096)))
.push(ferris(300))
.push(
Text::new("You made it!")
.width(Length::Fill)
.size(50)
.horizontal_alignment(HorizontalAlignment::Center),
)
}
fn text_input(
value: &str,
is_secure: bool,
state: &'a mut text_input::State,
) -> Column<'a, StepMessage> {
let text_input = TextInput::new(
state,
"Type something to continue...",
value,
StepMessage::InputChanged,
)
.padding(10)
.size(30);
Self::container("Text input")
.push(Text::new(
"Use a text input to ask for different kinds of information.",
))
.push(if is_secure {
text_input.password()
} else {
text_input
})
.push(Checkbox::new(
is_secure,
"Enable password mode",
StepMessage::ToggleSecureInput,
))
.push(Text::new(
"A text input produces a message every time it changes. It is \
very easy to keep track of its contents:",
))
.push(
Text::new(if value.is_empty() {
"You have not typed anything yet..."
} else {
value
})
.width(Length::Fill)
.horizontal_alignment(HorizontalAlignment::Center),
)
}
fn debugger(debug: bool) -> Column<'a, StepMessage> {
Self::container("Debugger")
.push(Text::new(
"You can ask Iced to visually explain the layouting of the \
different elements comprising your UI!",
))
.push(Text::new(
"Give it a shot! Check the following checkbox to be able to \
see element boundaries.",
))
.push(if cfg!(target_arch = "wasm32") {
Element::new(
Text::new("Not available on web yet!")
.color([0.7, 0.7, 0.7])
.horizontal_alignment(HorizontalAlignment::Center),
)
} else {
Element::new(Checkbox::new(
debug,
"Explain layout",
StepMessage::DebugToggled,
))
})
.push(Text::new("Feel free to go back and take a look."))
}
fn end() -> Column<'a, StepMessage> {
Self::container("You reached the end!")
.push(Text::new(
"This tour will be updated as more features are added.",
))
.push(Text::new("Make sure to keep an eye on it!"))
}
}
fn ferris<'a>(_width: u16) -> Container<'a, StepMessage> {
Container::new(
// This should go away once we unify resource loading on native
// platforms
Text::new("sorry ferris, you can't come out in glow."),
)
.width(Length::Fill)
.center_x()
}
fn button<'a, Message: Clone>(
state: &'a mut button::State,
label: &str,
) -> Button<'a, Message> {
Button::new(
state,
Text::new(label).horizontal_alignment(HorizontalAlignment::Center),
)
.padding(12)
.min_width(100)
}
fn color_slider(
state: &mut slider::State,
component: f32,
update: impl Fn(f32) -> Color + 'static,
) -> Slider<f64, StepMessage> {
Slider::new(state, 0.0..=1.0, f64::from(component), move |c| {
StepMessage::TextColorChanged(update(c as f32))
})
.step(0.01)
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum Language {
Rust,
Elm,
Ruby,
Haskell,
C,
Other,
}
impl Language {
fn all() -> [Language; 6] {
[
Language::C,
Language::Elm,
Language::Ruby,
Language::Haskell,
Language::Rust,
Language::Other,
]
}
}
impl From<Language> for String {
fn from(language: Language) -> String {
String::from(match language {
Language::Rust => "Rust",
Language::Elm => "Elm",
Language::Ruby => "Ruby",
Language::Haskell => "Haskell",
Language::C => "C",
Language::Other => "Other",
})
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum Layout {
Row,
Column,
}
mod style {
use iced::{button, Background, Color, Vector};
pub enum Button {
Primary,
Secondary,
}
impl button::StyleSheet for Button {
fn active(&self) -> button::Style {
button::Style {
background: Some(Background::Color(match self {
Button::Primary => Color::from_rgb(0.11, 0.42, 0.87),
Button::Secondary => Color::from_rgb(0.5, 0.5, 0.5),
})),
border_radius: 12.0,
shadow_offset: Vector::new(1.0, 1.0),
text_color: Color::from_rgb8(0xEE, 0xEE, 0xEE),
..button::Style::default()
}
}
fn hovered(&self) -> button::Style {
button::Style {
text_color: Color::WHITE,
shadow_offset: Vector::new(1.0, 2.0),
..self.active()
}
}
}
}

View File

@ -0,0 +1,10 @@
[package]
name = "url_handler"
version = "0.1.0"
authors = ["Héctor Ramón Jiménez <hector0193@gmail.com>"]
edition = "2018"
publish = false
[dependencies]
iced = { path = "../.." }
iced_native = { path = "../../native" }

View File

@ -0,0 +1,73 @@
use iced::{
executor, Application, Clipboard, Command, Container, Element, Length,
Settings, Subscription, Text,
};
use iced_native::{
event::{MacOS, PlatformSpecific},
Event,
};
pub fn main() -> iced::Result {
App::run(Settings::default())
}
#[derive(Debug, Default)]
struct App {
url: Option<String>,
}
#[derive(Debug, Clone)]
enum Message {
EventOccurred(iced_native::Event),
}
impl Application for App {
type Executor = executor::Default;
type Message = Message;
type Flags = ();
fn new(_flags: ()) -> (App, Command<Message>) {
(App::default(), Command::none())
}
fn title(&self) -> String {
String::from("Url - Iced")
}
fn update(
&mut self,
message: Message,
_clipboard: &mut Clipboard,
) -> Command<Message> {
match message {
Message::EventOccurred(event) => {
if let Event::PlatformSpecific(PlatformSpecific::MacOS(
MacOS::ReceivedUrl(url),
)) = event
{
self.url = Some(url);
}
}
};
Command::none()
}
fn subscription(&self) -> Subscription<Message> {
iced_native::subscription::events().map(Message::EventOccurred)
}
fn view(&mut self) -> Element<Message> {
let content = match &self.url {
Some(url) => Text::new(format!("{}", url)),
None => Text::new("No URL received yet!"),
};
Container::new(content.size(48))
.width(Length::Fill)
.height(Length::Fill)
.center_x()
.center_y()
.into()
}
}

View File

@ -1,6 +1,6 @@
[package]
name = "iced_futures"
version = "0.1.2"
version = "0.3.0"
authors = ["Héctor Ramón Jiménez <hector0193@gmail.com>"]
edition = "2018"
description = "Commands, subscriptions, and runtimes for Iced"
@ -19,16 +19,27 @@ log = "0.4"
[dependencies.futures]
version = "0.3"
[target.'cfg(not(target_arch = "wasm32"))'.dependencies.tokio]
[target.'cfg(not(target_arch = "wasm32"))'.dependencies.tokio_old]
package = "tokio"
version = "0.2"
optional = true
features = ["rt-core", "rt-threaded", "time", "stream"]
[target.'cfg(not(target_arch = "wasm32"))'.dependencies.tokio]
package = "tokio"
version = "1.0"
optional = true
features = ["rt", "rt-multi-thread", "time"]
[target.'cfg(not(target_arch = "wasm32"))'.dependencies.async-std]
version = "1.0"
optional = true
features = ["unstable"]
[target.'cfg(not(target_arch = "wasm32"))'.dependencies.smol]
version = "1.2"
optional = true
[target.'cfg(target_arch = "wasm32")'.dependencies]
wasm-bindgen-futures = "0.4"

View File

@ -5,9 +5,6 @@ use futures::future::{Future, FutureExt};
///
/// You should be able to turn a future easily into a [`Command`], either by
/// using the `From` trait or [`Command::perform`].
///
/// [`Command`]: struct.Command.html
/// [`Command::perform`]: #method.perform
pub struct Command<T> {
futures: Vec<BoxFuture<T>>,
}
@ -16,8 +13,6 @@ impl<T> Command<T> {
/// Creates an empty [`Command`].
///
/// In other words, a [`Command`] that does nothing.
///
/// [`Command`]: struct.Command.html
pub fn none() -> Self {
Self {
futures: Vec::new(),
@ -25,8 +20,6 @@ impl<T> Command<T> {
}
/// Creates a [`Command`] that performs the action of the given future.
///
/// [`Command`]: struct.Command.html
#[cfg(not(target_arch = "wasm32"))]
pub fn perform<A>(
future: impl Future<Output = T> + 'static + Send,
@ -38,8 +31,6 @@ impl<T> Command<T> {
}
/// Creates a [`Command`] that performs the action of the given future.
///
/// [`Command`]: struct.Command.html
#[cfg(target_arch = "wasm32")]
pub fn perform<A>(
future: impl Future<Output = T> + 'static,
@ -51,8 +42,6 @@ impl<T> Command<T> {
}
/// Applies a transformation to the result of a [`Command`].
///
/// [`Command`]: struct.Command.html
#[cfg(not(target_arch = "wasm32"))]
pub fn map<A>(
mut self,
@ -78,8 +67,6 @@ impl<T> Command<T> {
}
/// Applies a transformation to the result of a [`Command`].
///
/// [`Command`]: struct.Command.html
#[cfg(target_arch = "wasm32")]
pub fn map<A>(mut self, f: impl Fn(T) -> A + 'static) -> Command<A>
where
@ -105,8 +92,6 @@ impl<T> Command<T> {
/// commands.
///
/// Once this command is run, all the commands will be executed at once.
///
/// [`Command`]: struct.Command.html
pub fn batch(commands: impl IntoIterator<Item = Command<T>>) -> Self {
Self {
futures: commands
@ -117,8 +102,6 @@ impl<T> Command<T> {
}
/// Converts a [`Command`] into its underlying list of futures.
///
/// [`Command`]: struct.Command.html
pub fn futures(self) -> Vec<BoxFuture<T>> {
self.futures
}

View File

@ -7,9 +7,15 @@ mod thread_pool;
#[cfg(all(not(target_arch = "wasm32"), feature = "tokio"))]
mod tokio;
#[cfg(all(not(target_arch = "wasm32"), feature = "tokio_old"))]
mod tokio_old;
#[cfg(all(not(target_arch = "wasm32"), feature = "async-std"))]
mod async_std;
#[cfg(all(not(target_arch = "wasm32"), feature = "smol"))]
mod smol;
#[cfg(target_arch = "wasm32")]
mod wasm_bindgen;
@ -21,9 +27,15 @@ pub use thread_pool::ThreadPool;
#[cfg(all(not(target_arch = "wasm32"), feature = "tokio"))]
pub use self::tokio::Tokio;
#[cfg(all(not(target_arch = "wasm32"), feature = "tokio_old"))]
pub use self::tokio_old::TokioOld;
#[cfg(all(not(target_arch = "wasm32"), feature = "async-std"))]
pub use self::async_std::AsyncStd;
#[cfg(all(not(target_arch = "wasm32"), feature = "smol"))]
pub use self::smol::Smol;
#[cfg(target_arch = "wasm32")]
pub use wasm_bindgen::WasmBindgen;
@ -32,21 +44,15 @@ use futures::Future;
/// A type that can run futures.
pub trait Executor: Sized {
/// Creates a new [`Executor`].
///
/// [`Executor`]: trait.Executor.html
fn new() -> Result<Self, futures::io::Error>
where
Self: Sized;
/// Spawns a future in the [`Executor`].
///
/// [`Executor`]: trait.Executor.html
#[cfg(not(target_arch = "wasm32"))]
fn spawn(&self, future: impl Future<Output = ()> + Send + 'static);
/// Spawns a local future in the [`Executor`].
///
/// [`Executor`]: trait.Executor.html
#[cfg(target_arch = "wasm32")]
fn spawn(&self, future: impl Future<Output = ()> + 'static);
@ -56,8 +62,6 @@ pub trait Executor: Sized {
/// before creating futures. This method can be leveraged to set up this
/// global state, call a function, restore the state, and obtain the result
/// of the call.
///
/// [`Executor`]: trait.Executor.html
fn enter<R>(&self, f: impl FnOnce() -> R) -> R {
f()
}

View File

@ -0,0 +1,18 @@
use crate::Executor;
use futures::Future;
/// A `smol` runtime.
#[cfg_attr(docsrs, doc(cfg(feature = "smol")))]
#[derive(Debug)]
pub struct Smol;
impl Executor for Smol {
fn new() -> Result<Self, futures::io::Error> {
Ok(Self)
}
fn spawn(&self, future: impl Future<Output = ()> + Send + 'static) {
smol::spawn(future).detach();
}
}

View File

@ -16,6 +16,7 @@ impl Executor for Tokio {
}
fn enter<R>(&self, f: impl FnOnce() -> R) -> R {
tokio::runtime::Runtime::enter(self, f)
let _guard = tokio::runtime::Runtime::enter(self);
f()
}
}

View File

@ -0,0 +1,21 @@
use crate::Executor;
use futures::Future;
/// An old `tokio` runtime.
#[cfg_attr(docsrs, doc(cfg(feature = "tokio_old")))]
pub type TokioOld = tokio_old::runtime::Runtime;
impl Executor for TokioOld {
fn new() -> Result<Self, futures::io::Error> {
tokio_old::runtime::Runtime::new()
}
fn spawn(&self, future: impl Future<Output = ()> + Send + 'static) {
let _ = tokio_old::runtime::Runtime::spawn(self, future);
}
fn enter<R>(&self, f: impl FnOnce() -> R) -> R {
tokio_old::runtime::Runtime::enter(self, f)
}
}

View File

@ -1,4 +1,6 @@
//! Asynchronous tasks for GUI programming, inspired by Elm.
//!
//! ![The foundations of the Iced ecosystem](https://github.com/hecrj/iced/blob/0525d76ff94e828b7b21634fa94a747022001c83/docs/graphs/foundations.png?raw=true)
#![deny(missing_docs)]
#![deny(missing_debug_implementations)]
#![deny(unused_results)]
@ -15,10 +17,22 @@ pub mod executor;
pub mod subscription;
#[cfg(all(
any(feature = "tokio", feature = "async-std"),
any(
feature = "tokio",
feature = "tokio_old",
feature = "async-std",
feature = "smol"
),
not(target_arch = "wasm32")
))]
#[cfg_attr(docsrs, doc(cfg(any(feature = "tokio", feature = "async-std"))))]
#[cfg_attr(
docsrs,
doc(cfg(any(
feature = "tokio",
feature = "async-std",
feature = "smol"
)))
)]
pub mod time;
pub use command::Command;

View File

@ -8,11 +8,6 @@ use std::marker::PhantomData;
///
/// If you have an [`Executor`], a [`Runtime`] can be leveraged to run any
/// [`Command`] or [`Subscription`] and get notified of the results!
///
/// [`Runtime`]: struct.Runtime.html
/// [`Executor`]: executor/trait.Executor.html
/// [`Command`]: struct.Command.html
/// [`Subscription`]: subscription/struct.Subscription.html
#[derive(Debug)]
pub struct Runtime<Hasher, Event, Executor, Sender, Message> {
executor: Executor,
@ -36,8 +31,6 @@ where
/// You need to provide:
/// - an [`Executor`] to spawn futures
/// - a `Sender` implementing `Sink` to receive the results
///
/// [`Runtime`]: struct.Runtime.html
pub fn new(executor: Executor, sender: Sender) -> Self {
Self {
executor,
@ -50,10 +43,6 @@ where
/// Runs the given closure inside the [`Executor`] of the [`Runtime`].
///
/// See [`Executor::enter`] to learn more.
///
/// [`Executor`]: executor/trait.Executor.html
/// [`Runtime`]: struct.Runtime.html
/// [`Executor::enter`]: executor/trait.Executor.html#method.enter
pub fn enter<R>(&self, f: impl FnOnce() -> R) -> R {
self.executor.enter(f)
}
@ -62,9 +51,6 @@ where
///
/// The resulting `Message` will be forwarded to the `Sender` of the
/// [`Runtime`].
///
/// [`Command`]: struct.Command.html
/// [`Runtime`]: struct.Runtime.html
pub fn spawn(&mut self, command: Command<Message>) {
use futures::{FutureExt, SinkExt};
@ -88,9 +74,7 @@ where
/// It will spawn new streams or close old ones as necessary! See
/// [`Tracker::update`] to learn more about this!
///
/// [`Subscription`]: subscription/struct.Subscription.html
/// [`Runtime`]: struct.Runtime.html
/// [`Tracker::update`]: subscription/struct.Tracker.html#method.update
/// [`Tracker::update`]: subscription::Tracker::update
pub fn track(
&mut self,
subscription: Subscription<Hasher, Event, Message>,
@ -115,9 +99,7 @@ where
///
/// See [`Tracker::broadcast`] to learn more.
///
/// [`Runtime`]: struct.Runtime.html
/// [`Tracker::broadcast`]:
/// subscription/struct.Tracker.html#method.broadcast
/// [`Tracker::broadcast`]: subscription::Tracker::broadcast
pub fn broadcast(&mut self, event: Event) {
self.subscriptions.broadcast(event);
}

View File

@ -19,8 +19,7 @@ use crate::BoxStream;
/// This type is normally aliased by runtimes with a specific `Event` and/or
/// `Hasher`.
///
/// [`Command`]: ../struct.Command.html
/// [`Subscription`]: struct.Subscription.html
/// [`Command`]: crate::Command
pub struct Subscription<Hasher, Event, Output> {
recipes: Vec<Box<dyn Recipe<Hasher, Event, Output = Output>>>,
}
@ -30,8 +29,6 @@ where
H: std::hash::Hasher,
{
/// Returns an empty [`Subscription`] that will not produce any output.
///
/// [`Subscription`]: struct.Subscription.html
pub fn none() -> Self {
Self {
recipes: Vec::new(),
@ -39,9 +36,6 @@ where
}
/// Creates a [`Subscription`] from a [`Recipe`] describing it.
///
/// [`Subscription`]: struct.Subscription.html
/// [`Recipe`]: trait.Recipe.html
pub fn from_recipe(
recipe: impl Recipe<H, E, Output = O> + 'static,
) -> Self {
@ -52,8 +46,6 @@ where
/// Batches all the provided subscriptions and returns the resulting
/// [`Subscription`].
///
/// [`Subscription`]: struct.Subscription.html
pub fn batch(
subscriptions: impl IntoIterator<Item = Subscription<H, E, O>>,
) -> Self {
@ -66,8 +58,6 @@ where
}
/// Returns the different recipes of the [`Subscription`].
///
/// [`Subscription`]: struct.Subscription.html
pub fn recipes(self) -> Vec<Box<dyn Recipe<H, E, Output = O>>> {
self.recipes
}
@ -75,12 +65,6 @@ where
/// Adds a value to the [`Subscription`] context.
///
/// The value will be part of the identity of a [`Subscription`].
///
/// This is necessary if you want to use multiple instances of the same
/// [`Subscription`] to produce different kinds of messages based on some
/// external data.
///
/// [`Subscription`]: struct.Subscription.html
pub fn with<T>(mut self, value: T) -> Subscription<H, E, (T, O)>
where
H: 'static,
@ -101,26 +85,19 @@ where
}
/// Transforms the [`Subscription`] output with the given function.
///
/// [`Subscription`]: struct.Subscription.html
pub fn map<A>(
mut self,
f: impl Fn(O) -> A + Send + Sync + 'static,
) -> Subscription<H, E, A>
pub fn map<A>(mut self, f: fn(O) -> A) -> Subscription<H, E, A>
where
H: 'static,
E: 'static,
O: 'static,
A: 'static,
{
let function = std::sync::Arc::new(f);
Subscription {
recipes: self
.recipes
.drain(..)
.map(|recipe| {
Box::new(Map::new(recipe, function.clone()))
Box::new(Map::new(recipe, f))
as Box<dyn Recipe<H, E, Output = A>>
})
.collect(),
@ -140,9 +117,6 @@ impl<I, O, H> std::fmt::Debug for Subscription<I, O, H> {
/// by runtimes to run and identify subscriptions. You can use it to create your
/// own!
///
/// [`Subscription`]: struct.Subscription.html
/// [`Recipe`]: trait.Recipe.html
///
/// # Examples
/// The repository has a couple of [examples] that use a custom [`Recipe`]:
///
@ -151,23 +125,17 @@ impl<I, O, H> std::fmt::Debug for Subscription<I, O, H> {
/// - [`stopwatch`], a watch with start/stop and reset buttons showcasing how
/// to listen to time.
///
/// [examples]: https://github.com/hecrj/iced/tree/0.1/examples
/// [`download_progress`]: https://github.com/hecrj/iced/tree/0.1/examples/download_progress
/// [`stopwatch`]: https://github.com/hecrj/iced/tree/0.1/examples/stopwatch
/// [examples]: https://github.com/hecrj/iced/tree/0.3/examples
/// [`download_progress`]: https://github.com/hecrj/iced/tree/0.3/examples/download_progress
/// [`stopwatch`]: https://github.com/hecrj/iced/tree/0.3/examples/stopwatch
pub trait Recipe<Hasher: std::hash::Hasher, Event> {
/// The events that will be produced by a [`Subscription`] with this
/// [`Recipe`].
///
/// [`Subscription`]: struct.Subscription.html
/// [`Recipe`]: trait.Recipe.html
type Output;
/// Hashes the [`Recipe`].
///
/// This is used by runtimes to uniquely identify a [`Subscription`].
///
/// [`Subscription`]: struct.Subscription.html
/// [`Recipe`]: trait.Recipe.html
fn hash(&self, state: &mut Hasher);
/// Executes the [`Recipe`] and produces the stream of events of its
@ -175,9 +143,6 @@ pub trait Recipe<Hasher: std::hash::Hasher, Event> {
///
/// It receives some stream of generic events, which is normally defined by
/// shells.
///
/// [`Subscription`]: struct.Subscription.html
/// [`Recipe`]: trait.Recipe.html
fn stream(
self: Box<Self>,
input: BoxStream<Event>,
@ -186,13 +151,13 @@ pub trait Recipe<Hasher: std::hash::Hasher, Event> {
struct Map<Hasher, Event, A, B> {
recipe: Box<dyn Recipe<Hasher, Event, Output = A>>,
mapper: std::sync::Arc<dyn Fn(A) -> B + Send + Sync>,
mapper: fn(A) -> B,
}
impl<H, E, A, B> Map<H, E, A, B> {
fn new(
recipe: Box<dyn Recipe<H, E, Output = A>>,
mapper: std::sync::Arc<dyn Fn(A) -> B + Send + Sync + 'static>,
mapper: fn(A) -> B,
) -> Self {
Map { recipe, mapper }
}
@ -209,8 +174,8 @@ where
fn hash(&self, state: &mut H) {
use std::hash::Hash;
std::any::TypeId::of::<B>().hash(state);
self.recipe.hash(state);
self.mapper.hash(state);
}
fn stream(self: Box<Self>, input: BoxStream<E>) -> BoxStream<Self::Output> {

View File

@ -26,8 +26,6 @@ where
Event: 'static + Send + Clone,
{
/// Creates a new empty [`Tracker`].
///
/// [`Tracker`]: struct.Tracker.html
pub fn new() -> Self {
Self {
subscriptions: HashMap::new(),
@ -52,9 +50,7 @@ where
/// It returns a list of futures that need to be spawned to materialize
/// the [`Tracker`] changes.
///
/// [`Tracker`]: struct.Tracker.html
/// [`Subscription`]: struct.Subscription.html
/// [`Recipe`]: trait.Recipe.html
/// [`Recipe`]: crate::subscription::Recipe
pub fn update<Message, Receiver>(
&mut self,
subscription: Subscription<Hasher, Event, Message>,
@ -132,14 +128,14 @@ where
/// This method publishes the given event to all the subscription streams
/// currently open.
///
/// [`Recipe::stream`]: trait.Recipe.html#tymethod.stream
/// [`Recipe::stream`]: crate::subscription::Recipe::stream
pub fn broadcast(&mut self, event: Event) {
self.subscriptions
.values_mut()
.filter_map(|connection| connection.listener.as_mut())
.for_each(|listener| {
if let Err(error) = listener.try_send(event.clone()) {
log::error!(
log::warn!(
"Error sending event to subscription: {:?}",
error
);

View File

@ -5,8 +5,6 @@ use crate::subscription::{self, Subscription};
///
/// The first message is produced after a `duration`, and then continues to
/// produce more messages every `duration` after that.
///
/// [`Subscription`]: ../subscription/struct.Subscription.html
pub fn every<H: std::hash::Hasher, E>(
duration: std::time::Duration,
) -> Subscription<H, E, std::time::Instant> {
@ -15,6 +13,33 @@ pub fn every<H: std::hash::Hasher, E>(
struct Every(std::time::Duration);
#[cfg(all(
not(any(feature = "tokio_old", feature = "tokio", feature = "async-std")),
feature = "smol"
))]
impl<H, E> subscription::Recipe<H, E> for Every
where
H: std::hash::Hasher,
{
type Output = std::time::Instant;
fn hash(&self, state: &mut H) {
use std::hash::Hash;
std::any::TypeId::of::<Self>().hash(state);
self.0.hash(state);
}
fn stream(
self: Box<Self>,
_input: futures::stream::BoxStream<'static, E>,
) -> futures::stream::BoxStream<'static, Self::Output> {
use futures::stream::StreamExt;
smol::Timer::interval(self.0).boxed()
}
}
#[cfg(feature = "async-std")]
impl<H, E> subscription::Recipe<H, E> for Every
where
@ -41,7 +66,10 @@ where
}
}
#[cfg(all(feature = "tokio", not(feature = "async-std")))]
#[cfg(all(
any(feature = "tokio", feature = "tokio_old"),
not(any(feature = "async-std", feature = "smol"))
))]
impl<H, E> subscription::Recipe<H, E> for Every
where
H: std::hash::Hasher,
@ -61,10 +89,25 @@ where
) -> futures::stream::BoxStream<'static, Self::Output> {
use futures::stream::StreamExt;
#[cfg(feature = "tokio_old")]
use tokio_old as tokio;
let start = tokio::time::Instant::now() + self.0;
tokio::time::interval_at(start, self.0)
.map(|_| std::time::Instant::now())
.boxed()
let stream = {
#[cfg(feature = "tokio")]
{
futures::stream::unfold(
tokio::time::interval_at(start, self.0),
|mut interval| async move {
Some((interval.tick().await, interval))
},
)
}
#[cfg(feature = "tokio_old")]
tokio::time::interval_at(start, self.0)
};
stream.map(tokio::time::Instant::into_std).boxed()
}
}

View File

@ -1,6 +1,6 @@
[package]
name = "iced_glow"
version = "0.1.0"
version = "0.2.0"
authors = ["Héctor Ramón Jiménez <hector0193@gmail.com>"]
edition = "2018"
description = "A glow renderer for iced"
@ -9,25 +9,26 @@ repository = "https://github.com/hecrj/iced"
[features]
canvas = ["iced_graphics/canvas"]
qr_code = ["iced_graphics/qr_code"]
default_system_font = ["iced_graphics/font-source"]
# Not supported yet!
image = []
svg = []
[dependencies]
glow = "0.5"
glow_glyph = "0.3"
glow = "0.6"
glow_glyph = { version = "0.4", git = "https://bics.ga/reivilibre/glow_glyph_compat.git", commit = "13ce059bdfe1b3745fa222d73fb2faaf0ec0311d" }
glyph_brush = "0.7"
euclid = "0.20"
bytemuck = "1.2"
euclid = "0.22"
bytemuck = "1.4"
log = "0.4"
[dependencies.iced_native]
version = "0.2"
version = "0.4"
path = "../native"
[dependencies.iced_graphics]
version = "0.1"
version = "0.2"
path = "../graphics"
features = ["font-fallback", "font-icons", "opengl"]

View File

@ -23,8 +23,6 @@ pub struct Backend {
impl Backend {
/// Creates a new [`Backend`].
///
/// [`Backend`]: struct.Backend.html
pub fn new(gl: &glow::Context, settings: Settings) -> Self {
let text_pipeline = text::Pipeline::new(gl, settings.default_font);
let quad_pipeline = quad::Pipeline::new(gl);

Some files were not shown because too many files have changed in this diff Show More