summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--.github/workflows/audit.yml2
-rw-r--r--.github/workflows/document.yml2
-rw-r--r--Cargo.toml7
-rw-r--r--core/Cargo.toml10
-rw-r--r--core/src/element.rs18
-rw-r--r--core/src/keyboard.rs7
-rw-r--r--core/src/keyboard/event.rs29
-rw-r--r--core/src/keyboard/key.rs744
-rw-r--r--core/src/keyboard/key_code.rs203
-rw-r--r--core/src/keyboard/location.rs12
-rw-r--r--core/src/layout.rs106
-rw-r--r--core/src/layout/flex.rs117
-rw-r--r--core/src/layout/limits.rs88
-rw-r--r--core/src/layout/node.rs35
-rw-r--r--core/src/length.rs18
-rw-r--r--core/src/lib.rs2
-rw-r--r--core/src/mouse/button.rs6
-rw-r--r--core/src/padding.rs6
-rw-r--r--core/src/point.rs20
-rw-r--r--core/src/size.rs24
-rw-r--r--core/src/time.rs13
-rw-r--r--core/src/widget.rs15
-rw-r--r--core/src/widget/text.rs58
-rw-r--r--core/src/window/event.rs2
-rw-r--r--examples/custom_quad/src/main.rs11
-rw-r--r--examples/custom_widget/src/main.rs11
-rw-r--r--examples/download_progress/src/main.rs19
-rw-r--r--examples/editor/Cargo.toml2
-rw-r--r--examples/editor/src/main.rs4
-rw-r--r--examples/events/src/main.rs3
-rw-r--r--examples/game_of_life/src/main.rs4
-rw-r--r--examples/geometry/src/main.rs15
-rw-r--r--examples/integration/src/controls.rs39
-rw-r--r--examples/integration/src/main.rs124
-rw-r--r--examples/layout/Cargo.toml9
-rw-r--r--examples/layout/src/main.rs371
-rw-r--r--examples/lazy/src/main.rs46
-rw-r--r--examples/loading_spinners/src/circular.rs22
-rw-r--r--examples/loading_spinners/src/linear.rs22
-rw-r--r--examples/loading_spinners/src/main.rs11
-rw-r--r--examples/modal/src/main.rs27
-rw-r--r--examples/pane_grid/src/main.rs32
-rw-r--r--examples/pick_list/src/main.rs9
-rw-r--r--examples/screenshot/src/main.rs33
-rw-r--r--examples/scrollable/src/main.rs52
-rw-r--r--examples/sierpinski_triangle/src/main.rs2
-rw-r--r--examples/stopwatch/src/main.rs12
-rw-r--r--examples/styling/src/main.rs26
-rw-r--r--examples/svg/src/main.rs1
-rw-r--r--examples/toast/src/main.rs29
-rw-r--r--examples/todos/src/main.rs25
-rw-r--r--examples/tour/src/main.rs7
-rw-r--r--examples/vectorial_text/Cargo.toml9
-rw-r--r--examples/vectorial_text/src/main.rs175
-rw-r--r--examples/websocket/src/main.rs15
-rw-r--r--futures/src/keyboard.rs16
-rw-r--r--graphics/src/damage.rs5
-rw-r--r--graphics/src/geometry/text.rs135
-rw-r--r--graphics/src/primitive.rs2
-rw-r--r--graphics/src/text.rs37
-rw-r--r--graphics/src/text/paragraph.rs25
-rw-r--r--renderer/src/backend.rs100
-rw-r--r--runtime/src/window.rs22
-rw-r--r--runtime/src/window/action.rs23
-rw-r--r--src/lib.rs3
-rw-r--r--src/time.rs1
-rw-r--r--style/src/container.rs26
-rw-r--r--style/src/theme.rs42
-rw-r--r--style/src/theme/palette.rs3
-rw-r--r--tiny_skia/src/backend.rs48
-rw-r--r--tiny_skia/src/geometry.rs131
-rw-r--r--tiny_skia/src/primitive.rs4
-rw-r--r--tiny_skia/src/text.rs43
-rw-r--r--wgpu/src/geometry.rs167
-rw-r--r--wgpu/src/layer.rs15
-rw-r--r--wgpu/src/layer/text.rs5
-rw-r--r--wgpu/src/primitive/pipeline.rs4
-rw-r--r--wgpu/src/text.rs22
-rw-r--r--widget/src/button.rs34
-rw-r--r--widget/src/canvas.rs21
-rw-r--r--widget/src/canvas/event.rs2
-rw-r--r--widget/src/checkbox.rs11
-rw-r--r--widget/src/column.rs59
-rw-r--r--widget/src/combo_box.rs26
-rw-r--r--widget/src/container.rs53
-rw-r--r--widget/src/helpers.rs25
-rw-r--r--widget/src/image.rs13
-rw-r--r--widget/src/image/viewer.rs20
-rw-r--r--widget/src/keyed/column.rs60
-rw-r--r--widget/src/lazy.rs11
-rw-r--r--widget/src/lazy/component.rs11
-rw-r--r--widget/src/lazy/helpers.rs3
-rw-r--r--widget/src/lazy/responsive.rs11
-rw-r--r--widget/src/mouse_area.rs10
-rw-r--r--widget/src/overlay/menu.rs23
-rw-r--r--widget/src/pane_grid.rs89
-rw-r--r--widget/src/pane_grid/content.rs9
-rw-r--r--widget/src/pane_grid/state.rs17
-rw-r--r--widget/src/pane_grid/title_bar.rs18
-rw-r--r--widget/src/pick_list.rs24
-rw-r--r--widget/src/progress_bar.rs23
-rw-r--r--widget/src/qr_code.rs11
-rw-r--r--widget/src/radio.rs11
-rw-r--r--widget/src/row.rs58
-rw-r--r--widget/src/rule.rs15
-rw-r--r--widget/src/scrollable.rs50
-rw-r--r--widget/src/shader.rs16
-rw-r--r--widget/src/shader/event.rs2
-rw-r--r--widget/src/slider.rs16
-rw-r--r--widget/src/space.rs15
-rw-r--r--widget/src/svg.rs16
-rw-r--r--widget/src/text_editor.rs94
-rw-r--r--widget/src/text_input.rs226
-rw-r--r--widget/src/toggler.rs11
-rw-r--r--widget/src/tooltip.rs16
-rw-r--r--widget/src/vertical_slider.rs16
-rw-r--r--winit/src/application.rs399
-rw-r--r--winit/src/application/state.rs24
-rw-r--r--winit/src/conversion.rs583
-rw-r--r--winit/src/multi_window.rs594
-rw-r--r--winit/src/multi_window/state.rs24
121 files changed, 4029 insertions, 2331 deletions
diff --git a/.github/workflows/audit.yml b/.github/workflows/audit.yml
index 80bbcacd..57169796 100644
--- a/.github/workflows/audit.yml
+++ b/.github/workflows/audit.yml
@@ -27,4 +27,4 @@ jobs:
- name: Delete `web-sys` dependency from `integration` example
run: sed -i '$d' examples/integration/Cargo.toml
- name: Find outdated dependencies
- run: cargo outdated --workspace --exit-code 1
+ run: cargo outdated --workspace --exit-code 1 --ignore raw-window-handle
diff --git a/.github/workflows/document.yml b/.github/workflows/document.yml
index 62e28ca3..35bf10f4 100644
--- a/.github/workflows/document.yml
+++ b/.github/workflows/document.yml
@@ -8,7 +8,7 @@ jobs:
steps:
- uses: hecrj/setup-rust-action@v1
with:
- rust-version: nightly
+ rust-version: nightly-2023-12-11
- uses: actions/checkout@v2
- name: Generate documentation
run: |
diff --git a/Cargo.toml b/Cargo.toml
index 0afbcd51..ac72f212 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -126,11 +126,10 @@ bytemuck = { version = "1.0", features = ["derive"] }
cosmic-text = "0.10"
futures = "0.3"
glam = "0.24"
-glyphon = { git = "https://github.com/grovesNL/glyphon.git", rev = "2caa9fc5e5923c1d827d177c3619cab7e9885b85" }
+glyphon = "0.4"
guillotiere = "0.6"
half = "2.2"
image = "0.24"
-instant = "0.1"
kamadak-exif = "0.5"
kurbo = "0.9"
log = "0.4"
@@ -145,6 +144,7 @@ raw-window-handle = "0.5"
resvg = "0.36"
rustc-hash = "1.0"
smol = "1.0"
+smol_str = "0.2"
softbuffer = "0.2"
syntect = "5.1"
sysinfo = "0.28"
@@ -157,7 +157,8 @@ unicode-segmentation = "1.0"
wasm-bindgen-futures = "0.4"
wasm-timer = "0.2"
web-sys = "0.3"
+web-time = "0.2"
wgpu = "0.18"
winapi = "0.3"
window_clipboard = "0.3"
-winit = { git = "https://github.com/iced-rs/winit.git", rev = "c52db2045d0a2f1b8d9923870de1d4ab1994146e", default-features = false }
+winit = { git = "https://github.com/iced-rs/winit.git", rev = "b91e39ece2c0d378c3b80da7f3ab50e17bb798a5", features = ["rwh_05"] }
diff --git a/core/Cargo.toml b/core/Cargo.toml
index 4672c754..be92a572 100644
--- a/core/Cargo.toml
+++ b/core/Cargo.toml
@@ -13,18 +13,18 @@ keywords.workspace = true
[dependencies]
bitflags.workspace = true
log.workspace = true
+num-traits.workspace = true
+smol_str.workspace = true
thiserror.workspace = true
+web-time.workspace = true
xxhash-rust.workspace = true
-num-traits.workspace = true
palette.workspace = true
palette.optional = true
-[target.'cfg(target_arch = "wasm32")'.dependencies]
-instant.workspace = true
-
[target.'cfg(windows)'.dependencies]
-raw-window-handle.workspace = true
+# TODO: Use `workspace` dependency once `wgpu` upgrades `raw-window-handle`
+raw-window-handle = "0.6"
[dev-dependencies]
approx = "0.5"
diff --git a/core/src/element.rs b/core/src/element.rs
index dea111af..8b510218 100644
--- a/core/src/element.rs
+++ b/core/src/element.rs
@@ -6,7 +6,7 @@ use crate::renderer;
use crate::widget;
use crate::widget::tree::{self, Tree};
use crate::{
- Clipboard, Color, Layout, Length, Rectangle, Shell, Vector, Widget,
+ Clipboard, Color, Layout, Length, Rectangle, Shell, Size, Vector, Widget,
};
use std::any::Any;
@@ -296,12 +296,8 @@ where
self.widget.diff(tree);
}
- fn width(&self) -> Length {
- self.widget.width()
- }
-
- fn height(&self) -> Length {
- self.widget.height()
+ fn size(&self) -> Size<Length> {
+ self.widget.size()
}
fn layout(
@@ -466,12 +462,8 @@ impl<'a, Message, Renderer> Widget<Message, Renderer>
where
Renderer: crate::Renderer,
{
- fn width(&self) -> Length {
- self.element.widget.width()
- }
-
- fn height(&self) -> Length {
- self.element.widget.height()
+ fn size(&self) -> Size<Length> {
+ self.element.widget.size()
}
fn tag(&self) -> tree::Tag {
diff --git a/core/src/keyboard.rs b/core/src/keyboard.rs
index 4c6ca08d..b810ccb0 100644
--- a/core/src/keyboard.rs
+++ b/core/src/keyboard.rs
@@ -1,8 +1,11 @@
//! Listen to keyboard events.
+pub mod key;
+
mod event;
-mod key_code;
+mod location;
mod modifiers;
pub use event::Event;
-pub use key_code::KeyCode;
+pub use key::Key;
+pub use location::Location;
pub use modifiers::Modifiers;
diff --git a/core/src/keyboard/event.rs b/core/src/keyboard/event.rs
index 016761af..1eb42334 100644
--- a/core/src/keyboard/event.rs
+++ b/core/src/keyboard/event.rs
@@ -1,4 +1,5 @@
-use super::{KeyCode, Modifiers};
+use crate::keyboard::{Key, Location, Modifiers};
+use crate::SmolStr;
/// A keyboard event.
///
@@ -6,29 +7,35 @@ use super::{KeyCode, Modifiers};
/// additional events, feel free to [open an issue] and share your use case!_
///
/// [open an issue]: https://github.com/iced-rs/iced/issues
-#[derive(Debug, Clone, Copy, PartialEq, Eq)]
+#[derive(Debug, Clone, PartialEq, Eq)]
pub enum Event {
/// A keyboard key was pressed.
KeyPressed {
- /// The key identifier
- key_code: KeyCode,
+ /// The key pressed.
+ key: Key,
- /// The state of the modifier keys
+ /// The location of the key.
+ location: Location,
+
+ /// The state of the modifier keys.
modifiers: Modifiers,
+
+ /// The text produced by the key press, if any.
+ text: Option<SmolStr>,
},
/// A keyboard key was released.
KeyReleased {
- /// The key identifier
- key_code: KeyCode,
+ /// The key released.
+ key: Key,
- /// The state of the modifier keys
+ /// The location of the key.
+ location: Location,
+
+ /// The state of the modifier keys.
modifiers: Modifiers,
},
- /// A unicode character was received.
- CharacterReceived(char),
-
/// The keyboard modifiers have changed.
ModifiersChanged(Modifiers),
}
diff --git a/core/src/keyboard/key.rs b/core/src/keyboard/key.rs
new file mode 100644
index 00000000..dbde5196
--- /dev/null
+++ b/core/src/keyboard/key.rs
@@ -0,0 +1,744 @@
+//! Identify keyboard keys.
+use crate::SmolStr;
+
+/// A key on the keyboard.
+///
+/// This is mostly the `Key` type found in [`winit`].
+///
+/// [`winit`]: https://docs.rs/winit/0.29.10/winit/keyboard/enum.Key.html
+#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
+pub enum Key<C = SmolStr> {
+ /// A key with an established name.
+ Named(Named),
+
+ /// A key string that corresponds to the character typed by the user, taking into account the
+ /// user’s current locale setting, and any system-level keyboard mapping overrides that are in
+ /// effect.
+ Character(C),
+
+ /// An unidentified key.
+ Unidentified,
+}
+
+impl Key {
+ /// Convert `Key::Character(SmolStr)` to `Key::Character(&str)` so you can more easily match on
+ /// `Key`. All other variants remain unchanged.
+ pub fn as_ref(&self) -> Key<&str> {
+ match self {
+ Self::Named(named) => Key::Named(*named),
+ Self::Character(c) => Key::Character(c.as_ref()),
+ Self::Unidentified => Key::Unidentified,
+ }
+ }
+}
+
+/// A named key.
+///
+/// This is mostly the `NamedKey` type found in [`winit`].
+///
+/// [`winit`]: https://docs.rs/winit/0.29.10/winit/keyboard/enum.Key.html
+#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
+#[allow(missing_docs)]
+pub enum Named {
+ /// The `Alt` (Alternative) key.
+ ///
+ /// This key enables the alternate modifier function for interpreting concurrent or subsequent
+ /// keyboard input. This key value is also used for the Apple <kbd>Option</kbd> key.
+ Alt,
+ /// The Alternate Graphics (<kbd>AltGr</kbd> or <kbd>AltGraph</kbd>) key.
+ ///
+ /// This key is used enable the ISO Level 3 shift modifier (the standard `Shift` key is the
+ /// level 2 modifier).
+ AltGraph,
+ /// The `Caps Lock` (Capital) key.
+ ///
+ /// Toggle capital character lock function for interpreting subsequent keyboard input event.
+ CapsLock,
+ /// The `Control` or `Ctrl` key.
+ ///
+ /// Used to enable control modifier function for interpreting concurrent or subsequent keyboard
+ /// input.
+ Control,
+ /// The Function switch `Fn` key. Activating this key simultaneously with another key changes
+ /// that key’s value to an alternate character or function. This key is often handled directly
+ /// in the keyboard hardware and does not usually generate key events.
+ Fn,
+ /// The Function-Lock (`FnLock` or `F-Lock`) key. Activating this key switches the mode of the
+ /// keyboard to changes some keys' values to an alternate character or function. This key is
+ /// often handled directly in the keyboard hardware and does not usually generate key events.
+ FnLock,
+ /// The `NumLock` or Number Lock key. Used to toggle numpad mode function for interpreting
+ /// subsequent keyboard input.
+ NumLock,
+ /// Toggle between scrolling and cursor movement modes.
+ ScrollLock,
+ /// Used to enable shift modifier function for interpreting concurrent or subsequent keyboard
+ /// input.
+ Shift,
+ /// The Symbol modifier key (used on some virtual keyboards).
+ Symbol,
+ SymbolLock,
+ // Legacy modifier key. Also called "Super" in certain places.
+ Meta,
+ // Legacy modifier key.
+ Hyper,
+ /// Used to enable "super" modifier function for interpreting concurrent or subsequent keyboard
+ /// input. This key value is used for the "Windows Logo" key and the Apple `Command` or `⌘` key.
+ ///
+ /// Note: In some contexts (e.g. the Web) this is referred to as the "Meta" key.
+ Super,
+ /// The `Enter` or `↵` key. Used to activate current selection or accept current input. This key
+ /// value is also used for the `Return` (Macintosh numpad) key. This key value is also used for
+ /// the Android `KEYCODE_DPAD_CENTER`.
+ Enter,
+ /// The Horizontal Tabulation `Tab` key.
+ Tab,
+ /// Used in text to insert a space between words. Usually located below the character keys.
+ Space,
+ /// Navigate or traverse downward. (`KEYCODE_DPAD_DOWN`)
+ ArrowDown,
+ /// Navigate or traverse leftward. (`KEYCODE_DPAD_LEFT`)
+ ArrowLeft,
+ /// Navigate or traverse rightward. (`KEYCODE_DPAD_RIGHT`)
+ ArrowRight,
+ /// Navigate or traverse upward. (`KEYCODE_DPAD_UP`)
+ ArrowUp,
+ /// The End key, used with keyboard entry to go to the end of content (`KEYCODE_MOVE_END`).
+ End,
+ /// The Home key, used with keyboard entry, to go to start of content (`KEYCODE_MOVE_HOME`).
+ /// For the mobile phone `Home` key (which goes to the phone’s main screen), use [`GoHome`].
+ ///
+ /// [`GoHome`]: Self::GoHome
+ Home,
+ /// Scroll down or display next page of content.
+ PageDown,
+ /// Scroll up or display previous page of content.
+ PageUp,
+ /// Used to remove the character to the left of the cursor. This key value is also used for
+ /// the key labeled `Delete` on MacOS keyboards.
+ Backspace,
+ /// Remove the currently selected input.
+ Clear,
+ /// Copy the current selection. (`APPCOMMAND_COPY`)
+ Copy,
+ /// The Cursor Select key.
+ CrSel,
+ /// Cut the current selection. (`APPCOMMAND_CUT`)
+ Cut,
+ /// Used to delete the character to the right of the cursor. This key value is also used for the
+ /// key labeled `Delete` on MacOS keyboards when `Fn` is active.
+ Delete,
+ /// The Erase to End of Field key. This key deletes all characters from the current cursor
+ /// position to the end of the current field.
+ EraseEof,
+ /// The Extend Selection (Exsel) key.
+ ExSel,
+ /// Toggle between text modes for insertion or overtyping.
+ /// (`KEYCODE_INSERT`)
+ Insert,
+ /// The Paste key. (`APPCOMMAND_PASTE`)
+ Paste,
+ /// Redo the last action. (`APPCOMMAND_REDO`)
+ Redo,
+ /// Undo the last action. (`APPCOMMAND_UNDO`)
+ Undo,
+ /// The Accept (Commit, OK) key. Accept current option or input method sequence conversion.
+ Accept,
+ /// Redo or repeat an action.
+ Again,
+ /// The Attention (Attn) key.
+ Attn,
+ Cancel,
+ /// Show the application’s context menu.
+ /// This key is commonly found between the right `Super` key and the right `Control` key.
+ ContextMenu,
+ /// The `Esc` key. This key was originally used to initiate an escape sequence, but is
+ /// now more generally used to exit or "escape" the current context, such as closing a dialog
+ /// or exiting full screen mode.
+ Escape,
+ Execute,
+ /// Open the Find dialog. (`APPCOMMAND_FIND`)
+ Find,
+ /// Open a help dialog or toggle display of help information. (`APPCOMMAND_HELP`,
+ /// `KEYCODE_HELP`)
+ Help,
+ /// Pause the current state or application (as appropriate).
+ ///
+ /// Note: Do not use this value for the `Pause` button on media controllers. Use `"MediaPause"`
+ /// instead.
+ Pause,
+ /// Play or resume the current state or application (as appropriate).
+ ///
+ /// Note: Do not use this value for the `Play` button on media controllers. Use `"MediaPlay"`
+ /// instead.
+ Play,
+ /// The properties (Props) key.
+ Props,
+ Select,
+ /// The ZoomIn key. (`KEYCODE_ZOOM_IN`)
+ ZoomIn,
+ /// The ZoomOut key. (`KEYCODE_ZOOM_OUT`)
+ ZoomOut,
+ /// The Brightness Down key. Typically controls the display brightness.
+ /// (`KEYCODE_BRIGHTNESS_DOWN`)
+ BrightnessDown,
+ /// The Brightness Up key. Typically controls the display brightness. (`KEYCODE_BRIGHTNESS_UP`)
+ BrightnessUp,
+ /// Toggle removable media to eject (open) and insert (close) state. (`KEYCODE_MEDIA_EJECT`)
+ Eject,
+ LogOff,
+ /// Toggle power state. (`KEYCODE_POWER`)
+ /// Note: Note: Some devices might not expose this key to the operating environment.
+ Power,
+ /// The `PowerOff` key. Sometime called `PowerDown`.
+ PowerOff,
+ /// Initiate print-screen function.
+ PrintScreen,
+ /// The Hibernate key. This key saves the current state of the computer to disk so that it can
+ /// be restored. The computer will then shutdown.
+ Hibernate,
+ /// The Standby key. This key turns off the display and places the computer into a low-power
+ /// mode without completely shutting down. It is sometimes labelled `Suspend` or `Sleep` key.
+ /// (`KEYCODE_SLEEP`)
+ Standby,
+ /// The WakeUp key. (`KEYCODE_WAKEUP`)
+ WakeUp,
+ /// Initate the multi-candidate mode.
+ AllCandidates,
+ Alphanumeric,
+ /// Initiate the Code Input mode to allow characters to be entered by
+ /// their code points.
+ CodeInput,
+ /// The Compose key, also known as "Multi_key" on the X Window System. This key acts in a
+ /// manner similar to a dead key, triggering a mode where subsequent key presses are combined to
+ /// produce a different character.
+ Compose,
+ /// Convert the current input method sequence.
+ Convert,
+ /// The Final Mode `Final` key used on some Asian keyboards, to enable the final mode for IMEs.
+ FinalMode,
+ /// Switch to the first character group. (ISO/IEC 9995)
+ GroupFirst,
+ /// Switch to the last character group. (ISO/IEC 9995)
+ GroupLast,
+ /// Switch to the next character group. (ISO/IEC 9995)
+ GroupNext,
+ /// Switch to the previous character group. (ISO/IEC 9995)
+ GroupPrevious,
+ /// Toggle between or cycle through input modes of IMEs.
+ ModeChange,
+ NextCandidate,
+ /// Accept current input method sequence without
+ /// conversion in IMEs.
+ NonConvert,
+ PreviousCandidate,
+ Process,
+ SingleCandidate,
+ /// Toggle between Hangul and English modes.
+ HangulMode,
+ HanjaMode,
+ JunjaMode,
+ /// The Eisu key. This key may close the IME, but its purpose is defined by the current IME.
+ /// (`KEYCODE_EISU`)
+ Eisu,
+ /// The (Half-Width) Characters key.
+ Hankaku,
+ /// The Hiragana (Japanese Kana characters) key.
+ Hiragana,
+ /// The Hiragana/Katakana toggle key. (`KEYCODE_KATAKANA_HIRAGANA`)
+ HiraganaKatakana,
+ /// The Kana Mode (Kana Lock) key. This key is used to enter hiragana mode (typically from
+ /// romaji mode).
+ KanaMode,
+ /// The Kanji (Japanese name for ideographic characters of Chinese origin) Mode key. This key is
+ /// typically used to switch to a hiragana keyboard for the purpose of converting input into
+ /// kanji. (`KEYCODE_KANA`)
+ KanjiMode,
+ /// The Katakana (Japanese Kana characters) key.
+ Katakana,
+ /// The Roman characters function key.
+ Romaji,
+ /// The Zenkaku (Full-Width) Characters key.
+ Zenkaku,
+ /// The Zenkaku/Hankaku (full-width/half-width) toggle key. (`KEYCODE_ZENKAKU_HANKAKU`)
+ ZenkakuHankaku,
+ /// General purpose virtual function key, as index 1.
+ Soft1,
+ /// General purpose virtual function key, as index 2.
+ Soft2,
+ /// General purpose virtual function key, as index 3.
+ Soft3,
+ /// General purpose virtual function key, as index 4.
+ Soft4,
+ /// Select next (numerically or logically) lower channel. (`APPCOMMAND_MEDIA_CHANNEL_DOWN`,
+ /// `KEYCODE_CHANNEL_DOWN`)
+ ChannelDown,
+ /// Select next (numerically or logically) higher channel. (`APPCOMMAND_MEDIA_CHANNEL_UP`,
+ /// `KEYCODE_CHANNEL_UP`)
+ ChannelUp,
+ /// Close the current document or message (Note: This doesn’t close the application).
+ /// (`APPCOMMAND_CLOSE`)
+ Close,
+ /// Open an editor to forward the current message. (`APPCOMMAND_FORWARD_MAIL`)
+ MailForward,
+ /// Open an editor to reply to the current message. (`APPCOMMAND_REPLY_TO_MAIL`)
+ MailReply,
+ /// Send the current message. (`APPCOMMAND_SEND_MAIL`)
+ MailSend,
+ /// Close the current media, for example to close a CD or DVD tray. (`KEYCODE_MEDIA_CLOSE`)
+ MediaClose,
+ /// Initiate or continue forward playback at faster than normal speed, or increase speed if
+ /// already fast forwarding. (`APPCOMMAND_MEDIA_FAST_FORWARD`, `KEYCODE_MEDIA_FAST_FORWARD`)
+ MediaFastForward,
+ /// Pause the currently playing media. (`APPCOMMAND_MEDIA_PAUSE`, `KEYCODE_MEDIA_PAUSE`)
+ ///
+ /// Note: Media controller devices should use this value rather than `"Pause"` for their pause
+ /// keys.
+ MediaPause,
+ /// Initiate or continue media playback at normal speed, if not currently playing at normal
+ /// speed. (`APPCOMMAND_MEDIA_PLAY`, `KEYCODE_MEDIA_PLAY`)
+ MediaPlay,
+ /// Toggle media between play and pause states. (`APPCOMMAND_MEDIA_PLAY_PAUSE`,
+ /// `KEYCODE_MEDIA_PLAY_PAUSE`)
+ MediaPlayPause,
+ /// Initiate or resume recording of currently selected media. (`APPCOMMAND_MEDIA_RECORD`,
+ /// `KEYCODE_MEDIA_RECORD`)
+ MediaRecord,
+ /// Initiate or continue reverse playback at faster than normal speed, or increase speed if
+ /// already rewinding. (`APPCOMMAND_MEDIA_REWIND`, `KEYCODE_MEDIA_REWIND`)
+ MediaRewind,
+ /// Stop media playing, pausing, forwarding, rewinding, or recording, if not already stopped.
+ /// (`APPCOMMAND_MEDIA_STOP`, `KEYCODE_MEDIA_STOP`)
+ MediaStop,
+ /// Seek to next media or program track. (`APPCOMMAND_MEDIA_NEXTTRACK`, `KEYCODE_MEDIA_NEXT`)
+ MediaTrackNext,
+ /// Seek to previous media or program track. (`APPCOMMAND_MEDIA_PREVIOUSTRACK`,
+ /// `KEYCODE_MEDIA_PREVIOUS`)
+ MediaTrackPrevious,
+ /// Open a new document or message. (`APPCOMMAND_NEW`)
+ New,
+ /// Open an existing document or message. (`APPCOMMAND_OPEN`)
+ Open,
+ /// Print the current document or message. (`APPCOMMAND_PRINT`)
+ Print,
+ /// Save the current document or message. (`APPCOMMAND_SAVE`)
+ Save,
+ /// Spellcheck the current document or selection. (`APPCOMMAND_SPELL_CHECK`)
+ SpellCheck,
+ /// The `11` key found on media numpads that
+ /// have buttons from `1` ... `12`.
+ Key11,
+ /// The `12` key found on media numpads that
+ /// have buttons from `1` ... `12`.
+ Key12,
+ /// Adjust audio balance leftward. (`VK_AUDIO_BALANCE_LEFT`)
+ AudioBalanceLeft,
+ /// Adjust audio balance rightward. (`VK_AUDIO_BALANCE_RIGHT`)
+ AudioBalanceRight,
+ /// Decrease audio bass boost or cycle down through bass boost states. (`APPCOMMAND_BASS_DOWN`,
+ /// `VK_BASS_BOOST_DOWN`)
+ AudioBassBoostDown,
+ /// Toggle bass boost on/off. (`APPCOMMAND_BASS_BOOST`)
+ AudioBassBoostToggle,
+ /// Increase audio bass boost or cycle up through bass boost states. (`APPCOMMAND_BASS_UP`,
+ /// `VK_BASS_BOOST_UP`)
+ AudioBassBoostUp,
+ /// Adjust audio fader towards front. (`VK_FADER_FRONT`)
+ AudioFaderFront,
+ /// Adjust audio fader towards rear. (`VK_FADER_REAR`)
+ AudioFaderRear,
+ /// Advance surround audio mode to next available mode. (`VK_SURROUND_MODE_NEXT`)
+ AudioSurroundModeNext,
+ /// Decrease treble. (`APPCOMMAND_TREBLE_DOWN`)
+ AudioTrebleDown,
+ /// Increase treble. (`APPCOMMAND_TREBLE_UP`)
+ AudioTrebleUp,
+ /// Decrease audio volume. (`APPCOMMAND_VOLUME_DOWN`, `KEYCODE_VOLUME_DOWN`)
+ AudioVolumeDown,
+ /// Increase audio volume. (`APPCOMMAND_VOLUME_UP`, `KEYCODE_VOLUME_UP`)
+ AudioVolumeUp,
+ /// Toggle between muted state and prior volume level. (`APPCOMMAND_VOLUME_MUTE`,
+ /// `KEYCODE_VOLUME_MUTE`)
+ AudioVolumeMute,
+ /// Toggle the microphone on/off. (`APPCOMMAND_MIC_ON_OFF_TOGGLE`)
+ MicrophoneToggle,
+ /// Decrease microphone volume. (`APPCOMMAND_MICROPHONE_VOLUME_DOWN`)
+ MicrophoneVolumeDown,
+ /// Increase microphone volume. (`APPCOMMAND_MICROPHONE_VOLUME_UP`)
+ MicrophoneVolumeUp,
+ /// Mute the microphone. (`APPCOMMAND_MICROPHONE_VOLUME_MUTE`, `KEYCODE_MUTE`)
+ MicrophoneVolumeMute,
+ /// Show correction list when a word is incorrectly identified. (`APPCOMMAND_CORRECTION_LIST`)
+ SpeechCorrectionList,
+ /// Toggle between dictation mode and command/control mode.
+ /// (`APPCOMMAND_DICTATE_OR_COMMAND_CONTROL_TOGGLE`)
+ SpeechInputToggle,
+ /// The first generic "LaunchApplication" key. This is commonly associated with launching "My
+ /// Computer", and may have a computer symbol on the key. (`APPCOMMAND_LAUNCH_APP1`)
+ LaunchApplication1,
+ /// The second generic "LaunchApplication" key. This is commonly associated with launching
+ /// "Calculator", and may have a calculator symbol on the key. (`APPCOMMAND_LAUNCH_APP2`,
+ /// `KEYCODE_CALCULATOR`)
+ LaunchApplication2,
+ /// The "Calendar" key. (`KEYCODE_CALENDAR`)
+ LaunchCalendar,
+ /// The "Contacts" key. (`KEYCODE_CONTACTS`)
+ LaunchContacts,
+ /// The "Mail" key. (`APPCOMMAND_LAUNCH_MAIL`)
+ LaunchMail,
+ /// The "Media Player" key. (`APPCOMMAND_LAUNCH_MEDIA_SELECT`)
+ LaunchMediaPlayer,
+ LaunchMusicPlayer,
+ LaunchPhone,
+ LaunchScreenSaver,
+ LaunchSpreadsheet,
+ LaunchWebBrowser,
+ LaunchWebCam,
+ LaunchWordProcessor,
+ /// Navigate to previous content or page in current history. (`APPCOMMAND_BROWSER_BACKWARD`)
+ BrowserBack,
+ /// Open the list of browser favorites. (`APPCOMMAND_BROWSER_FAVORITES`)
+ BrowserFavorites,
+ /// Navigate to next content or page in current history. (`APPCOMMAND_BROWSER_FORWARD`)
+ BrowserForward,
+ /// Go to the user’s preferred home page. (`APPCOMMAND_BROWSER_HOME`)
+ BrowserHome,
+ /// Refresh the current page or content. (`APPCOMMAND_BROWSER_REFRESH`)
+ BrowserRefresh,
+ /// Call up the user’s preferred search page. (`APPCOMMAND_BROWSER_SEARCH`)
+ BrowserSearch,
+ /// Stop loading the current page or content. (`APPCOMMAND_BROWSER_STOP`)
+ BrowserStop,
+ /// The Application switch key, which provides a list of recent apps to switch between.
+ /// (`KEYCODE_APP_SWITCH`)
+ AppSwitch,
+ /// The Call key. (`KEYCODE_CALL`)
+ Call,
+ /// The Camera key. (`KEYCODE_CAMERA`)
+ Camera,
+ /// The Camera focus key. (`KEYCODE_FOCUS`)
+ CameraFocus,
+ /// The End Call key. (`KEYCODE_ENDCALL`)
+ EndCall,
+ /// The Back key. (`KEYCODE_BACK`)
+ GoBack,
+ /// The Home key, which goes to the phone’s main screen. (`KEYCODE_HOME`)
+ GoHome,
+ /// The Headset Hook key. (`KEYCODE_HEADSETHOOK`)
+ HeadsetHook,
+ LastNumberRedial,
+ /// The Notification key. (`KEYCODE_NOTIFICATION`)
+ Notification,
+ /// Toggle between manner mode state: silent, vibrate, ring, ... (`KEYCODE_MANNER_MODE`)
+ MannerMode,
+ VoiceDial,
+ /// Switch to viewing TV. (`KEYCODE_TV`)
+ TV,
+ /// TV 3D Mode. (`KEYCODE_3D_MODE`)
+ TV3DMode,
+ /// Toggle between antenna and cable input. (`KEYCODE_TV_ANTENNA_CABLE`)
+ TVAntennaCable,
+ /// Audio description. (`KEYCODE_TV_AUDIO_DESCRIPTION`)
+ TVAudioDescription,
+ /// Audio description mixing volume down. (`KEYCODE_TV_AUDIO_DESCRIPTION_MIX_DOWN`)
+ TVAudioDescriptionMixDown,
+ /// Audio description mixing volume up. (`KEYCODE_TV_AUDIO_DESCRIPTION_MIX_UP`)
+ TVAudioDescriptionMixUp,
+ /// Contents menu. (`KEYCODE_TV_CONTENTS_MENU`)
+ TVContentsMenu,
+ /// Contents menu. (`KEYCODE_TV_DATA_SERVICE`)
+ TVDataService,
+ /// Switch the input mode on an external TV. (`KEYCODE_TV_INPUT`)
+ TVInput,
+ /// Switch to component input #1. (`KEYCODE_TV_INPUT_COMPONENT_1`)
+ TVInputComponent1,
+ /// Switch to component input #2. (`KEYCODE_TV_INPUT_COMPONENT_2`)
+ TVInputComponent2,
+ /// Switch to composite input #1. (`KEYCODE_TV_INPUT_COMPOSITE_1`)
+ TVInputComposite1,
+ /// Switch to composite input #2. (`KEYCODE_TV_INPUT_COMPOSITE_2`)
+ TVInputComposite2,
+ /// Switch to HDMI input #1. (`KEYCODE_TV_INPUT_HDMI_1`)
+ TVInputHDMI1,
+ /// Switch to HDMI input #2. (`KEYCODE_TV_INPUT_HDMI_2`)
+ TVInputHDMI2,
+ /// Switch to HDMI input #3. (`KEYCODE_TV_INPUT_HDMI_3`)
+ TVInputHDMI3,
+ /// Switch to HDMI input #4. (`KEYCODE_TV_INPUT_HDMI_4`)
+ TVInputHDMI4,
+ /// Switch to VGA input #1. (`KEYCODE_TV_INPUT_VGA_1`)
+ TVInputVGA1,
+ /// Media context menu. (`KEYCODE_TV_MEDIA_CONTEXT_MENU`)
+ TVMediaContext,
+ /// Toggle network. (`KEYCODE_TV_NETWORK`)
+ TVNetwork,
+ /// Number entry. (`KEYCODE_TV_NUMBER_ENTRY`)
+ TVNumberEntry,
+ /// Toggle the power on an external TV. (`KEYCODE_TV_POWER`)
+ TVPower,
+ /// Radio. (`KEYCODE_TV_RADIO_SERVICE`)
+ TVRadioService,
+ /// Satellite. (`KEYCODE_TV_SATELLITE`)
+ TVSatellite,
+ /// Broadcast Satellite. (`KEYCODE_TV_SATELLITE_BS`)
+ TVSatelliteBS,
+ /// Communication Satellite. (`KEYCODE_TV_SATELLITE_CS`)
+ TVSatelliteCS,
+ /// Toggle between available satellites. (`KEYCODE_TV_SATELLITE_SERVICE`)
+ TVSatelliteToggle,
+ /// Analog Terrestrial. (`KEYCODE_TV_TERRESTRIAL_ANALOG`)
+ TVTerrestrialAnalog,
+ /// Digital Terrestrial. (`KEYCODE_TV_TERRESTRIAL_DIGITAL`)
+ TVTerrestrialDigital,
+ /// Timer programming. (`KEYCODE_TV_TIMER_PROGRAMMING`)
+ TVTimer,
+ /// Switch the input mode on an external AVR (audio/video receiver). (`KEYCODE_AVR_INPUT`)
+ AVRInput,
+ /// Toggle the power on an external AVR (audio/video receiver). (`KEYCODE_AVR_POWER`)
+ AVRPower,
+ /// General purpose color-coded media function key, as index 0 (red). (`VK_COLORED_KEY_0`,
+ /// `KEYCODE_PROG_RED`)
+ ColorF0Red,
+ /// General purpose color-coded media function key, as index 1 (green). (`VK_COLORED_KEY_1`,
+ /// `KEYCODE_PROG_GREEN`)
+ ColorF1Green,
+ /// General purpose color-coded media function key, as index 2 (yellow). (`VK_COLORED_KEY_2`,
+ /// `KEYCODE_PROG_YELLOW`)
+ ColorF2Yellow,
+ /// General purpose color-coded media function key, as index 3 (blue). (`VK_COLORED_KEY_3`,
+ /// `KEYCODE_PROG_BLUE`)
+ ColorF3Blue,
+ /// General purpose color-coded media function key, as index 4 (grey). (`VK_COLORED_KEY_4`)
+ ColorF4Grey,
+ /// General purpose color-coded media function key, as index 5 (brown). (`VK_COLORED_KEY_5`)
+ ColorF5Brown,
+ /// Toggle the display of Closed Captions. (`VK_CC`, `KEYCODE_CAPTIONS`)
+ ClosedCaptionToggle,
+ /// Adjust brightness of device, by toggling between or cycling through states. (`VK_DIMMER`)
+ Dimmer,
+ /// Swap video sources. (`VK_DISPLAY_SWAP`)
+ DisplaySwap,
+ /// Select Digital Video Rrecorder. (`KEYCODE_DVR`)
+ DVR,
+ /// Exit the current application. (`VK_EXIT`)
+ Exit,
+ /// Clear program or content stored as favorite 0. (`VK_CLEAR_FAVORITE_0`)
+ FavoriteClear0,
+ /// Clear program or content stored as favorite 1. (`VK_CLEAR_FAVORITE_1`)
+ FavoriteClear1,
+ /// Clear program or content stored as favorite 2. (`VK_CLEAR_FAVORITE_2`)
+ FavoriteClear2,
+ /// Clear program or content stored as favorite 3. (`VK_CLEAR_FAVORITE_3`)
+ FavoriteClear3,
+ /// Select (recall) program or content stored as favorite 0. (`VK_RECALL_FAVORITE_0`)
+ FavoriteRecall0,
+ /// Select (recall) program or content stored as favorite 1. (`VK_RECALL_FAVORITE_1`)
+ FavoriteRecall1,
+ /// Select (recall) program or content stored as favorite 2. (`VK_RECALL_FAVORITE_2`)
+ FavoriteRecall2,
+ /// Select (recall) program or content stored as favorite 3. (`VK_RECALL_FAVORITE_3`)
+ FavoriteRecall3,
+ /// Store current program or content as favorite 0. (`VK_STORE_FAVORITE_0`)
+ FavoriteStore0,
+ /// Store current program or content as favorite 1. (`VK_STORE_FAVORITE_1`)
+ FavoriteStore1,
+ /// Store current program or content as favorite 2. (`VK_STORE_FAVORITE_2`)
+ FavoriteStore2,
+ /// Store current program or content as favorite 3. (`VK_STORE_FAVORITE_3`)
+ FavoriteStore3,
+ /// Toggle display of program or content guide. (`VK_GUIDE`, `KEYCODE_GUIDE`)
+ Guide,
+ /// If guide is active and displayed, then display next day’s content. (`VK_NEXT_DAY`)
+ GuideNextDay,
+ /// If guide is active and displayed, then display previous day’s content. (`VK_PREV_DAY`)
+ GuidePreviousDay,
+ /// Toggle display of information about currently selected context or media. (`VK_INFO`,
+ /// `KEYCODE_INFO`)
+ Info,
+ /// Toggle instant replay. (`VK_INSTANT_REPLAY`)
+ InstantReplay,
+ /// Launch linked content, if available and appropriate. (`VK_LINK`)
+ Link,
+ /// List the current program. (`VK_LIST`)
+ ListProgram,
+ /// Toggle display listing of currently available live content or programs. (`VK_LIVE`)
+ LiveContent,
+ /// Lock or unlock current content or program. (`VK_LOCK`)
+ Lock,
+ /// Show a list of media applications: audio/video players and image viewers. (`VK_APPS`)
+ ///
+ /// Note: Do not confuse this key value with the Windows' `VK_APPS` / `VK_CONTEXT_MENU` key,
+ /// which is encoded as `"ContextMenu"`.
+ MediaApps,
+ /// Audio track key. (`KEYCODE_MEDIA_AUDIO_TRACK`)
+ MediaAudioTrack,
+ /// Select previously selected channel or media. (`VK_LAST`, `KEYCODE_LAST_CHANNEL`)
+ MediaLast,
+ /// Skip backward to next content or program. (`KEYCODE_MEDIA_SKIP_BACKWARD`)
+ MediaSkipBackward,
+ /// Skip forward to next content or program. (`VK_SKIP`, `KEYCODE_MEDIA_SKIP_FORWARD`)
+ MediaSkipForward,
+ /// Step backward to next content or program. (`KEYCODE_MEDIA_STEP_BACKWARD`)
+ MediaStepBackward,
+ /// Step forward to next content or program. (`KEYCODE_MEDIA_STEP_FORWARD`)
+ MediaStepForward,
+ /// Media top menu. (`KEYCODE_MEDIA_TOP_MENU`)
+ MediaTopMenu,
+ /// Navigate in. (`KEYCODE_NAVIGATE_IN`)
+ NavigateIn,
+ /// Navigate to next key. (`KEYCODE_NAVIGATE_NEXT`)
+ NavigateNext,
+ /// Navigate out. (`KEYCODE_NAVIGATE_OUT`)
+ NavigateOut,
+ /// Navigate to previous key. (`KEYCODE_NAVIGATE_PREVIOUS`)
+ NavigatePrevious,
+ /// Cycle to next favorite channel (in favorites list). (`VK_NEXT_FAVORITE_CHANNEL`)
+ NextFavoriteChannel,
+ /// Cycle to next user profile (if there are multiple user profiles). (`VK_USER`)
+ NextUserProfile,
+ /// Access on-demand content or programs. (`VK_ON_DEMAND`)
+ OnDemand,
+ /// Pairing key to pair devices. (`KEYCODE_PAIRING`)
+ Pairing,
+ /// Move picture-in-picture window down. (`VK_PINP_DOWN`)
+ PinPDown,
+ /// Move picture-in-picture window. (`VK_PINP_MOVE`)
+ PinPMove,
+ /// Toggle display of picture-in-picture window. (`VK_PINP_TOGGLE`)
+ PinPToggle,
+ /// Move picture-in-picture window up. (`VK_PINP_UP`)
+ PinPUp,
+ /// Decrease media playback speed. (`VK_PLAY_SPEED_DOWN`)
+ PlaySpeedDown,
+ /// Reset playback to normal speed. (`VK_PLAY_SPEED_RESET`)
+ PlaySpeedReset,
+ /// Increase media playback speed. (`VK_PLAY_SPEED_UP`)
+ PlaySpeedUp,
+ /// Toggle random media or content shuffle mode. (`VK_RANDOM_TOGGLE`)
+ RandomToggle,
+ /// Not a physical key, but this key code is sent when the remote control battery is low.
+ /// (`VK_RC_LOW_BATTERY`)
+ RcLowBattery,
+ /// Toggle or cycle between media recording speeds. (`VK_RECORD_SPEED_NEXT`)
+ RecordSpeedNext,
+ /// Toggle RF (radio frequency) input bypass mode (pass RF input directly to the RF output).
+ /// (`VK_RF_BYPASS`)
+ RfBypass,
+ /// Toggle scan channels mode. (`VK_SCAN_CHANNELS_TOGGLE`)
+ ScanChannelsToggle,
+ /// Advance display screen mode to next available mode. (`VK_SCREEN_MODE_NEXT`)
+ ScreenModeNext,
+ /// Toggle display of device settings screen. (`VK_SETTINGS`, `KEYCODE_SETTINGS`)
+ Settings,
+ /// Toggle split screen mode. (`VK_SPLIT_SCREEN_TOGGLE`)
+ SplitScreenToggle,
+ /// Switch the input mode on an external STB (set top box). (`KEYCODE_STB_INPUT`)
+ STBInput,
+ /// Toggle the power on an external STB (set top box). (`KEYCODE_STB_POWER`)
+ STBPower,
+ /// Toggle display of subtitles, if available. (`VK_SUBTITLE`)
+ Subtitle,
+ /// Toggle display of teletext, if available (`VK_TELETEXT`, `KEYCODE_TV_TELETEXT`).
+ Teletext,
+ /// Advance video mode to next available mode. (`VK_VIDEO_MODE_NEXT`)
+ VideoModeNext,
+ /// Cause device to identify itself in some manner, e.g., audibly or visibly. (`VK_WINK`)
+ Wink,
+ /// Toggle between full-screen and scaled content, or alter magnification level. (`VK_ZOOM`,
+ /// `KEYCODE_TV_ZOOM_MODE`)
+ ZoomToggle,
+ /// General-purpose function key.
+ /// Usually found at the top of the keyboard.
+ F1,
+ /// General-purpose function key.
+ /// Usually found at the top of the keyboard.
+ F2,
+ /// General-purpose function key.
+ /// Usually found at the top of the keyboard.
+ F3,
+ /// General-purpose function key.
+ /// Usually found at the top of the keyboard.
+ F4,
+ /// General-purpose function key.
+ /// Usually found at the top of the keyboard.
+ F5,
+ /// General-purpose function key.
+ /// Usually found at the top of the keyboard.
+ F6,
+ /// General-purpose function key.
+ /// Usually found at the top of the keyboard.
+ F7,
+ /// General-purpose function key.
+ /// Usually found at the top of the keyboard.
+ F8,
+ /// General-purpose function key.
+ /// Usually found at the top of the keyboard.
+ F9,
+ /// General-purpose function key.
+ /// Usually found at the top of the keyboard.
+ F10,
+ /// General-purpose function key.
+ /// Usually found at the top of the keyboard.
+ F11,
+ /// General-purpose function key.
+ /// Usually found at the top of the keyboard.
+ F12,
+ /// General-purpose function key.
+ /// Usually found at the top of the keyboard.
+ F13,
+ /// General-purpose function key.
+ /// Usually found at the top of the keyboard.
+ F14,
+ /// General-purpose function key.
+ /// Usually found at the top of the keyboard.
+ F15,
+ /// General-purpose function key.
+ /// Usually found at the top of the keyboard.
+ F16,
+ /// General-purpose function key.
+ /// Usually found at the top of the keyboard.
+ F17,
+ /// General-purpose function key.
+ /// Usually found at the top of the keyboard.
+ F18,
+ /// General-purpose function key.
+ /// Usually found at the top of the keyboard.
+ F19,
+ /// General-purpose function key.
+ /// Usually found at the top of the keyboard.
+ F20,
+ /// General-purpose function key.
+ /// Usually found at the top of the keyboard.
+ F21,
+ /// General-purpose function key.
+ /// Usually found at the top of the keyboard.
+ F22,
+ /// General-purpose function key.
+ /// Usually found at the top of the keyboard.
+ F23,
+ /// General-purpose function key.
+ /// Usually found at the top of the keyboard.
+ F24,
+ /// General-purpose function key.
+ F25,
+ /// General-purpose function key.
+ F26,
+ /// General-purpose function key.
+ F27,
+ /// General-purpose function key.
+ F28,
+ /// General-purpose function key.
+ F29,
+ /// General-purpose function key.
+ F30,
+ /// General-purpose function key.
+ F31,
+ /// General-purpose function key.
+ F32,
+ /// General-purpose function key.
+ F33,
+ /// General-purpose function key.
+ F34,
+ /// General-purpose function key.
+ F35,
+}
diff --git a/core/src/keyboard/key_code.rs b/core/src/keyboard/key_code.rs
deleted file mode 100644
index 74ead170..00000000
--- a/core/src/keyboard/key_code.rs
+++ /dev/null
@@ -1,203 +0,0 @@
-/// The symbolic name of a keyboard key.
-///
-/// This is mostly the `KeyCode` type found in [`winit`].
-///
-/// [`winit`]: https://docs.rs/winit/0.20.0-alpha3/winit/
-#[derive(Debug, Hash, Ord, PartialOrd, PartialEq, Eq, Clone, Copy)]
-#[repr(u32)]
-#[allow(missing_docs)]
-pub enum KeyCode {
- /// The '1' key over the letters.
- Key1,
- /// The '2' key over the letters.
- Key2,
- /// The '3' key over the letters.
- Key3,
- /// The '4' key over the letters.
- Key4,
- /// The '5' key over the letters.
- Key5,
- /// The '6' key over the letters.
- Key6,
- /// The '7' key over the letters.
- Key7,
- /// The '8' key over the letters.
- Key8,
- /// The '9' key over the letters.
- Key9,
- /// The '0' key over the 'O' and 'P' keys.
- Key0,
-
- A,
- B,
- C,
- D,
- E,
- F,
- G,
- H,
- I,
- J,
- K,
- L,
- M,
- N,
- O,
- P,
- Q,
- R,
- S,
- T,
- U,
- V,
- W,
- X,
- Y,
- Z,
-
- /// The Escape key, next to F1.
- Escape,
-
- F1,
- F2,
- F3,
- F4,
- F5,
- F6,
- F7,
- F8,
- F9,
- F10,
- F11,
- F12,
- F13,
- F14,
- F15,
- F16,
- F17,
- F18,
- F19,
- F20,
- F21,
- F22,
- F23,
- F24,
-
- /// Print Screen/SysRq.
- Snapshot,
- /// Scroll Lock.
- Scroll,
- /// Pause/Break key, next to Scroll lock.
- Pause,
-
- /// `Insert`, next to Backspace.
- Insert,
- Home,
- Delete,
- End,
- PageDown,
- PageUp,
-
- Left,
- Up,
- Right,
- Down,
-
- /// The Backspace key, right over Enter.
- Backspace,
- /// The Enter key.
- Enter,
- /// The space bar.
- Space,
-
- /// The "Compose" key on Linux.
- Compose,
-
- Caret,
-
- Numlock,
- Numpad0,
- Numpad1,
- Numpad2,
- Numpad3,
- Numpad4,
- Numpad5,
- Numpad6,
- Numpad7,
- Numpad8,
- Numpad9,
- NumpadAdd,
- NumpadDivide,
- NumpadDecimal,
- NumpadComma,
- NumpadEnter,
- NumpadEquals,
- NumpadMultiply,
- NumpadSubtract,
-
- AbntC1,
- AbntC2,
- Apostrophe,
- Apps,
- Asterisk,
- At,
- Ax,
- Backslash,
- Calculator,
- Capital,
- Colon,
- Comma,
- Convert,
- Equals,
- Grave,
- Kana,
- Kanji,
- LAlt,
- LBracket,
- LControl,
- LShift,
- LWin,
- Mail,
- MediaSelect,
- MediaStop,
- Minus,
- Mute,
- MyComputer,
- NavigateForward, // also called "Next"
- NavigateBackward, // also called "Prior"
- NextTrack,
- NoConvert,
- OEM102,
- Period,
- PlayPause,
- Plus,
- Power,
- PrevTrack,
- RAlt,
- RBracket,
- RControl,
- RShift,
- RWin,
- Semicolon,
- Slash,
- Sleep,
- Stop,
- Sysrq,
- Tab,
- Underline,
- Unlabeled,
- VolumeDown,
- VolumeUp,
- Wake,
- WebBack,
- WebFavorites,
- WebForward,
- WebHome,
- WebRefresh,
- WebSearch,
- WebStop,
- Yen,
- Copy,
- Paste,
- Cut,
-}
diff --git a/core/src/keyboard/location.rs b/core/src/keyboard/location.rs
new file mode 100644
index 00000000..feff0820
--- /dev/null
+++ b/core/src/keyboard/location.rs
@@ -0,0 +1,12 @@
+/// The location of a key on the keyboard.
+#[derive(Debug, Clone, Copy, PartialEq, Eq)]
+pub enum Location {
+ /// The standard group of keys on the keyboard.
+ Standard,
+ /// The left side of the keyboard.
+ Left,
+ /// The right side of the keyboard.
+ Right,
+ /// The numpad of the keyboard.
+ Numpad,
+}
diff --git a/core/src/layout.rs b/core/src/layout.rs
index caf315b6..95720aba 100644
--- a/core/src/layout.rs
+++ b/core/src/layout.rs
@@ -7,7 +7,7 @@ pub mod flex;
pub use limits::Limits;
pub use node::Node;
-use crate::{Point, Rectangle, Size, Vector};
+use crate::{Length, Padding, Point, Rectangle, Size, Vector};
/// The bounds of a [`Node`] and its children, using absolute coordinates.
#[derive(Debug, Clone, Copy)]
@@ -71,12 +71,12 @@ pub fn next_to_each_other(
left: impl FnOnce(&Limits) -> Node,
right: impl FnOnce(&Limits) -> Node,
) -> Node {
- let mut left_node = left(limits);
+ let left_node = left(limits);
let left_size = left_node.size();
let right_limits = limits.shrink(Size::new(left_size.width + spacing, 0.0));
- let mut right_node = right(&right_limits);
+ let right_node = right(&right_limits);
let right_size = right_node.size();
let (left_y, right_y) = if left_size.height > right_size.height {
@@ -85,14 +85,106 @@ pub fn next_to_each_other(
((right_size.height - left_size.height) / 2.0, 0.0)
};
- left_node.move_to(Point::new(0.0, left_y));
- right_node.move_to(Point::new(left_size.width + spacing, right_y));
-
Node::with_children(
Size::new(
left_size.width + spacing + right_size.width,
left_size.height.max(right_size.height),
),
- vec![left_node, right_node],
+ vec![
+ left_node.move_to(Point::new(0.0, left_y)),
+ right_node.move_to(Point::new(left_size.width + spacing, right_y)),
+ ],
+ )
+}
+
+/// Computes the resulting [`Node`] that fits the [`Limits`] given
+/// some width and height requirements and no intrinsic size.
+pub fn atomic(
+ limits: &Limits,
+ width: impl Into<Length>,
+ height: impl Into<Length>,
+) -> Node {
+ let width = width.into();
+ let height = height.into();
+
+ Node::new(limits.resolve(width, height, Size::ZERO))
+}
+
+/// Computes the resulting [`Node`] that fits the [`Limits`] given
+/// some width and height requirements and a closure that produces
+/// the intrinsic [`Size`] inside the given [`Limits`].
+pub fn sized(
+ limits: &Limits,
+ width: impl Into<Length>,
+ height: impl Into<Length>,
+ f: impl FnOnce(&Limits) -> Size,
+) -> Node {
+ let width = width.into();
+ let height = height.into();
+
+ let limits = limits.width(width).height(height);
+ let intrinsic_size = f(&limits);
+
+ Node::new(limits.resolve(width, height, intrinsic_size))
+}
+
+/// Computes the resulting [`Node`] that fits the [`Limits`] given
+/// some width and height requirements and a closure that produces
+/// the content [`Node`] inside the given [`Limits`].
+pub fn contained(
+ limits: &Limits,
+ width: impl Into<Length>,
+ height: impl Into<Length>,
+ f: impl FnOnce(&Limits) -> Node,
+) -> Node {
+ let width = width.into();
+ let height = height.into();
+
+ let limits = limits.width(width).height(height);
+ let content = f(&limits);
+
+ Node::with_children(
+ limits.resolve(width, height, content.size()),
+ vec![content],
+ )
+}
+
+/// Computes the [`Node`] that fits the [`Limits`] given some width, height, and
+/// [`Padding`] requirements and a closure that produces the content [`Node`]
+/// inside the given [`Limits`].
+pub fn padded(
+ limits: &Limits,
+ width: impl Into<Length>,
+ height: impl Into<Length>,
+ padding: impl Into<Padding>,
+ layout: impl FnOnce(&Limits) -> Node,
+) -> Node {
+ positioned(limits, width, height, padding, layout, |content, _| content)
+}
+
+/// Computes a [`padded`] [`Node`] with a positioning step.
+pub fn positioned(
+ limits: &Limits,
+ width: impl Into<Length>,
+ height: impl Into<Length>,
+ padding: impl Into<Padding>,
+ layout: impl FnOnce(&Limits) -> Node,
+ position: impl FnOnce(Node, Size) -> Node,
+) -> Node {
+ let width = width.into();
+ let height = height.into();
+ let padding = padding.into();
+
+ let limits = limits.width(width).height(height);
+ let content = layout(&limits.shrink(padding));
+ let padding = padding.fit(content.size(), limits.max());
+
+ let size = limits
+ .shrink(padding)
+ .resolve(width, height, content.size());
+
+ Node::with_children(
+ size.expand(padding),
+ vec![position(content.move_to((padding.left, padding.top)), size)],
)
}
diff --git a/core/src/layout/flex.rs b/core/src/layout/flex.rs
index c02b63d8..3358ef3d 100644
--- a/core/src/layout/flex.rs
+++ b/core/src/layout/flex.rs
@@ -20,7 +20,7 @@ use crate::Element;
use crate::layout::{Limits, Node};
use crate::widget;
-use crate::{Alignment, Padding, Point, Size};
+use crate::{Alignment, Length, Padding, Point, Size};
/// The main axis of a flex layout.
#[derive(Debug)]
@@ -47,7 +47,7 @@ impl Axis {
}
}
- fn pack(&self, main: f32, cross: f32) -> (f32, f32) {
+ fn pack<T>(&self, main: T, cross: T) -> (T, T) {
match self {
Axis::Horizontal => (main, cross),
Axis::Vertical => (cross, main),
@@ -63,6 +63,8 @@ pub fn resolve<Message, Renderer>(
axis: Axis,
renderer: &Renderer,
limits: &Limits,
+ width: Length,
+ height: Length,
padding: Padding,
spacing: f32,
align_items: Alignment,
@@ -72,26 +74,64 @@ pub fn resolve<Message, Renderer>(
where
Renderer: crate::Renderer,
{
- let limits = limits.pad(padding);
+ let limits = limits.width(width).height(height).shrink(padding);
let total_spacing = spacing * items.len().saturating_sub(1) as f32;
let max_cross = axis.cross(limits.max());
- let mut fill_sum = 0;
- let mut cross = axis.cross(limits.min()).max(axis.cross(limits.fill()));
+ let mut fill_main_sum = 0;
+ let mut cross = match axis {
+ Axis::Horizontal => match height {
+ Length::Shrink => 0.0,
+ _ => max_cross,
+ },
+ Axis::Vertical => match width {
+ Length::Shrink => 0.0,
+ _ => max_cross,
+ },
+ };
+
let mut available = axis.main(limits.max()) - total_spacing;
let mut nodes: Vec<Node> = Vec::with_capacity(items.len());
nodes.resize(items.len(), Node::default());
for (i, (child, tree)) in items.iter().zip(trees.iter_mut()).enumerate() {
- let fill_factor = match axis {
- Axis::Horizontal => child.as_widget().width(),
- Axis::Vertical => child.as_widget().height(),
+ let (fill_main_factor, fill_cross_factor) = {
+ let size = child.as_widget().size();
+
+ axis.pack(size.width.fill_factor(), size.height.fill_factor())
+ };
+
+ if fill_main_factor == 0 {
+ if fill_cross_factor == 0 {
+ let (max_width, max_height) = axis.pack(available, max_cross);
+
+ let child_limits =
+ Limits::new(Size::ZERO, Size::new(max_width, max_height));
+
+ let layout =
+ child.as_widget().layout(tree, renderer, &child_limits);
+ let size = layout.size();
+
+ available -= axis.main(size);
+ cross = cross.max(axis.cross(size));
+
+ nodes[i] = layout;
+ }
+ } else {
+ fill_main_sum += fill_main_factor;
}
- .fill_factor();
+ }
- if fill_factor == 0 {
- let (max_width, max_height) = axis.pack(available, max_cross);
+ for (i, (child, tree)) in items.iter().zip(trees.iter_mut()).enumerate() {
+ let (fill_main_factor, fill_cross_factor) = {
+ let size = child.as_widget().size();
+
+ axis.pack(size.width.fill_factor(), size.height.fill_factor())
+ };
+
+ if fill_main_factor == 0 && fill_cross_factor != 0 {
+ let (max_width, max_height) = axis.pack(available, cross);
let child_limits =
Limits::new(Size::ZERO, Size::new(max_width, max_height));
@@ -101,34 +141,47 @@ where
let size = layout.size();
available -= axis.main(size);
- cross = cross.max(axis.cross(size));
+ cross = cross.max(axis.cross(layout.size()));
nodes[i] = layout;
- } else {
- fill_sum += fill_factor;
}
}
- let remaining = available.max(0.0);
+ let remaining = match axis {
+ Axis::Horizontal => match width {
+ Length::Shrink => 0.0,
+ _ => available.max(0.0),
+ },
+ Axis::Vertical => match height {
+ Length::Shrink => 0.0,
+ _ => available.max(0.0),
+ },
+ };
for (i, (child, tree)) in items.iter().zip(trees).enumerate() {
- let fill_factor = match axis {
- Axis::Horizontal => child.as_widget().width(),
- Axis::Vertical => child.as_widget().height(),
- }
- .fill_factor();
+ let (fill_main_factor, fill_cross_factor) = {
+ let size = child.as_widget().size();
+
+ axis.pack(size.width.fill_factor(), size.height.fill_factor())
+ };
+
+ if fill_main_factor != 0 {
+ let max_main =
+ remaining * fill_main_factor as f32 / fill_main_sum as f32;
- if fill_factor != 0 {
- let max_main = remaining * fill_factor as f32 / fill_sum as f32;
let min_main = if max_main.is_infinite() {
0.0
} else {
max_main
};
- let (min_width, min_height) =
- axis.pack(min_main, axis.cross(limits.min()));
+ let max_cross = if fill_cross_factor == 0 {
+ max_cross
+ } else {
+ cross
+ };
+ let (min_width, min_height) = axis.pack(min_main, 0.0);
let (max_width, max_height) = axis.pack(max_main, max_cross);
let child_limits = Limits::new(
@@ -154,18 +207,18 @@ where
let (x, y) = axis.pack(main, pad.1);
- node.move_to(Point::new(x, y));
+ node.move_to_mut(Point::new(x, y));
match axis {
Axis::Horizontal => {
- node.align(
+ node.align_mut(
Alignment::Start,
align_items,
Size::new(0.0, cross),
);
}
Axis::Vertical => {
- node.align(
+ node.align_mut(
align_items,
Alignment::Start,
Size::new(cross, 0.0),
@@ -178,8 +231,12 @@ where
main += axis.main(size);
}
- let (width, height) = axis.pack(main - pad.0, cross);
- let size = limits.resolve(Size::new(width, height));
+ let (intrinsic_width, intrinsic_height) = axis.pack(main - pad.0, cross);
+ let size = limits.resolve(
+ width,
+ height,
+ Size::new(intrinsic_width, intrinsic_height),
+ );
- Node::with_children(size.pad(padding), nodes)
+ Node::with_children(size.expand(padding), nodes)
}
diff --git a/core/src/layout/limits.rs b/core/src/layout/limits.rs
index 39a3d98b..7fbc7b9d 100644
--- a/core/src/layout/limits.rs
+++ b/core/src/layout/limits.rs
@@ -1,12 +1,11 @@
#![allow(clippy::manual_clamp)]
-use crate::{Length, Padding, Size};
+use crate::{Length, Size};
/// A set of size constraints for layouting.
#[derive(Debug, Clone, Copy, PartialEq)]
pub struct Limits {
min: Size,
max: Size,
- fill: Size,
}
impl Limits {
@@ -14,16 +13,11 @@ impl Limits {
pub const NONE: Limits = Limits {
min: Size::ZERO,
max: Size::INFINITY,
- fill: Size::INFINITY,
};
/// Creates new [`Limits`] with the given minimum and maximum [`Size`].
pub const fn new(min: Size, max: Size) -> Limits {
- Limits {
- min,
- max,
- fill: Size::INFINITY,
- }
+ Limits { min, max }
}
/// Returns the minimum [`Size`] of the [`Limits`].
@@ -36,26 +30,15 @@ impl Limits {
self.max
}
- /// Returns the fill [`Size`] of the [`Limits`].
- pub fn fill(&self) -> Size {
- self.fill
- }
-
/// Applies a width constraint to the current [`Limits`].
pub fn width(mut self, width: impl Into<Length>) -> Limits {
match width.into() {
- Length::Shrink => {
- self.fill.width = self.min.width;
- }
- Length::Fill | Length::FillPortion(_) => {
- self.fill.width = self.fill.width.min(self.max.width);
- }
+ Length::Shrink | Length::Fill | Length::FillPortion(_) => {}
Length::Fixed(amount) => {
let new_width = amount.min(self.max.width).max(self.min.width);
self.min.width = new_width;
self.max.width = new_width;
- self.fill.width = new_width;
}
}
@@ -65,19 +48,13 @@ impl Limits {
/// Applies a height constraint to the current [`Limits`].
pub fn height(mut self, height: impl Into<Length>) -> Limits {
match height.into() {
- Length::Shrink => {
- self.fill.height = self.min.height;
- }
- Length::Fill | Length::FillPortion(_) => {
- self.fill.height = self.fill.height.min(self.max.height);
- }
+ Length::Shrink | Length::Fill | Length::FillPortion(_) => {}
Length::Fixed(amount) => {
let new_height =
amount.min(self.max.height).max(self.min.height);
self.min.height = new_height;
self.max.height = new_height;
- self.fill.height = new_height;
}
}
@@ -112,13 +89,10 @@ impl Limits {
self
}
- /// Shrinks the current [`Limits`] to account for the given padding.
- pub fn pad(&self, padding: Padding) -> Limits {
- self.shrink(Size::new(padding.horizontal(), padding.vertical()))
- }
-
/// Shrinks the current [`Limits`] by the given [`Size`].
- pub fn shrink(&self, size: Size) -> Limits {
+ pub fn shrink(&self, size: impl Into<Size>) -> Limits {
+ let size = size.into();
+
let min = Size::new(
(self.min().width - size.width).max(0.0),
(self.min().height - size.height).max(0.0),
@@ -129,12 +103,7 @@ impl Limits {
(self.max().height - size.height).max(0.0),
);
- let fill = Size::new(
- (self.fill.width - size.width).max(0.0),
- (self.fill.height - size.height).max(0.0),
- );
-
- Limits { min, max, fill }
+ Limits { min, max }
}
/// Removes the minimum width constraint for the current [`Limits`].
@@ -142,22 +111,39 @@ impl Limits {
Limits {
min: Size::ZERO,
max: self.max,
- fill: self.fill,
}
}
- /// Computes the resulting [`Size`] that fits the [`Limits`] given the
- /// intrinsic size of some content.
- pub fn resolve(&self, intrinsic_size: Size) -> Size {
- Size::new(
- intrinsic_size
- .width
- .min(self.max.width)
- .max(self.fill.width),
- intrinsic_size
+ /// Computes the resulting [`Size`] that fits the [`Limits`] given
+ /// some width and height requirements and the intrinsic size of
+ /// some content.
+ pub fn resolve(
+ &self,
+ width: impl Into<Length>,
+ height: impl Into<Length>,
+ intrinsic_size: Size,
+ ) -> Size {
+ let width = match width.into() {
+ Length::Fill | Length::FillPortion(_) => self.max.width,
+ Length::Fixed(amount) => {
+ amount.min(self.max.width).max(self.min.width)
+ }
+ Length::Shrink => {
+ intrinsic_size.width.min(self.max.width).max(self.min.width)
+ }
+ };
+
+ let height = match height.into() {
+ Length::Fill | Length::FillPortion(_) => self.max.height,
+ Length::Fixed(amount) => {
+ amount.min(self.max.height).max(self.min.height)
+ }
+ Length::Shrink => intrinsic_size
.height
.min(self.max.height)
- .max(self.fill.height),
- )
+ .max(self.min.height),
+ };
+
+ Size::new(width, height)
}
}
diff --git a/core/src/layout/node.rs b/core/src/layout/node.rs
index 2b44a7d5..5743a9bd 100644
--- a/core/src/layout/node.rs
+++ b/core/src/layout/node.rs
@@ -1,4 +1,4 @@
-use crate::{Alignment, Point, Rectangle, Size, Vector};
+use crate::{Alignment, Padding, Point, Rectangle, Size, Vector};
/// The bounds of an element and its children.
#[derive(Debug, Clone, Default)]
@@ -26,6 +26,14 @@ impl Node {
}
}
+ /// Creates a new [`Node`] that wraps a single child with some [`Padding`].
+ pub fn container(child: Self, padding: Padding) -> Self {
+ Self::with_children(
+ child.bounds.size().expand(padding),
+ vec![child.move_to(Point::new(padding.left, padding.top))],
+ )
+ }
+
/// Returns the [`Size`] of the [`Node`].
pub fn size(&self) -> Size {
Size::new(self.bounds.width, self.bounds.height)
@@ -43,6 +51,17 @@ impl Node {
/// Aligns the [`Node`] in the given space.
pub fn align(
+ mut self,
+ horizontal_alignment: Alignment,
+ vertical_alignment: Alignment,
+ space: Size,
+ ) -> Self {
+ self.align_mut(horizontal_alignment, vertical_alignment, space);
+ self
+ }
+
+ /// Mutable reference version of [`Self::align`].
+ pub fn align_mut(
&mut self,
horizontal_alignment: Alignment,
vertical_alignment: Alignment,
@@ -70,13 +89,23 @@ impl Node {
}
/// Moves the [`Node`] to the given position.
- pub fn move_to(&mut self, position: Point) {
+ pub fn move_to(mut self, position: impl Into<Point>) -> Self {
+ self.move_to_mut(position);
+ self
+ }
+
+ /// Mutable reference version of [`Self::move_to`].
+ pub fn move_to_mut(&mut self, position: impl Into<Point>) {
+ let position = position.into();
+
self.bounds.x = position.x;
self.bounds.y = position.y;
}
/// Translates the [`Node`] by the given translation.
- pub fn translate(self, translation: Vector) -> Self {
+ pub fn translate(self, translation: impl Into<Vector>) -> Self {
+ let translation = translation.into();
+
Self {
bounds: self.bounds + translation,
..self
diff --git a/core/src/length.rs b/core/src/length.rs
index 3adb996e..4c139895 100644
--- a/core/src/length.rs
+++ b/core/src/length.rs
@@ -36,6 +36,24 @@ impl Length {
Length::Fixed(_) => 0,
}
}
+
+ /// Returns `true` iff the [`Length`] is either [`Length::Fill`] or
+ // [`Length::FillPortion`].
+ pub fn is_fill(&self) -> bool {
+ self.fill_factor() != 0
+ }
+
+ /// Returns the "fluid" variant of the [`Length`].
+ ///
+ /// Specifically:
+ /// - [`Length::Shrink`] if [`Length::Shrink`] or [`Length::Fixed`].
+ /// - [`Length::Fill`] otherwise.
+ pub fn fluid(&self) -> Length {
+ match self {
+ Length::Fill | Length::FillPortion(_) => Length::Fill,
+ Length::Shrink | Length::Fixed(_) => Length::Shrink,
+ }
+ }
}
impl From<Pixels> for Length {
diff --git a/core/src/lib.rs b/core/src/lib.rs
index 54ea5839..864df6e6 100644
--- a/core/src/lib.rs
+++ b/core/src/lib.rs
@@ -75,3 +75,5 @@ pub use size::Size;
pub use text::Text;
pub use vector::Vector;
pub use widget::Widget;
+
+pub use smol_str::SmolStr;
diff --git a/core/src/mouse/button.rs b/core/src/mouse/button.rs
index 3eec7f42..a8f90329 100644
--- a/core/src/mouse/button.rs
+++ b/core/src/mouse/button.rs
@@ -10,6 +10,12 @@ pub enum Button {
/// The middle (wheel) button.
Middle,
+ /// The back mouse button.
+ Back,
+
+ /// The forward mouse button.
+ Forward,
+
/// Some other button.
Other(u16),
}
diff --git a/core/src/padding.rs b/core/src/padding.rs
index 0b1bba13..a63f6e29 100644
--- a/core/src/padding.rs
+++ b/core/src/padding.rs
@@ -154,3 +154,9 @@ impl From<[f32; 4]> for Padding {
}
}
}
+
+impl From<Padding> for Size {
+ fn from(padding: Padding) -> Self {
+ Self::new(padding.horizontal(), padding.vertical())
+ }
+}
diff --git a/core/src/point.rs b/core/src/point.rs
index ef42852f..cea57518 100644
--- a/core/src/point.rs
+++ b/core/src/point.rs
@@ -36,20 +36,26 @@ impl<T: Num> Point<T> {
}
}
-impl From<[f32; 2]> for Point {
- fn from([x, y]: [f32; 2]) -> Self {
+impl<T> From<[T; 2]> for Point<T>
+where
+ T: Num,
+{
+ fn from([x, y]: [T; 2]) -> Self {
Point { x, y }
}
}
-impl From<[u16; 2]> for Point<u16> {
- fn from([x, y]: [u16; 2]) -> Self {
- Point::new(x, y)
+impl<T> From<(T, T)> for Point<T>
+where
+ T: Num,
+{
+ fn from((x, y): (T, T)) -> Self {
+ Self { x, y }
}
}
-impl From<Point> for [f32; 2] {
- fn from(point: Point) -> [f32; 2] {
+impl<T> From<Point<T>> for [T; 2] {
+ fn from(point: Point<T>) -> [T; 2] {
[point.x, point.y]
}
}
diff --git a/core/src/size.rs b/core/src/size.rs
index 7ef2f602..90e50d13 100644
--- a/core/src/size.rs
+++ b/core/src/size.rs
@@ -1,4 +1,4 @@
-use crate::{Padding, Vector};
+use crate::Vector;
/// An amount of space in 2 dimensions.
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
@@ -26,15 +26,7 @@ impl Size {
/// A [`Size`] with infinite width and height.
pub const INFINITY: Size = Size::new(f32::INFINITY, f32::INFINITY);
- /// Increments the [`Size`] to account for the given padding.
- pub fn pad(&self, padding: Padding) -> Self {
- Size {
- width: self.width + padding.horizontal(),
- height: self.height + padding.vertical(),
- }
- }
-
- /// Returns the minimum of each component of this size and another
+ /// Returns the minimum of each component of this size and another.
pub fn min(self, other: Self) -> Self {
Size {
width: self.width.min(other.width),
@@ -42,13 +34,23 @@ impl Size {
}
}
- /// Returns the maximum of each component of this size and another
+ /// Returns the maximum of each component of this size and another.
pub fn max(self, other: Self) -> Self {
Size {
width: self.width.max(other.width),
height: self.height.max(other.height),
}
}
+
+ /// Expands this [`Size`] by the given amount.
+ pub fn expand(self, other: impl Into<Size>) -> Self {
+ let other = other.into();
+
+ Size {
+ width: self.width + other.width,
+ height: self.height + other.height,
+ }
+ }
}
impl From<[f32; 2]> for Size {
diff --git a/core/src/time.rs b/core/src/time.rs
index 9355ae6d..dcfe4e41 100644
--- a/core/src/time.rs
+++ b/core/src/time.rs
@@ -1,13 +1,4 @@
//! Keep track of time, both in native and web platforms!
-#[cfg(target_arch = "wasm32")]
-pub use instant::Instant;
-
-#[cfg(target_arch = "wasm32")]
-pub use instant::Duration;
-
-#[cfg(not(target_arch = "wasm32"))]
-pub use std::time::Instant;
-
-#[cfg(not(target_arch = "wasm32"))]
-pub use std::time::Duration;
+pub use web_time::Duration;
+pub use web_time::Instant;
diff --git a/core/src/widget.rs b/core/src/widget.rs
index 294d5984..7f5632ae 100644
--- a/core/src/widget.rs
+++ b/core/src/widget.rs
@@ -15,7 +15,7 @@ use crate::layout::{self, Layout};
use crate::mouse;
use crate::overlay;
use crate::renderer;
-use crate::{Clipboard, Length, Rectangle, Shell};
+use crate::{Clipboard, Length, Rectangle, Shell, Size};
/// A component that displays information and allows interaction.
///
@@ -43,11 +43,16 @@ pub trait Widget<Message, Renderer>
where
Renderer: crate::Renderer,
{
- /// Returns the width of the [`Widget`].
- fn width(&self) -> Length;
+ /// Returns the [`Size`] of the [`Widget`] in lengths.
+ fn size(&self) -> Size<Length>;
- /// Returns the height of the [`Widget`].
- fn height(&self) -> Length;
+ /// Returns a [`Size`] hint for laying out the [`Widget`].
+ ///
+ /// This hint may be used by some widget containers to adjust their sizing strategy
+ /// during construction.
+ fn size_hint(&self) -> Size<Length> {
+ self.size()
+ }
/// Returns the [`layout::Node`] of the [`Widget`].
///
diff --git a/core/src/widget/text.rs b/core/src/widget/text.rs
index e020b030..4cabc7ce 100644
--- a/core/src/widget/text.rs
+++ b/core/src/widget/text.rs
@@ -5,7 +5,9 @@ use crate::mouse;
use crate::renderer;
use crate::text::{self, Paragraph};
use crate::widget::tree::{self, Tree};
-use crate::{Color, Element, Layout, Length, Pixels, Point, Rectangle, Widget};
+use crate::{
+ Color, Element, Layout, Length, Pixels, Point, Rectangle, Size, Widget,
+};
use std::borrow::Cow;
@@ -134,12 +136,11 @@ where
tree::State::new(State(Renderer::Paragraph::default()))
}
- fn width(&self) -> Length {
- self.width
- }
-
- fn height(&self) -> Length {
- self.height
+ fn size(&self) -> Size<Length> {
+ Size {
+ width: self.width,
+ height: self.height,
+ }
}
fn layout(
@@ -205,28 +206,27 @@ pub fn layout<Renderer>(
where
Renderer: text::Renderer,
{
- let limits = limits.width(width).height(height);
- let bounds = limits.max();
-
- let size = size.unwrap_or_else(|| renderer.default_size());
- let font = font.unwrap_or_else(|| renderer.default_font());
-
- let State(ref mut paragraph) = state;
-
- paragraph.update(text::Text {
- content,
- bounds,
- size,
- line_height,
- font,
- horizontal_alignment,
- vertical_alignment,
- shaping,
- });
-
- let size = limits.resolve(paragraph.min_bounds());
-
- layout::Node::new(size)
+ layout::sized(limits, width, height, |limits| {
+ let bounds = limits.max();
+
+ let size = size.unwrap_or_else(|| renderer.default_size());
+ let font = font.unwrap_or_else(|| renderer.default_font());
+
+ let State(ref mut paragraph) = state;
+
+ paragraph.update(text::Text {
+ content,
+ bounds,
+ size,
+ line_height,
+ font,
+ horizontal_alignment,
+ vertical_alignment,
+ shaping,
+ });
+
+ paragraph.min_bounds()
+ })
}
/// Draws text using the same logic as the [`Text`] widget.
diff --git a/core/src/window/event.rs b/core/src/window/event.rs
index b9ee7aca..a14d127f 100644
--- a/core/src/window/event.rs
+++ b/core/src/window/event.rs
@@ -58,7 +58,7 @@ pub enum Event {
/// for each file separately.
FileHovered(PathBuf),
- /// A file has beend dropped into the window.
+ /// A file has been dropped into the window.
///
/// When the user drops multiple files at once, this event will be emitted
/// for each file separately.
diff --git a/examples/custom_quad/src/main.rs b/examples/custom_quad/src/main.rs
index 13b08250..cc9ad528 100644
--- a/examples/custom_quad/src/main.rs
+++ b/examples/custom_quad/src/main.rs
@@ -26,12 +26,11 @@ mod quad {
where
Renderer: renderer::Renderer,
{
- fn width(&self) -> Length {
- Length::Shrink
- }
-
- fn height(&self) -> Length {
- Length::Shrink
+ fn size(&self) -> Size<Length> {
+ Size {
+ width: Length::Shrink,
+ height: Length::Shrink,
+ }
}
fn layout(
diff --git a/examples/custom_widget/src/main.rs b/examples/custom_widget/src/main.rs
index 32a14cbe..7ffb4cd0 100644
--- a/examples/custom_widget/src/main.rs
+++ b/examples/custom_widget/src/main.rs
@@ -33,12 +33,11 @@ mod circle {
where
Renderer: renderer::Renderer,
{
- fn width(&self) -> Length {
- Length::Shrink
- }
-
- fn height(&self) -> Length {
- Length::Shrink
+ fn size(&self) -> Size<Length> {
+ Size {
+ width: Length::Shrink,
+ height: Length::Shrink,
+ }
}
fn layout(
diff --git a/examples/download_progress/src/main.rs b/examples/download_progress/src/main.rs
index a2fcb275..675e9e26 100644
--- a/examples/download_progress/src/main.rs
+++ b/examples/download_progress/src/main.rs
@@ -73,16 +73,15 @@ impl Application for Example {
}
fn view(&self) -> Element<Message> {
- let downloads = Column::with_children(
- self.downloads.iter().map(Download::view).collect(),
- )
- .push(
- button("Add another download")
- .on_press(Message::Add)
- .padding(10),
- )
- .spacing(20)
- .align_items(Alignment::End);
+ let downloads =
+ Column::with_children(self.downloads.iter().map(Download::view))
+ .push(
+ button("Add another download")
+ .on_press(Message::Add)
+ .padding(10),
+ )
+ .spacing(20)
+ .align_items(Alignment::End);
container(downloads)
.width(Length::Fill)
diff --git a/examples/editor/Cargo.toml b/examples/editor/Cargo.toml
index a3f6ea3b..dc885728 100644
--- a/examples/editor/Cargo.toml
+++ b/examples/editor/Cargo.toml
@@ -12,4 +12,4 @@ iced.features = ["highlighter", "tokio", "debug"]
tokio.workspace = true
tokio.features = ["fs"]
-rfd = "0.12"
+rfd = "0.13"
diff --git a/examples/editor/src/main.rs b/examples/editor/src/main.rs
index 03d1e283..bf2aaaa3 100644
--- a/examples/editor/src/main.rs
+++ b/examples/editor/src/main.rs
@@ -134,8 +134,8 @@ impl Application for Editor {
}
fn subscription(&self) -> Subscription<Message> {
- keyboard::on_key_press(|key_code, modifiers| match key_code {
- keyboard::KeyCode::S if modifiers.command() => {
+ keyboard::on_key_press(|key, modifiers| match key.as_ref() {
+ keyboard::Key::Character("s") if modifiers.command() => {
Some(Message::SaveFile)
}
_ => None,
diff --git a/examples/events/src/main.rs b/examples/events/src/main.rs
index 334b012d..fc51ac4a 100644
--- a/examples/events/src/main.rs
+++ b/examples/events/src/main.rs
@@ -82,8 +82,7 @@ impl Application for Events {
self.last
.iter()
.map(|event| text(format!("{event:?}")).size(40))
- .map(Element::from)
- .collect(),
+ .map(Element::from),
);
let toggle = checkbox(
diff --git a/examples/game_of_life/src/main.rs b/examples/game_of_life/src/main.rs
index 96840143..56f7afd5 100644
--- a/examples/game_of_life/src/main.rs
+++ b/examples/game_of_life/src/main.rs
@@ -146,7 +146,8 @@ impl Application for GameOfLife {
.view()
.map(move |message| Message::Grid(message, version)),
controls,
- ];
+ ]
+ .height(Length::Fill);
container(content)
.width(Length::Fill)
@@ -178,7 +179,6 @@ fn view_controls<'a>(
slider(1.0..=1000.0, speed as f32, Message::SpeedChanged),
text(format!("x{speed}")).size(16),
]
- .width(Length::Fill)
.align_items(Alignment::Center)
.spacing(10);
diff --git a/examples/geometry/src/main.rs b/examples/geometry/src/main.rs
index 8ab3b493..5cf9963d 100644
--- a/examples/geometry/src/main.rs
+++ b/examples/geometry/src/main.rs
@@ -16,12 +16,11 @@ mod rainbow {
}
impl<Message> Widget<Message, Renderer> for Rainbow {
- fn width(&self) -> Length {
- Length::Fill
- }
-
- fn height(&self) -> Length {
- Length::Shrink
+ fn size(&self) -> Size<Length> {
+ Size {
+ width: Length::Fill,
+ height: Length::Shrink,
+ }
}
fn layout(
@@ -30,9 +29,9 @@ mod rainbow {
_renderer: &Renderer,
limits: &layout::Limits,
) -> layout::Node {
- let size = limits.width(Length::Fill).resolve(Size::ZERO);
+ let width = limits.max().width;
- layout::Node::new(Size::new(size.width, size.width))
+ layout::Node::new(Size::new(width, width))
}
fn draw(
diff --git a/examples/integration/src/controls.rs b/examples/integration/src/controls.rs
index 4714c397..89a595c1 100644
--- a/examples/integration/src/controls.rs
+++ b/examples/integration/src/controls.rs
@@ -81,32 +81,25 @@ impl Program for Controls {
);
Row::new()
- .width(Length::Fill)
.height(Length::Fill)
.align_items(Alignment::End)
.push(
- Column::new()
- .width(Length::Fill)
- .align_items(Alignment::End)
- .push(
- Column::new()
- .padding(10)
- .spacing(10)
- .push(
- Text::new("Background color")
- .style(Color::WHITE),
- )
- .push(sliders)
- .push(
- Text::new(format!("{background_color:?}"))
- .size(14)
- .style(Color::WHITE),
- )
- .push(
- text_input("Placeholder", text)
- .on_input(Message::TextChanged),
- ),
- ),
+ Column::new().align_items(Alignment::End).push(
+ Column::new()
+ .padding(10)
+ .spacing(10)
+ .push(Text::new("Background color").style(Color::WHITE))
+ .push(sliders)
+ .push(
+ Text::new(format!("{background_color:?}"))
+ .size(14)
+ .style(Color::WHITE),
+ )
+ .push(
+ text_input("Placeholder", text)
+ .on_input(Message::TextChanged),
+ ),
+ ),
)
.into()
}
diff --git a/examples/integration/src/main.rs b/examples/integration/src/main.rs
index 276794c8..b0939d68 100644
--- a/examples/integration/src/main.rs
+++ b/examples/integration/src/main.rs
@@ -19,8 +19,9 @@ use iced_winit::winit;
use iced_winit::Clipboard;
use winit::{
- event::{Event, ModifiersState, WindowEvent},
+ event::{Event, WindowEvent},
event_loop::{ControlFlow, EventLoop},
+ keyboard::ModifiersState,
};
#[cfg(target_arch = "wasm32")]
@@ -48,7 +49,7 @@ pub fn main() -> Result<(), Box<dyn std::error::Error>> {
tracing_subscriber::fmt::init();
// Initialize winit
- let event_loop = EventLoop::new();
+ let event_loop = EventLoop::new()?;
#[cfg(target_arch = "wasm32")]
let window = winit::window::WindowBuilder::new()
@@ -160,67 +161,15 @@ pub fn main() -> Result<(), Box<dyn std::error::Error>> {
);
// Run event loop
- event_loop.run(move |event, _, control_flow| {
+ event_loop.run(move |event, window_target| {
// You should change this if you want to render continuosly
- *control_flow = ControlFlow::Wait;
+ window_target.set_control_flow(ControlFlow::Wait);
match event {
- Event::WindowEvent { event, .. } => {
- match event {
- WindowEvent::CursorMoved { position, .. } => {
- cursor_position = Some(position);
- }
- WindowEvent::ModifiersChanged(new_modifiers) => {
- modifiers = new_modifiers;
- }
- WindowEvent::Resized(_) => {
- resized = true;
- }
- WindowEvent::CloseRequested => {
- *control_flow = ControlFlow::Exit;
- }
- _ => {}
- }
-
- // Map window event to iced event
- if let Some(event) = iced_winit::conversion::window_event(
- window::Id::MAIN,
- &event,
- window.scale_factor(),
- modifiers,
- ) {
- state.queue_event(event);
- }
- }
- Event::MainEventsCleared => {
- // If there are events pending
- if !state.is_queue_empty() {
- // We update iced
- let _ = state.update(
- viewport.logical_size(),
- cursor_position
- .map(|p| {
- conversion::cursor_position(
- p,
- viewport.scale_factor(),
- )
- })
- .map(mouse::Cursor::Available)
- .unwrap_or(mouse::Cursor::Unavailable),
- &mut renderer,
- &Theme::Dark,
- &renderer::Style {
- text_color: Color::WHITE,
- },
- &mut clipboard,
- &mut debug,
- );
-
- // and request a redraw
- window.request_redraw();
- }
- }
- Event::RedrawRequested(_) => {
+ Event::WindowEvent {
+ event: WindowEvent::RedrawRequested,
+ ..
+ } => {
if resized {
let size = window.inner_size();
@@ -309,7 +258,60 @@ pub fn main() -> Result<(), Box<dyn std::error::Error>> {
},
}
}
+ Event::WindowEvent { event, .. } => {
+ match event {
+ WindowEvent::CursorMoved { position, .. } => {
+ cursor_position = Some(position);
+ }
+ WindowEvent::ModifiersChanged(new_modifiers) => {
+ modifiers = new_modifiers.state();
+ }
+ WindowEvent::Resized(_) => {
+ resized = true;
+ }
+ WindowEvent::CloseRequested => {
+ window_target.exit();
+ }
+ _ => {}
+ }
+
+ // Map window event to iced event
+ if let Some(event) = iced_winit::conversion::window_event(
+ window::Id::MAIN,
+ event,
+ window.scale_factor(),
+ modifiers,
+ ) {
+ state.queue_event(event);
+ }
+ }
_ => {}
}
- })
+
+ // If there are events pending
+ if !state.is_queue_empty() {
+ // We update iced
+ let _ = state.update(
+ viewport.logical_size(),
+ cursor_position
+ .map(|p| {
+ conversion::cursor_position(p, viewport.scale_factor())
+ })
+ .map(mouse::Cursor::Available)
+ .unwrap_or(mouse::Cursor::Unavailable),
+ &mut renderer,
+ &Theme::Dark,
+ &renderer::Style {
+ text_color: Color::WHITE,
+ },
+ &mut clipboard,
+ &mut debug,
+ );
+
+ // and request a redraw
+ window.request_redraw();
+ }
+ })?;
+
+ Ok(())
}
diff --git a/examples/layout/Cargo.toml b/examples/layout/Cargo.toml
new file mode 100644
index 00000000..855f98d0
--- /dev/null
+++ b/examples/layout/Cargo.toml
@@ -0,0 +1,9 @@
+[package]
+name = "layout"
+version = "0.1.0"
+authors = ["Héctor Ramón Jiménez <hector0193@gmail.com>"]
+edition = "2021"
+publish = false
+
+[dependencies]
+iced = { path = "../..", features = ["canvas"] }
diff --git a/examples/layout/src/main.rs b/examples/layout/src/main.rs
new file mode 100644
index 00000000..6cf0e570
--- /dev/null
+++ b/examples/layout/src/main.rs
@@ -0,0 +1,371 @@
+use iced::executor;
+use iced::keyboard;
+use iced::mouse;
+use iced::theme;
+use iced::widget::{
+ button, canvas, checkbox, column, container, horizontal_space, pick_list,
+ row, scrollable, text, vertical_rule,
+};
+use iced::{
+ color, Alignment, Application, Color, Command, Element, Font, Length,
+ Point, Rectangle, Renderer, Settings, Subscription, Theme,
+};
+
+pub fn main() -> iced::Result {
+ Layout::run(Settings::default())
+}
+
+#[derive(Debug)]
+struct Layout {
+ example: Example,
+ explain: bool,
+ theme: Theme,
+}
+
+#[derive(Debug, Clone)]
+enum Message {
+ Next,
+ Previous,
+ ExplainToggled(bool),
+ ThemeSelected(Theme),
+}
+
+impl Application for Layout {
+ type Message = Message;
+ type Theme = Theme;
+ type Executor = executor::Default;
+ type Flags = ();
+
+ fn new(_flags: Self::Flags) -> (Self, Command<Message>) {
+ (
+ Self {
+ example: Example::default(),
+ explain: false,
+ theme: Theme::Light,
+ },
+ Command::none(),
+ )
+ }
+
+ fn title(&self) -> String {
+ format!("{} - Layout - Iced", self.example.title)
+ }
+
+ fn update(&mut self, message: Self::Message) -> Command<Message> {
+ match message {
+ Message::Next => {
+ self.example = self.example.next();
+ }
+ Message::Previous => {
+ self.example = self.example.previous();
+ }
+ Message::ExplainToggled(explain) => {
+ self.explain = explain;
+ }
+ Message::ThemeSelected(theme) => {
+ self.theme = theme;
+ }
+ }
+
+ Command::none()
+ }
+
+ fn subscription(&self) -> Subscription<Message> {
+ use keyboard::key;
+
+ keyboard::on_key_release(|key, _modifiers| match key {
+ keyboard::Key::Named(key::Named::ArrowLeft) => {
+ Some(Message::Previous)
+ }
+ keyboard::Key::Named(key::Named::ArrowRight) => Some(Message::Next),
+ _ => None,
+ })
+ }
+
+ fn view(&self) -> Element<Message> {
+ let header = row![
+ text(self.example.title).size(20).font(Font::MONOSPACE),
+ horizontal_space(Length::Fill),
+ checkbox("Explain", self.explain, Message::ExplainToggled),
+ pick_list(
+ Theme::ALL,
+ Some(self.theme.clone()),
+ Message::ThemeSelected
+ ),
+ ]
+ .spacing(20)
+ .align_items(Alignment::Center);
+
+ let example = container(if self.explain {
+ self.example.view().explain(color!(0x0000ff))
+ } else {
+ self.example.view()
+ })
+ .style(|theme: &Theme| {
+ let palette = theme.extended_palette();
+
+ container::Appearance::default()
+ .with_border(palette.background.strong.color, 4.0)
+ })
+ .padding(4)
+ .width(Length::Fill)
+ .height(Length::Fill)
+ .center_x()
+ .center_y();
+
+ let controls = row([
+ (!self.example.is_first()).then_some(
+ button("← Previous")
+ .padding([5, 10])
+ .on_press(Message::Previous)
+ .into(),
+ ),
+ Some(horizontal_space(Length::Fill).into()),
+ (!self.example.is_last()).then_some(
+ button("Next →")
+ .padding([5, 10])
+ .on_press(Message::Next)
+ .into(),
+ ),
+ ]
+ .into_iter()
+ .flatten());
+
+ column![header, example, controls]
+ .spacing(10)
+ .padding(20)
+ .into()
+ }
+
+ fn theme(&self) -> Theme {
+ self.theme.clone()
+ }
+}
+
+#[derive(Debug, Clone, Copy, PartialEq, Eq)]
+struct Example {
+ title: &'static str,
+ view: fn() -> Element<'static, Message>,
+}
+
+impl Example {
+ const LIST: &'static [Self] = &[
+ Self {
+ title: "Centered",
+ view: centered,
+ },
+ Self {
+ title: "Column",
+ view: column_,
+ },
+ Self {
+ title: "Row",
+ view: row_,
+ },
+ Self {
+ title: "Space",
+ view: space,
+ },
+ Self {
+ title: "Application",
+ view: application,
+ },
+ Self {
+ title: "Nested Quotes",
+ view: nested_quotes,
+ },
+ ];
+
+ fn is_first(self) -> bool {
+ Self::LIST.first() == Some(&self)
+ }
+
+ fn is_last(self) -> bool {
+ Self::LIST.last() == Some(&self)
+ }
+
+ fn previous(self) -> Self {
+ let Some(index) =
+ Self::LIST.iter().position(|&example| example == self)
+ else {
+ return self;
+ };
+
+ Self::LIST
+ .get(index.saturating_sub(1))
+ .copied()
+ .unwrap_or(self)
+ }
+
+ fn next(self) -> Self {
+ let Some(index) =
+ Self::LIST.iter().position(|&example| example == self)
+ else {
+ return self;
+ };
+
+ Self::LIST.get(index + 1).copied().unwrap_or(self)
+ }
+
+ fn view(&self) -> Element<Message> {
+ (self.view)()
+ }
+}
+
+impl Default for Example {
+ fn default() -> Self {
+ Self::LIST[0]
+ }
+}
+
+fn centered<'a>() -> Element<'a, Message> {
+ container(text("I am centered!").size(50))
+ .width(Length::Fill)
+ .height(Length::Fill)
+ .center_x()
+ .center_y()
+ .into()
+}
+
+fn column_<'a>() -> Element<'a, Message> {
+ column![
+ "A column can be used to",
+ "lay out widgets vertically.",
+ square(50),
+ square(50),
+ square(50),
+ "The amount of space between",
+ "elements can be configured!",
+ ]
+ .spacing(40)
+ .into()
+}
+
+fn row_<'a>() -> Element<'a, Message> {
+ row![
+ "A row works like a column...",
+ square(50),
+ square(50),
+ square(50),
+ "but lays out widgets horizontally!",
+ ]
+ .spacing(40)
+ .into()
+}
+
+fn space<'a>() -> Element<'a, Message> {
+ row!["Left!", horizontal_space(Length::Fill), "Right!"].into()
+}
+
+fn application<'a>() -> Element<'a, Message> {
+ let header = container(
+ row![
+ square(40),
+ horizontal_space(Length::Fill),
+ "Header!",
+ horizontal_space(Length::Fill),
+ square(40),
+ ]
+ .padding(10)
+ .align_items(Alignment::Center),
+ )
+ .style(|theme: &Theme| {
+ let palette = theme.extended_palette();
+
+ container::Appearance::default()
+ .with_border(palette.background.strong.color, 1)
+ });
+
+ let sidebar = container(
+ column!["Sidebar!", square(50), square(50)]
+ .spacing(40)
+ .padding(10)
+ .width(200)
+ .align_items(Alignment::Center),
+ )
+ .style(theme::Container::Box)
+ .height(Length::Fill)
+ .center_y();
+
+ let content = container(
+ scrollable(
+ column![
+ "Content!",
+ square(400),
+ square(200),
+ square(400),
+ "The end"
+ ]
+ .spacing(40)
+ .align_items(Alignment::Center)
+ .width(Length::Fill),
+ )
+ .height(Length::Fill),
+ )
+ .padding(10);
+
+ column![header, row![sidebar, content]].into()
+}
+
+fn nested_quotes<'a>() -> Element<'a, Message> {
+ (1..5)
+ .fold(column![text("Original text")].padding(10), |quotes, i| {
+ column![
+ container(
+ row![vertical_rule(2), quotes].height(Length::Shrink)
+ )
+ .style(|theme: &Theme| {
+ let palette = theme.extended_palette();
+
+ container::Appearance::default().with_background(
+ if palette.is_dark {
+ Color {
+ a: 0.01,
+ ..Color::WHITE
+ }
+ } else {
+ Color {
+ a: 0.08,
+ ..Color::BLACK
+ }
+ },
+ )
+ }),
+ text(format!("Reply {i}"))
+ ]
+ .spacing(10)
+ .padding(10)
+ })
+ .into()
+}
+
+fn square<'a>(size: impl Into<Length> + Copy) -> Element<'a, Message> {
+ struct Square;
+
+ impl canvas::Program<Message> for Square {
+ type State = ();
+
+ fn draw(
+ &self,
+ _state: &Self::State,
+ renderer: &Renderer,
+ theme: &Theme,
+ bounds: Rectangle,
+ _cursor: mouse::Cursor,
+ ) -> Vec<canvas::Geometry> {
+ let mut frame = canvas::Frame::new(renderer, bounds.size());
+
+ let palette = theme.extended_palette();
+
+ frame.fill_rectangle(
+ Point::ORIGIN,
+ bounds.size(),
+ palette.background.strong.color,
+ );
+
+ vec![frame.into_geometry()]
+ }
+ }
+
+ canvas(Square).width(size).height(size).into()
+}
diff --git a/examples/lazy/src/main.rs b/examples/lazy/src/main.rs
index 01560598..04df0744 100644
--- a/examples/lazy/src/main.rs
+++ b/examples/lazy/src/main.rs
@@ -178,35 +178,23 @@ impl Sandbox for App {
}
});
- column(
- items
- .into_iter()
- .map(|item| {
- let button = button("Delete")
- .on_press(Message::DeleteItem(item.clone()))
- .style(theme::Button::Destructive);
-
- row![
- text(&item.name)
- .style(theme::Text::Color(item.color.into())),
- horizontal_space(Length::Fill),
- pick_list(
- Color::ALL,
- Some(item.color),
- move |color| {
- Message::ItemColorChanged(
- item.clone(),
- color,
- )
- }
- ),
- button
- ]
- .spacing(20)
- .into()
- })
- .collect(),
- )
+ column(items.into_iter().map(|item| {
+ let button = button("Delete")
+ .on_press(Message::DeleteItem(item.clone()))
+ .style(theme::Button::Destructive);
+
+ row![
+ text(&item.name)
+ .style(theme::Text::Color(item.color.into())),
+ horizontal_space(Length::Fill),
+ pick_list(Color::ALL, Some(item.color), move |color| {
+ Message::ItemColorChanged(item.clone(), color)
+ }),
+ button
+ ]
+ .spacing(20)
+ .into()
+ }))
.spacing(10)
});
diff --git a/examples/loading_spinners/src/circular.rs b/examples/loading_spinners/src/circular.rs
index dca8046a..2e119979 100644
--- a/examples/loading_spinners/src/circular.rs
+++ b/examples/loading_spinners/src/circular.rs
@@ -244,12 +244,11 @@ where
tree::State::new(State::default())
}
- fn width(&self) -> Length {
- Length::Fixed(self.size)
- }
-
- fn height(&self) -> Length {
- Length::Fixed(self.size)
+ fn size(&self) -> Size<Length> {
+ Size {
+ width: Length::Fixed(self.size),
+ height: Length::Fixed(self.size),
+ }
}
fn layout(
@@ -258,10 +257,7 @@ where
_renderer: &iced::Renderer<Theme>,
limits: &layout::Limits,
) -> layout::Node {
- let limits = limits.width(self.size).height(self.size);
- let size = limits.resolve(Size::ZERO);
-
- layout::Node::new(size)
+ layout::atomic(limits, self.size, self.size)
}
fn on_event(
@@ -275,8 +271,6 @@ where
shell: &mut Shell<'_, Message>,
_viewport: &Rectangle,
) -> event::Status {
- const FRAME_RATE: u64 = 60;
-
let state = tree.state.downcast_mut::<State>();
if let Event::Window(_, window::Event::RedrawRequested(now)) = event {
@@ -287,9 +281,7 @@ where
);
state.cache.clear();
- shell.request_redraw(RedrawRequest::At(
- now + Duration::from_millis(1000 / FRAME_RATE),
- ));
+ shell.request_redraw(RedrawRequest::NextFrame);
}
event::Status::Ignored
diff --git a/examples/loading_spinners/src/linear.rs b/examples/loading_spinners/src/linear.rs
index db10bfba..497e0834 100644
--- a/examples/loading_spinners/src/linear.rs
+++ b/examples/loading_spinners/src/linear.rs
@@ -165,12 +165,11 @@ where
tree::State::new(State::default())
}
- fn width(&self) -> Length {
- self.width
- }
-
- fn height(&self) -> Length {
- self.height
+ fn size(&self) -> Size<Length> {
+ Size {
+ width: self.width,
+ height: self.height,
+ }
}
fn layout(
@@ -179,10 +178,7 @@ where
_renderer: &Renderer,
limits: &layout::Limits,
) -> layout::Node {
- let limits = limits.width(self.width).height(self.height);
- let size = limits.resolve(Size::ZERO);
-
- layout::Node::new(size)
+ layout::atomic(limits, self.width, self.height)
}
fn on_event(
@@ -196,16 +192,12 @@ where
shell: &mut Shell<'_, Message>,
_viewport: &Rectangle,
) -> event::Status {
- const FRAME_RATE: u64 = 60;
-
let state = tree.state.downcast_mut::<State>();
if let Event::Window(_, window::Event::RedrawRequested(now)) = event {
*state = state.timed_transition(self.cycle_duration, now);
- shell.request_redraw(RedrawRequest::At(
- now + Duration::from_millis(1000 / FRAME_RATE),
- ));
+ shell.request_redraw(RedrawRequest::NextFrame);
}
event::Status::Ignored
diff --git a/examples/loading_spinners/src/main.rs b/examples/loading_spinners/src/main.rs
index a78e9590..93a4605e 100644
--- a/examples/loading_spinners/src/main.rs
+++ b/examples/loading_spinners/src/main.rs
@@ -96,15 +96,14 @@ impl Application for LoadingSpinners {
container(
column.push(
- row(vec![
- text("Cycle duration:").into(),
+ row![
+ text("Cycle duration:"),
slider(1.0..=1000.0, self.cycle_duration * 100.0, |x| {
Message::CycleDurationChanged(x / 100.0)
})
- .width(200.0)
- .into(),
- text(format!("{:.2}s", self.cycle_duration)).into(),
- ])
+ .width(200.0),
+ text(format!("{:.2}s", self.cycle_duration)),
+ ]
.align_items(iced::Alignment::Center)
.spacing(20.0),
),
diff --git a/examples/modal/src/main.rs b/examples/modal/src/main.rs
index acb14372..963c839e 100644
--- a/examples/modal/src/main.rs
+++ b/examples/modal/src/main.rs
@@ -1,6 +1,7 @@
use iced::event::{self, Event};
use iced::executor;
use iced::keyboard;
+use iced::keyboard::key;
use iced::theme;
use iced::widget::{
self, button, column, container, horizontal_space, pick_list, row, text,
@@ -85,8 +86,9 @@ impl Application for App {
}
Message::Event(event) => match event {
Event::Keyboard(keyboard::Event::KeyPressed {
- key_code: keyboard::KeyCode::Tab,
+ key: keyboard::Key::Named(key::Named::Tab),
modifiers,
+ ..
}) => {
if modifiers.shift() {
widget::focus_previous()
@@ -95,7 +97,7 @@ impl Application for App {
}
}
Event::Keyboard(keyboard::Event::KeyPressed {
- key_code: keyboard::KeyCode::Escape,
+ key: keyboard::Key::Named(key::Named::Escape),
..
}) => {
self.hide_modal();
@@ -281,12 +283,8 @@ mod modal {
tree.diff_children(&[&self.base, &self.modal]);
}
- fn width(&self) -> Length {
- self.base.as_widget().width()
- }
-
- fn height(&self) -> Length {
- self.base.as_widget().height()
+ fn size(&self) -> Size<Length> {
+ self.base.as_widget().size()
}
fn layout(
@@ -420,17 +418,14 @@ mod modal {
.width(Length::Fill)
.height(Length::Fill);
- let mut child = self
+ let child = self
.content
.as_widget()
- .layout(self.tree, renderer, &limits);
-
- child.align(Alignment::Center, Alignment::Center, limits.max());
-
- let mut node = layout::Node::with_children(self.size, vec![child]);
- node.move_to(position);
+ .layout(self.tree, renderer, &limits)
+ .align(Alignment::Center, Alignment::Center, limits.max());
- node
+ layout::Node::with_children(self.size, vec![child])
+ .move_to(position)
}
fn on_event(
diff --git a/examples/pane_grid/src/main.rs b/examples/pane_grid/src/main.rs
index aa3149bb..d5e5bcbe 100644
--- a/examples/pane_grid/src/main.rs
+++ b/examples/pane_grid/src/main.rs
@@ -220,23 +220,26 @@ const PANE_ID_COLOR_FOCUSED: Color = Color::from_rgb(
0x47 as f32 / 255.0,
);
-fn handle_hotkey(key_code: keyboard::KeyCode) -> Option<Message> {
- use keyboard::KeyCode;
+fn handle_hotkey(key: keyboard::Key) -> Option<Message> {
+ use keyboard::key::{self, Key};
use pane_grid::{Axis, Direction};
- let direction = match key_code {
- KeyCode::Up => Some(Direction::Up),
- KeyCode::Down => Some(Direction::Down),
- KeyCode::Left => Some(Direction::Left),
- KeyCode::Right => Some(Direction::Right),
- _ => None,
- };
+ match key.as_ref() {
+ Key::Character("v") => Some(Message::SplitFocused(Axis::Vertical)),
+ Key::Character("h") => Some(Message::SplitFocused(Axis::Horizontal)),
+ Key::Character("w") => Some(Message::CloseFocused),
+ Key::Named(key) => {
+ let direction = match key {
+ key::Named::ArrowUp => Some(Direction::Up),
+ key::Named::ArrowDown => Some(Direction::Down),
+ key::Named::ArrowLeft => Some(Direction::Left),
+ key::Named::ArrowRight => Some(Direction::Right),
+ _ => None,
+ };
- match key_code {
- KeyCode::V => Some(Message::SplitFocused(Axis::Vertical)),
- KeyCode::H => Some(Message::SplitFocused(Axis::Horizontal)),
- KeyCode::W => Some(Message::CloseFocused),
- _ => direction.map(Message::FocusAdjacent),
+ direction.map(Message::FocusAdjacent)
+ }
+ _ => None,
}
}
@@ -297,7 +300,6 @@ fn view_content<'a>(
text(format!("{}x{}", size.width, size.height)).size(24),
controls,
]
- .width(Length::Fill)
.spacing(10)
.align_items(Alignment::Center);
diff --git a/examples/pick_list/src/main.rs b/examples/pick_list/src/main.rs
index 21200621..e4d96dc8 100644
--- a/examples/pick_list/src/main.rs
+++ b/examples/pick_list/src/main.rs
@@ -1,4 +1,4 @@
-use iced::widget::{column, container, pick_list, scrollable, vertical_space};
+use iced::widget::{column, pick_list, scrollable, vertical_space};
use iced::{Alignment, Element, Length, Sandbox, Settings};
pub fn main() -> iced::Result {
@@ -52,12 +52,7 @@ impl Sandbox for Example {
.align_items(Alignment::Center)
.spacing(10);
- container(scrollable(content))
- .width(Length::Fill)
- .height(Length::Fill)
- .center_x()
- .center_y()
- .into()
+ scrollable(content).into()
}
}
diff --git a/examples/screenshot/src/main.rs b/examples/screenshot/src/main.rs
index 20d34be6..6955551e 100644
--- a/examples/screenshot/src/main.rs
+++ b/examples/screenshot/src/main.rs
@@ -1,11 +1,13 @@
-use iced::keyboard::KeyCode;
-use iced::theme::{Button, Container};
+use iced::alignment;
+use iced::executor;
+use iced::keyboard;
+use iced::theme;
use iced::widget::{button, column, container, image, row, text, text_input};
+use iced::window;
use iced::window::screenshot::{self, Screenshot};
-use iced::{alignment, window};
use iced::{
- event, executor, keyboard, Alignment, Application, Command, ContentFit,
- Element, Event, Length, Rectangle, Renderer, Subscription, Theme,
+ Alignment, Application, Command, ContentFit, Element, Length, Rectangle,
+ Renderer, Subscription, Theme,
};
use ::image as img;
@@ -147,7 +149,7 @@ impl Application for Example {
let image = container(image)
.padding(10)
- .style(Container::Box)
+ .style(theme::Container::Box)
.width(Length::FillPortion(2))
.height(Length::Fill)
.center_x()
@@ -202,9 +204,10 @@ impl Application for Example {
self.screenshot.is_some().then(|| Message::Png),
)
} else {
- button(centered_text("Saving...")).style(Button::Secondary)
+ button(centered_text("Saving..."))
+ .style(theme::Button::Secondary)
}
- .style(Button::Secondary)
+ .style(theme::Button::Secondary)
.padding([10, 20, 10, 20])
.width(Length::Fill)
]
@@ -213,7 +216,7 @@ impl Application for Example {
crop_controls,
button(centered_text("Crop"))
.on_press(Message::Crop)
- .style(Button::Destructive)
+ .style(theme::Button::Destructive)
.padding([10, 20, 10, 20])
.width(Length::Fill),
]
@@ -256,16 +259,10 @@ impl Application for Example {
}
fn subscription(&self) -> Subscription<Self::Message> {
- event::listen_with(|event, status| {
- if let event::Status::Captured = status {
- return None;
- }
+ use keyboard::key;
- if let Event::Keyboard(keyboard::Event::KeyPressed {
- key_code: KeyCode::F5,
- ..
- }) = event
- {
+ keyboard::on_key_press(|key, _modifiers| {
+ if let keyboard::Key::Named(key::Named::F5) = key {
Some(Message::Screenshot)
} else {
None
diff --git a/examples/scrollable/src/main.rs b/examples/scrollable/src/main.rs
index d82ea841..4b57a5a4 100644
--- a/examples/scrollable/src/main.rs
+++ b/examples/scrollable/src/main.rs
@@ -147,63 +147,54 @@ impl Application for ScrollableDemo {
text("Scroller width:"),
scroller_width_slider,
]
- .spacing(10)
- .width(Length::Fill);
+ .spacing(10);
- let scroll_orientation_controls = column(vec![
- text("Scrollbar direction:").into(),
+ let scroll_orientation_controls = column![
+ text("Scrollbar direction:"),
radio(
"Vertical",
Direction::Vertical,
Some(self.scrollable_direction),
Message::SwitchDirection,
- )
- .into(),
+ ),
radio(
"Horizontal",
Direction::Horizontal,
Some(self.scrollable_direction),
Message::SwitchDirection,
- )
- .into(),
+ ),
radio(
"Both!",
Direction::Multi,
Some(self.scrollable_direction),
Message::SwitchDirection,
- )
- .into(),
- ])
- .spacing(10)
- .width(Length::Fill);
+ ),
+ ]
+ .spacing(10);
- let scroll_alignment_controls = column(vec![
- text("Scrollable alignment:").into(),
+ let scroll_alignment_controls = column![
+ text("Scrollable alignment:"),
radio(
"Start",
scrollable::Alignment::Start,
Some(self.alignment),
Message::AlignmentChanged,
- )
- .into(),
+ ),
radio(
"End",
scrollable::Alignment::End,
Some(self.alignment),
Message::AlignmentChanged,
)
- .into(),
- ])
- .spacing(10)
- .width(Length::Fill);
+ ]
+ .spacing(10);
let scroll_controls = row![
scroll_slider_controls,
scroll_orientation_controls,
scroll_alignment_controls
]
- .spacing(20)
- .width(Length::Fill);
+ .spacing(20);
let scroll_to_end_button = || {
button("Scroll to end")
@@ -229,11 +220,11 @@ impl Application for ScrollableDemo {
text("End!"),
scroll_to_beginning_button(),
]
- .width(Length::Fill)
.align_items(Alignment::Center)
.padding([40, 0, 40, 0])
.spacing(40),
)
+ .width(Length::Fill)
.height(Length::Fill)
.direction(scrollable::Direction::Vertical(
Properties::new()
@@ -259,6 +250,7 @@ impl Application for ScrollableDemo {
.padding([0, 40, 0, 40])
.spacing(40),
)
+ .width(Length::Fill)
.height(Length::Fill)
.direction(scrollable::Direction::Horizontal(
Properties::new()
@@ -301,6 +293,7 @@ impl Application for ScrollableDemo {
.padding([0, 40, 0, 40])
.spacing(40),
)
+ .width(Length::Fill)
.height(Length::Fill)
.direction({
let properties = Properties::new()
@@ -341,20 +334,11 @@ impl Application for ScrollableDemo {
let content: Element<Message> =
column![scroll_controls, scrollable_content, progress_bars]
- .width(Length::Fill)
- .height(Length::Fill)
.align_items(Alignment::Center)
.spacing(10)
.into();
- Element::from(
- container(content)
- .width(Length::Fill)
- .height(Length::Fill)
- .padding(40)
- .center_x()
- .center_y(),
- )
+ container(content).padding(20).center_x().center_y().into()
}
fn theme(&self) -> Self::Theme {
diff --git a/examples/sierpinski_triangle/src/main.rs b/examples/sierpinski_triangle/src/main.rs
index ef935c33..01a114bb 100644
--- a/examples/sierpinski_triangle/src/main.rs
+++ b/examples/sierpinski_triangle/src/main.rs
@@ -79,12 +79,10 @@ impl Application for SierpinskiEmulator {
row![
text(format!("Iteration: {:?}", self.graph.iteration)),
slider(0..=10000, self.graph.iteration, Message::IterationSet)
- .width(Length::Fill)
]
.padding(10)
.spacing(20),
]
- .width(Length::Fill)
.align_items(iced::Alignment::Center)
.into()
}
diff --git a/examples/stopwatch/src/main.rs b/examples/stopwatch/src/main.rs
index 0b0f0607..8a0674c1 100644
--- a/examples/stopwatch/src/main.rs
+++ b/examples/stopwatch/src/main.rs
@@ -86,12 +86,16 @@ impl Application for Stopwatch {
};
fn handle_hotkey(
- key_code: keyboard::KeyCode,
+ key: keyboard::Key,
_modifiers: keyboard::Modifiers,
) -> Option<Message> {
- match key_code {
- keyboard::KeyCode::Space => Some(Message::Toggle),
- keyboard::KeyCode::R => Some(Message::Reset),
+ use keyboard::key;
+
+ match key.as_ref() {
+ keyboard::Key::Named(key::Named::Space) => {
+ Some(Message::Toggle)
+ }
+ keyboard::Key::Character("r") => Some(Message::Reset),
_ => None,
}
}
diff --git a/examples/styling/src/main.rs b/examples/styling/src/main.rs
index 51538ec2..10f3c79d 100644
--- a/examples/styling/src/main.rs
+++ b/examples/styling/src/main.rs
@@ -53,13 +53,16 @@ impl Sandbox for Styling {
self.theme = match theme {
ThemeType::Light => Theme::Light,
ThemeType::Dark => Theme::Dark,
- ThemeType::Custom => Theme::custom(theme::Palette {
- background: Color::from_rgb(1.0, 0.9, 1.0),
- text: Color::BLACK,
- primary: Color::from_rgb(0.5, 0.5, 0.0),
- success: Color::from_rgb(0.0, 1.0, 0.0),
- danger: Color::from_rgb(1.0, 0.0, 0.0),
- }),
+ ThemeType::Custom => Theme::custom(
+ String::from("Custom"),
+ theme::Palette {
+ background: Color::from_rgb(1.0, 0.9, 1.0),
+ text: Color::BLACK,
+ primary: Color::from_rgb(0.5, 0.5, 0.0),
+ success: Color::from_rgb(0.0, 1.0, 0.0),
+ danger: Color::from_rgb(1.0, 0.0, 0.0),
+ },
+ ),
}
}
Message::InputChanged(value) => self.input_value = value,
@@ -104,10 +107,11 @@ impl Sandbox for Styling {
let progress_bar = progress_bar(0.0..=100.0, self.slider_value);
- let scrollable = scrollable(
- column!["Scroll me!", vertical_space(800), "You did it!"]
- .width(Length::Fill),
- )
+ let scrollable = scrollable(column![
+ "Scroll me!",
+ vertical_space(800),
+ "You did it!"
+ ])
.width(Length::Fill)
.height(100);
diff --git a/examples/svg/src/main.rs b/examples/svg/src/main.rs
index 4dc92416..3bf4960f 100644
--- a/examples/svg/src/main.rs
+++ b/examples/svg/src/main.rs
@@ -63,7 +63,6 @@ impl Sandbox for Tiger {
container(apply_color_filter).width(Length::Fill).center_x()
]
.spacing(20)
- .width(Length::Fill)
.height(Length::Fill),
)
.width(Length::Fill)
diff --git a/examples/toast/src/main.rs b/examples/toast/src/main.rs
index 31b6f191..2e837fa3 100644
--- a/examples/toast/src/main.rs
+++ b/examples/toast/src/main.rs
@@ -1,6 +1,7 @@
use iced::event::{self, Event};
use iced::executor;
use iced::keyboard;
+use iced::keyboard::key;
use iced::widget::{
self, button, column, container, pick_list, row, slider, text, text_input,
};
@@ -93,11 +94,12 @@ impl Application for App {
Command::none()
}
Message::Event(Event::Keyboard(keyboard::Event::KeyPressed {
- key_code: keyboard::KeyCode::Tab,
+ key: keyboard::Key::Named(key::Named::Tab),
modifiers,
+ ..
})) if modifiers.shift() => widget::focus_previous(),
Message::Event(Event::Keyboard(keyboard::Event::KeyPressed {
- key_code: keyboard::KeyCode::Tab,
+ key: keyboard::Key::Named(key::Named::Tab),
..
})) => widget::focus_next(),
Message::Event(_) => Command::none(),
@@ -106,9 +108,7 @@ impl Application for App {
fn view<'a>(&'a self) -> Element<'a, Message> {
let subtitle = |title, content: Element<'a, Message>| {
- column![text(title).size(14), content]
- .width(Length::Fill)
- .spacing(5)
+ column![text(title).size(14), content].spacing(5)
};
let mut add_toast = button("Add Toast");
@@ -153,14 +153,11 @@ impl Application for App {
Message::Timeout
)
.step(1.0)
- .width(Length::Fill)
]
.spacing(5)
.into()
),
- column![add_toast]
- .width(Length::Fill)
- .align_items(Alignment::End)
+ column![add_toast].align_items(Alignment::End)
]
.spacing(10)
.max_width(200),
@@ -318,12 +315,8 @@ mod toast {
}
impl<'a, Message> Widget<Message, Renderer> for Manager<'a, Message> {
- fn width(&self) -> Length {
- self.content.as_widget().width()
- }
-
- fn height(&self) -> Length {
- self.content.as_widget().height()
+ fn size(&self) -> Size<Length> {
+ self.content.as_widget().size()
}
fn layout(
@@ -513,14 +506,14 @@ mod toast {
position: Point,
_translation: Vector,
) -> layout::Node {
- let limits = layout::Limits::new(Size::ZERO, bounds)
- .width(Length::Fill)
- .height(Length::Fill);
+ let limits = layout::Limits::new(Size::ZERO, bounds);
layout::flex::resolve(
layout::flex::Axis::Vertical,
renderer,
&limits,
+ Length::Fill,
+ Length::Fill,
10.into(),
10.0,
Alignment::End,
diff --git a/examples/todos/src/main.rs b/examples/todos/src/main.rs
index 4dac032c..3d79f087 100644
--- a/examples/todos/src/main.rs
+++ b/examples/todos/src/main.rs
@@ -254,27 +254,27 @@ impl Application for Todos {
.spacing(20)
.max_width(800);
- scrollable(
- container(content)
- .width(Length::Fill)
- .padding(40)
- .center_x(),
- )
- .into()
+ scrollable(container(content).padding(40).center_x()).into()
}
}
}
fn subscription(&self) -> Subscription<Message> {
- keyboard::on_key_press(|key_code, modifiers| {
- match (key_code, modifiers) {
- (keyboard::KeyCode::Tab, _) => Some(Message::TabPressed {
+ use keyboard::key;
+
+ keyboard::on_key_press(|key, modifiers| {
+ let keyboard::Key::Named(key) = key else {
+ return None;
+ };
+
+ match (key, modifiers) {
+ (key::Named::Tab, _) => Some(Message::TabPressed {
shift: modifiers.shift(),
}),
- (keyboard::KeyCode::Up, keyboard::Modifiers::SHIFT) => {
+ (key::Named::ArrowUp, keyboard::Modifiers::SHIFT) => {
Some(Message::ToggleFullscreen(window::Mode::Fullscreen))
}
- (keyboard::KeyCode::Down, keyboard::Modifiers::SHIFT) => {
+ (key::Named::ArrowDown, keyboard::Modifiers::SHIFT) => {
Some(Message::ToggleFullscreen(window::Mode::Windowed))
}
_ => None,
@@ -472,7 +472,6 @@ fn empty_message(message: &str) -> Element<'_, Message> {
.horizontal_alignment(alignment::Horizontal::Center)
.style(Color::from([0.7, 0.7, 0.7])),
)
- .width(Length::Fill)
.height(200)
.center_y()
.into()
diff --git a/examples/tour/src/main.rs b/examples/tour/src/main.rs
index 7003d8ae..8633bc0a 100644
--- a/examples/tour/src/main.rs
+++ b/examples/tour/src/main.rs
@@ -509,7 +509,6 @@ impl<'a> Step {
)
})
.map(Element::from)
- .collect()
)
.spacing(10)
]
@@ -692,11 +691,7 @@ fn ferris<'a>(
}
fn button<'a, Message: Clone>(label: &str) -> Button<'a, Message> {
- iced::widget::button(
- text(label).horizontal_alignment(alignment::Horizontal::Center),
- )
- .padding(12)
- .width(100)
+ iced::widget::button(text(label)).padding([12, 24])
}
fn color_slider<'a>(
diff --git a/examples/vectorial_text/Cargo.toml b/examples/vectorial_text/Cargo.toml
new file mode 100644
index 00000000..76c1af7c
--- /dev/null
+++ b/examples/vectorial_text/Cargo.toml
@@ -0,0 +1,9 @@
+[package]
+name = "vectorial_text"
+version = "0.1.0"
+authors = ["Héctor Ramón Jiménez <hector0193@gmail.com>"]
+edition = "2021"
+publish = false
+
+[dependencies]
+iced = { path = "../..", features = ["canvas", "debug"] }
diff --git a/examples/vectorial_text/src/main.rs b/examples/vectorial_text/src/main.rs
new file mode 100644
index 00000000..d366b907
--- /dev/null
+++ b/examples/vectorial_text/src/main.rs
@@ -0,0 +1,175 @@
+use iced::alignment::{self, Alignment};
+use iced::mouse;
+use iced::widget::{
+ canvas, checkbox, column, horizontal_space, row, slider, text,
+};
+use iced::{
+ Element, Length, Point, Rectangle, Renderer, Sandbox, Settings, Theme,
+ Vector,
+};
+
+pub fn main() -> iced::Result {
+ VectorialText::run(Settings {
+ antialiasing: true,
+ ..Settings::default()
+ })
+}
+
+struct VectorialText {
+ state: State,
+}
+
+#[derive(Debug, Clone, Copy)]
+enum Message {
+ SizeChanged(f32),
+ AngleChanged(f32),
+ ScaleChanged(f32),
+ ToggleJapanese(bool),
+}
+
+impl Sandbox for VectorialText {
+ type Message = Message;
+
+ fn new() -> Self {
+ Self {
+ state: State::new(),
+ }
+ }
+
+ fn title(&self) -> String {
+ String::from("Vectorial Text - Iced")
+ }
+
+ fn update(&mut self, message: Message) {
+ match message {
+ Message::SizeChanged(size) => {
+ self.state.size = size;
+ }
+ Message::AngleChanged(angle) => {
+ self.state.angle = angle;
+ }
+ Message::ScaleChanged(scale) => {
+ self.state.scale = scale;
+ }
+ Message::ToggleJapanese(use_japanese) => {
+ self.state.use_japanese = use_japanese;
+ }
+ }
+
+ self.state.cache.clear();
+ }
+
+ fn view(&self) -> Element<Message> {
+ let slider_with_label = |label, range, value, message: fn(f32) -> _| {
+ column![
+ row![
+ text(label),
+ horizontal_space(Length::Fill),
+ text(format!("{:.2}", value))
+ ],
+ slider(range, value, message).step(0.01)
+ ]
+ .spacing(2)
+ };
+
+ column![
+ canvas(&self.state).width(Length::Fill).height(Length::Fill),
+ column![
+ checkbox(
+ "Use Japanese",
+ self.state.use_japanese,
+ Message::ToggleJapanese
+ ),
+ row![
+ slider_with_label(
+ "Size",
+ 2.0..=80.0,
+ self.state.size,
+ Message::SizeChanged,
+ ),
+ slider_with_label(
+ "Angle",
+ 0.0..=360.0,
+ self.state.angle,
+ Message::AngleChanged,
+ ),
+ slider_with_label(
+ "Scale",
+ 1.0..=20.0,
+ self.state.scale,
+ Message::ScaleChanged,
+ ),
+ ]
+ .spacing(20),
+ ]
+ .align_items(Alignment::Center)
+ .spacing(10)
+ ]
+ .spacing(10)
+ .padding(20)
+ .into()
+ }
+
+ fn theme(&self) -> Theme {
+ Theme::Dark
+ }
+}
+
+struct State {
+ size: f32,
+ angle: f32,
+ scale: f32,
+ use_japanese: bool,
+ cache: canvas::Cache,
+}
+
+impl State {
+ pub fn new() -> Self {
+ Self {
+ size: 40.0,
+ angle: 0.0,
+ scale: 1.0,
+ use_japanese: false,
+ cache: canvas::Cache::new(),
+ }
+ }
+}
+
+impl<Message> canvas::Program<Message> for State {
+ type State = ();
+
+ fn draw(
+ &self,
+ _state: &Self::State,
+ renderer: &Renderer,
+ theme: &Theme,
+ bounds: Rectangle,
+ _cursor: mouse::Cursor,
+ ) -> Vec<canvas::Geometry> {
+ let geometry = self.cache.draw(renderer, bounds.size(), |frame| {
+ let palette = theme.palette();
+ let center = bounds.center();
+
+ frame.translate(Vector::new(center.x, center.y));
+ frame.scale(self.scale);
+ frame.rotate(self.angle * std::f32::consts::PI / 180.0);
+
+ frame.fill_text(canvas::Text {
+ position: Point::new(0.0, 0.0),
+ color: palette.text,
+ size: self.size.into(),
+ content: String::from(if self.use_japanese {
+ "ベクトルテキスト🎉"
+ } else {
+ "Vectorial Text! 🎉"
+ }),
+ horizontal_alignment: alignment::Horizontal::Center,
+ vertical_alignment: alignment::Vertical::Center,
+ shaping: text::Shaping::Advanced,
+ ..canvas::Text::default()
+ });
+ });
+
+ vec![geometry]
+ }
+}
diff --git a/examples/websocket/src/main.rs b/examples/websocket/src/main.rs
index 920189f5..38a6db1e 100644
--- a/examples/websocket/src/main.rs
+++ b/examples/websocket/src/main.rs
@@ -3,7 +3,7 @@ mod echo;
use iced::alignment::{self, Alignment};
use iced::executor;
use iced::widget::{
- button, column, container, row, scrollable, text, text_input, Column,
+ button, column, container, row, scrollable, text, text_input,
};
use iced::{
Application, Color, Command, Element, Length, Settings, Subscription, Theme,
@@ -108,15 +108,9 @@ impl Application for WebSocket {
.into()
} else {
scrollable(
- Column::with_children(
- self.messages
- .iter()
- .cloned()
- .map(text)
- .map(Element::from)
- .collect(),
+ column(
+ self.messages.iter().cloned().map(text).map(Element::from),
)
- .width(Length::Fill)
.spacing(10),
)
.id(MESSAGE_LOG.clone())
@@ -131,7 +125,7 @@ impl Application for WebSocket {
let mut button = button(
text("Send")
- .height(Length::Fill)
+ .height(40)
.vertical_alignment(alignment::Vertical::Center),
)
.padding([0, 20]);
@@ -149,7 +143,6 @@ impl Application for WebSocket {
};
column![message_log, new_message_input]
- .width(Length::Fill)
.height(Length::Fill)
.padding(20)
.spacing(10)
diff --git a/futures/src/keyboard.rs b/futures/src/keyboard.rs
index af68e1f2..8e7da38f 100644
--- a/futures/src/keyboard.rs
+++ b/futures/src/keyboard.rs
@@ -1,6 +1,6 @@
//! Listen to keyboard events.
use crate::core;
-use crate::core::keyboard::{Event, KeyCode, Modifiers};
+use crate::core::keyboard::{Event, Key, Modifiers};
use crate::subscription::{self, Subscription};
use crate::MaybeSend;
@@ -10,7 +10,7 @@ use crate::MaybeSend;
/// If the function returns `None`, the key press will be simply
/// ignored.
pub fn on_key_press<Message>(
- f: fn(KeyCode, Modifiers) -> Option<Message>,
+ f: fn(Key, Modifiers) -> Option<Message>,
) -> Subscription<Message>
where
Message: MaybeSend + 'static,
@@ -22,11 +22,10 @@ where
match (event, status) {
(
core::Event::Keyboard(Event::KeyPressed {
- key_code,
- modifiers,
+ key, modifiers, ..
}),
core::event::Status::Ignored,
- ) => f(key_code, modifiers),
+ ) => f(key, modifiers),
_ => None,
}
})
@@ -38,7 +37,7 @@ where
/// If the function returns `None`, the key release will be simply
/// ignored.
pub fn on_key_release<Message>(
- f: fn(KeyCode, Modifiers) -> Option<Message>,
+ f: fn(Key, Modifiers) -> Option<Message>,
) -> Subscription<Message>
where
Message: MaybeSend + 'static,
@@ -50,11 +49,12 @@ where
match (event, status) {
(
core::Event::Keyboard(Event::KeyReleased {
- key_code,
+ key,
modifiers,
+ ..
}),
core::event::Status::Ignored,
- ) => f(key_code, modifiers),
+ ) => f(key, modifiers),
_ => None,
}
})
diff --git a/graphics/src/damage.rs b/graphics/src/damage.rs
index 595cc274..59e9f5b4 100644
--- a/graphics/src/damage.rs
+++ b/graphics/src/damage.rs
@@ -73,6 +73,11 @@ impl<T: Damage> Damage for Primitive<T> {
bounds.expand(1.5)
}
+ Self::RawText(raw) => {
+ // TODO: Add `size` field to `raw` to compute more accurate
+ // damage bounds (?)
+ raw.clip_bounds.expand(1.5)
+ }
Self::Quad { bounds, .. }
| Self::Image { bounds, .. }
| Self::Svg { bounds, .. } => bounds.expand(1.0),
diff --git a/graphics/src/geometry/text.rs b/graphics/src/geometry/text.rs
index 0bf7ec97..d314e85e 100644
--- a/graphics/src/geometry/text.rs
+++ b/graphics/src/geometry/text.rs
@@ -1,6 +1,8 @@
use crate::core::alignment;
use crate::core::text::{LineHeight, Shaping};
-use crate::core::{Color, Font, Pixels, Point};
+use crate::core::{Color, Font, Pixels, Point, Size, Vector};
+use crate::geometry::Path;
+use crate::text;
/// A bunch of text that can be drawn to a canvas
#[derive(Debug, Clone)]
@@ -32,6 +34,137 @@ pub struct Text {
pub shaping: Shaping,
}
+impl Text {
+ /// Computes the [`Path`]s of the [`Text`] and draws them using
+ /// the given closure.
+ pub fn draw_with(&self, mut f: impl FnMut(Path, Color)) {
+ let mut font_system =
+ text::font_system().write().expect("Write font system");
+
+ let mut buffer = cosmic_text::BufferLine::new(
+ &self.content,
+ cosmic_text::AttrsList::new(text::to_attributes(self.font)),
+ text::to_shaping(self.shaping),
+ );
+
+ let layout = buffer.layout(
+ font_system.raw(),
+ self.size.0,
+ f32::MAX,
+ cosmic_text::Wrap::None,
+ );
+
+ let translation_x = match self.horizontal_alignment {
+ alignment::Horizontal::Left => self.position.x,
+ alignment::Horizontal::Center | alignment::Horizontal::Right => {
+ let mut line_width = 0.0f32;
+
+ for line in layout.iter() {
+ line_width = line_width.max(line.w);
+ }
+
+ if self.horizontal_alignment == alignment::Horizontal::Center {
+ self.position.x - line_width / 2.0
+ } else {
+ self.position.x - line_width
+ }
+ }
+ };
+
+ let translation_y = {
+ let line_height = self.line_height.to_absolute(self.size);
+
+ match self.vertical_alignment {
+ alignment::Vertical::Top => self.position.y,
+ alignment::Vertical::Center => {
+ self.position.y - line_height.0 / 2.0
+ }
+ alignment::Vertical::Bottom => self.position.y - line_height.0,
+ }
+ };
+
+ let mut swash_cache = cosmic_text::SwashCache::new();
+
+ for run in layout.iter() {
+ for glyph in run.glyphs.iter() {
+ let physical_glyph = glyph.physical((0.0, 0.0), 1.0);
+
+ let start_x = translation_x + glyph.x + glyph.x_offset;
+ let start_y = translation_y + glyph.y_offset + self.size.0;
+ let offset = Vector::new(start_x, start_y);
+
+ if let Some(commands) = swash_cache.get_outline_commands(
+ font_system.raw(),
+ physical_glyph.cache_key,
+ ) {
+ let glyph = Path::new(|path| {
+ use cosmic_text::Command;
+
+ for command in commands {
+ match command {
+ Command::MoveTo(p) => {
+ path.move_to(
+ Point::new(p.x, -p.y) + offset,
+ );
+ }
+ Command::LineTo(p) => {
+ path.line_to(
+ Point::new(p.x, -p.y) + offset,
+ );
+ }
+ Command::CurveTo(control_a, control_b, to) => {
+ path.bezier_curve_to(
+ Point::new(control_a.x, -control_a.y)
+ + offset,
+ Point::new(control_b.x, -control_b.y)
+ + offset,
+ Point::new(to.x, -to.y) + offset,
+ );
+ }
+ Command::QuadTo(control, to) => {
+ path.quadratic_curve_to(
+ Point::new(control.x, -control.y)
+ + offset,
+ Point::new(to.x, -to.y) + offset,
+ );
+ }
+ Command::Close => {
+ path.close();
+ }
+ }
+ }
+ });
+
+ f(glyph, self.color);
+ } else {
+ // TODO: Raster image support for `Canvas`
+ let [r, g, b, a] = self.color.into_rgba8();
+
+ swash_cache.with_pixels(
+ font_system.raw(),
+ physical_glyph.cache_key,
+ cosmic_text::Color::rgba(r, g, b, a),
+ |x, y, color| {
+ f(
+ Path::rectangle(
+ Point::new(x as f32, y as f32) + offset,
+ Size::new(1.0, 1.0),
+ ),
+ Color::from_rgba8(
+ color.r(),
+ color.g(),
+ color.b(),
+ color.a() as f32 / 255.0,
+ ),
+ );
+ },
+ );
+ }
+ }
+ }
+ }
+}
+
impl Default for Text {
fn default() -> Text {
Text {
diff --git a/graphics/src/primitive.rs b/graphics/src/primitive.rs
index ed75776c..20affaaf 100644
--- a/graphics/src/primitive.rs
+++ b/graphics/src/primitive.rs
@@ -57,6 +57,8 @@ pub enum Primitive<T> {
/// The clip bounds of the editor.
clip_bounds: Rectangle,
},
+ /// A raw `cosmic-text` primitive
+ RawText(crate::text::Raw),
/// A quad primitive
Quad {
/// The bounds of the quad
diff --git a/graphics/src/text.rs b/graphics/src/text.rs
index fc7694c2..7c4b5e31 100644
--- a/graphics/src/text.rs
+++ b/graphics/src/text.rs
@@ -9,14 +9,13 @@ pub use paragraph::Paragraph;
pub use cosmic_text;
-use crate::color;
use crate::core::font::{self, Font};
use crate::core::text::Shaping;
-use crate::core::{Color, Size};
+use crate::core::{Color, Point, Rectangle, Size};
use once_cell::sync::OnceCell;
use std::borrow::Cow;
-use std::sync::{Arc, RwLock};
+use std::sync::{Arc, RwLock, Weak};
/// Returns the global [`FontSystem`].
pub fn font_system() -> &'static RwLock<FontSystem> {
@@ -68,6 +67,29 @@ impl FontSystem {
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Default)]
pub struct Version(u32);
+/// A weak reference to a [`cosmic-text::Buffer`] that can be drawn.
+#[derive(Debug, Clone)]
+pub struct Raw {
+ /// A weak reference to a [`cosmic_text::Buffer`].
+ pub buffer: Weak<cosmic_text::Buffer>,
+ /// The position of the text.
+ pub position: Point,
+ /// The color of the text.
+ pub color: Color,
+ /// The clip bounds of the text.
+ pub clip_bounds: Rectangle,
+}
+
+impl PartialEq for Raw {
+ fn eq(&self, _other: &Self) -> bool {
+ // TODO: There is no proper way to compare raw buffers
+ // For now, no two instances of `Raw` text will be equal.
+ // This should be fine, but could trigger unnecessary redraws
+ // in the future.
+ false
+ }
+}
+
/// Measures the dimensions of the given [`cosmic_text::Buffer`].
pub fn measure(buffer: &cosmic_text::Buffer) -> Size {
let (width, total_lines) = buffer
@@ -150,12 +172,7 @@ pub fn to_shaping(shaping: Shaping) -> cosmic_text::Shaping {
/// Converts some [`Color`] to a [`cosmic_text::Color`].
pub fn to_color(color: Color) -> cosmic_text::Color {
- let [r, g, b, a] = color::pack(color).components();
+ let [r, g, b, a] = color.into_rgba8();
- cosmic_text::Color::rgba(
- (r * 255.0) as u8,
- (g * 255.0) as u8,
- (b * 255.0) as u8,
- (a * 255.0) as u8,
- )
+ cosmic_text::Color::rgba(r, g, b, a)
}
diff --git a/graphics/src/text/paragraph.rs b/graphics/src/text/paragraph.rs
index 4a08a8f4..5d027542 100644
--- a/graphics/src/text/paragraph.rs
+++ b/graphics/src/text/paragraph.rs
@@ -187,38 +187,43 @@ impl core::text::Paragraph for Paragraph {
}
fn grapheme_position(&self, line: usize, index: usize) -> Option<Point> {
+ use unicode_segmentation::UnicodeSegmentation;
+
let run = self.internal().buffer.layout_runs().nth(line)?;
// index represents a grapheme, not a glyph
// Let's find the first glyph for the given grapheme cluster
let mut last_start = None;
+ let mut last_grapheme_count = 0;
let mut graphemes_seen = 0;
let glyph = run
.glyphs
.iter()
.find(|glyph| {
- if graphemes_seen == index {
- return true;
- }
-
if Some(glyph.start) != last_start {
+ last_grapheme_count = run.text[glyph.start..glyph.end]
+ .graphemes(false)
+ .count();
last_start = Some(glyph.start);
- graphemes_seen += 1;
+ graphemes_seen += last_grapheme_count;
}
- false
+ graphemes_seen >= index
})
.or_else(|| run.glyphs.last())?;
- let advance_last = if index == run.glyphs.len() {
- glyph.w
- } else {
+ let advance = if index == 0 {
0.0
+ } else {
+ glyph.w
+ * (1.0
+ - graphemes_seen.saturating_sub(index) as f32
+ / last_grapheme_count.max(1) as f32)
};
Some(Point::new(
- glyph.x + glyph.x_offset * glyph.font_size + advance_last,
+ glyph.x + glyph.x_offset * glyph.font_size + advance,
glyph.y - glyph.y_offset * glyph.font_size,
))
}
diff --git a/renderer/src/backend.rs b/renderer/src/backend.rs
deleted file mode 100644
index 3f229b52..00000000
--- a/renderer/src/backend.rs
+++ /dev/null
@@ -1,100 +0,0 @@
-use crate::core::text;
-use crate::core::{Font, Point, Size};
-use crate::graphics::backend;
-
-use std::borrow::Cow;
-
-#[allow(clippy::large_enum_variant)]
-pub enum Backend {
- TinySkia(iced_tiny_skia::Backend),
- #[cfg(feature = "wgpu")]
- Wgpu(iced_wgpu::Backend),
-}
-
-macro_rules! delegate {
- ($backend:expr, $name:ident, $body:expr) => {
- match $backend {
- Self::TinySkia($name) => $body,
- #[cfg(feature = "wgpu")]
- Self::Wgpu($name) => $body,
- }
- };
-}
-
-impl backend::Text for Backend {
- const ICON_FONT: Font = Font::with_name("Iced-Icons");
- const CHECKMARK_ICON: char = '\u{f00c}';
- const ARROW_DOWN_ICON: char = '\u{e800}';
-
- fn default_font(&self) -> Font {
- delegate!(self, backend, backend.default_font())
- }
-
- fn default_size(&self) -> f32 {
- delegate!(self, backend, backend.default_size())
- }
-
- fn measure(
- &self,
- contents: &str,
- size: f32,
- line_height: text::LineHeight,
- font: Font,
- bounds: Size,
- shaping: text::Shaping,
- ) -> Size {
- delegate!(
- self,
- backend,
- backend.measure(contents, size, line_height, font, bounds, shaping)
- )
- }
-
- fn hit_test(
- &self,
- contents: &str,
- size: f32,
- line_height: text::LineHeight,
- font: Font,
- bounds: Size,
- shaping: text::Shaping,
- position: Point,
- nearest_only: bool,
- ) -> Option<text::Hit> {
- delegate!(
- self,
- backend,
- backend.hit_test(
- contents,
- size,
- line_height,
- font,
- bounds,
- shaping,
- position,
- nearest_only,
- )
- )
- }
-
- fn load_font(&mut self, font: Cow<'static, [u8]>) {
- delegate!(self, backend, backend.load_font(font));
- }
-}
-
-#[cfg(feature = "image")]
-impl backend::Image for Backend {
- fn dimensions(&self, handle: &crate::core::image::Handle) -> Size<u32> {
- delegate!(self, backend, backend.dimensions(handle))
- }
-}
-
-#[cfg(feature = "svg")]
-impl backend::Svg for Backend {
- fn viewport_dimensions(
- &self,
- handle: &crate::core::svg::Handle,
- ) -> Size<u32> {
- delegate!(self, backend, backend.viewport_dimensions(handle))
- }
-}
diff --git a/runtime/src/window.rs b/runtime/src/window.rs
index f9d943f6..2136d64d 100644
--- a/runtime/src/window.rs
+++ b/runtime/src/window.rs
@@ -65,11 +65,33 @@ pub fn fetch_size<Message>(
Command::single(command::Action::Window(Action::FetchSize(id, Box::new(f))))
}
+/// Fetches if the window is maximized.
+pub fn fetch_maximized<Message>(
+ id: Id,
+ f: impl FnOnce(bool) -> Message + 'static,
+) -> Command<Message> {
+ Command::single(command::Action::Window(Action::FetchMaximized(
+ id,
+ Box::new(f),
+ )))
+}
+
/// Maximizes the window.
pub fn maximize<Message>(id: Id, maximized: bool) -> Command<Message> {
Command::single(command::Action::Window(Action::Maximize(id, maximized)))
}
+/// Fetches if the window is minimized.
+pub fn fetch_minimized<Message>(
+ id: Id,
+ f: impl FnOnce(Option<bool>) -> Message + 'static,
+) -> Command<Message> {
+ Command::single(command::Action::Window(Action::FetchMinimized(
+ id,
+ Box::new(f),
+ )))
+}
+
/// Minimizes the window.
pub fn minimize<Message>(id: Id, minimized: bool) -> Command<Message> {
Command::single(command::Action::Window(Action::Minimize(id, minimized)))
diff --git a/runtime/src/window/action.rs b/runtime/src/window/action.rs
index 2d98b607..8b532569 100644
--- a/runtime/src/window/action.rs
+++ b/runtime/src/window/action.rs
@@ -21,8 +21,19 @@ pub enum Action<T> {
Resize(Id, Size),
/// Fetch the current logical dimensions of the window.
FetchSize(Id, Box<dyn FnOnce(Size) -> T + 'static>),
+ /// Fetch if the current window is maximized or not.
+ ///
+ /// ## Platform-specific
+ /// - **iOS / Android / Web:** Unsupported.
+ FetchMaximized(Id, Box<dyn FnOnce(bool) -> T + 'static>),
/// Set the window to maximized or back
Maximize(Id, bool),
+ /// Fetch if the current window is minimized or not.
+ ///
+ /// ## Platform-specific
+ /// - **Wayland:** Always `None`.
+ /// - **iOS / Android / Web:** Unsupported.
+ FetchMinimized(Id, Box<dyn FnOnce(Option<bool>) -> T + 'static>),
/// Set the window to minimized or back
Minimize(Id, bool),
/// Move the window to the given logical coordinates.
@@ -106,7 +117,13 @@ impl<T> Action<T> {
Self::FetchSize(id, o) => {
Action::FetchSize(id, Box::new(move |s| f(o(s))))
}
+ Self::FetchMaximized(id, o) => {
+ Action::FetchMaximized(id, Box::new(move |s| f(o(s))))
+ }
Self::Maximize(id, maximized) => Action::Maximize(id, maximized),
+ Self::FetchMinimized(id, o) => {
+ Action::FetchMinimized(id, Box::new(move |s| f(o(s))))
+ }
Self::Minimize(id, minimized) => Action::Minimize(id, minimized),
Self::Move(id, position) => Action::Move(id, position),
Self::ChangeMode(id, mode) => Action::ChangeMode(id, mode),
@@ -144,9 +161,15 @@ impl<T> fmt::Debug for Action<T> {
write!(f, "Action::Resize({id:?}, {size:?})")
}
Self::FetchSize(id, _) => write!(f, "Action::FetchSize({id:?})"),
+ Self::FetchMaximized(id, _) => {
+ write!(f, "Action::FetchMaximized({id:?})")
+ }
Self::Maximize(id, maximized) => {
write!(f, "Action::Maximize({id:?}, {maximized})")
}
+ Self::FetchMinimized(id, _) => {
+ write!(f, "Action::FetchMinimized({id:?})")
+ }
Self::Minimize(id, minimized) => {
write!(f, "Action::Minimize({id:?}, {minimized}")
}
diff --git a/src/lib.rs b/src/lib.rs
index 002d2a79..446590ec 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -230,7 +230,8 @@ pub mod event {
pub mod keyboard {
//! Listen and react to keyboard events.
- pub use crate::core::keyboard::{Event, KeyCode, Modifiers};
+ pub use crate::core::keyboard::key;
+ pub use crate::core::keyboard::{Event, Key, Location, Modifiers};
pub use iced_futures::keyboard::{on_key_press, on_key_release};
}
diff --git a/src/time.rs b/src/time.rs
index 37d454ed..f10f7a5e 100644
--- a/src/time.rs
+++ b/src/time.rs
@@ -1,4 +1,5 @@
//! Listen and react to time.
pub use iced_core::time::{Duration, Instant};
+#[allow(unused_imports)]
pub use iced_futures::backend::default::time::*;
diff --git a/style/src/container.rs b/style/src/container.rs
index ec543ae4..490a9dab 100644
--- a/style/src/container.rs
+++ b/style/src/container.rs
@@ -1,5 +1,5 @@
//! Change the appearance of a container.
-use iced_core::{Background, BorderRadius, Color};
+use crate::core::{Background, BorderRadius, Color, Pixels};
/// The appearance of a container.
#[derive(Debug, Clone, Copy)]
@@ -16,6 +16,30 @@ pub struct Appearance {
pub border_color: Color,
}
+impl Appearance {
+ /// Derives a new [`Appearance`] with a border of the given [`Color`] and
+ /// `width`.
+ pub fn with_border(
+ self,
+ color: impl Into<Color>,
+ width: impl Into<Pixels>,
+ ) -> Self {
+ Self {
+ border_color: color.into(),
+ border_width: width.into().0,
+ ..self
+ }
+ }
+
+ /// Derives a new [`Appearance`] with the given [`Background`].
+ pub fn with_background(self, background: impl Into<Background>) -> Self {
+ Self {
+ background: Some(background.into()),
+ ..self
+ }
+ }
+}
+
impl std::default::Default for Appearance {
fn default() -> Self {
Self {
diff --git a/style/src/theme.rs b/style/src/theme.rs
index 4af07794..f78587e5 100644
--- a/style/src/theme.rs
+++ b/style/src/theme.rs
@@ -23,6 +23,7 @@ use crate::toggler;
use iced_core::{Background, Color, Vector};
+use std::fmt;
use std::rc::Rc;
/// A built-in theme.
@@ -38,18 +39,22 @@ pub enum Theme {
}
impl Theme {
+ /// A list with all the defined themes.
+ pub const ALL: &'static [Self] = &[Self::Light, Self::Dark];
+
/// Creates a new custom [`Theme`] from the given [`Palette`].
- pub fn custom(palette: Palette) -> Self {
- Self::custom_with_fn(palette, palette::Extended::generate)
+ pub fn custom(name: String, palette: Palette) -> Self {
+ Self::custom_with_fn(name, palette, palette::Extended::generate)
}
/// Creates a new custom [`Theme`] from the given [`Palette`], with
/// a custom generator of a [`palette::Extended`].
pub fn custom_with_fn(
+ name: String,
palette: Palette,
generate: impl FnOnce(Palette) -> palette::Extended,
) -> Self {
- Self::Custom(Box::new(Custom::with_fn(palette, generate)))
+ Self::Custom(Box::new(Custom::with_fn(name, palette, generate)))
}
/// Returns the [`Palette`] of the [`Theme`].
@@ -71,32 +76,51 @@ impl Theme {
}
}
+impl fmt::Display for Theme {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ match self {
+ Self::Light => write!(f, "Light"),
+ Self::Dark => write!(f, "Dark"),
+ Self::Custom(custom) => custom.fmt(f),
+ }
+ }
+}
+
/// A [`Theme`] with a customized [`Palette`].
-#[derive(Debug, Clone, Copy, PartialEq)]
+#[derive(Debug, Clone, PartialEq)]
pub struct Custom {
+ name: String,
palette: Palette,
extended: palette::Extended,
}
impl Custom {
/// Creates a [`Custom`] theme from the given [`Palette`].
- pub fn new(palette: Palette) -> Self {
- Self::with_fn(palette, palette::Extended::generate)
+ pub fn new(name: String, palette: Palette) -> Self {
+ Self::with_fn(name, palette, palette::Extended::generate)
}
/// Creates a [`Custom`] theme from the given [`Palette`] with
/// a custom generator of a [`palette::Extended`].
pub fn with_fn(
+ name: String,
palette: Palette,
generate: impl FnOnce(Palette) -> palette::Extended,
) -> Self {
Self {
+ name,
palette,
extended: generate(palette),
}
}
}
+impl fmt::Display for Custom {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ write!(f, "{}", self.name)
+ }
+}
+
/// The style of an application.
#[derive(Default)]
pub enum Application {
@@ -383,6 +407,12 @@ pub enum Container {
Custom(Box<dyn container::StyleSheet<Style = Theme>>),
}
+impl From<container::Appearance> for Container {
+ fn from(appearance: container::Appearance) -> Self {
+ Self::Custom(Box::new(move |_: &_| appearance))
+ }
+}
+
impl<T: Fn(&Theme) -> container::Appearance + 'static> From<T> for Container {
fn from(f: T) -> Self {
Self::Custom(Box::new(f))
diff --git a/style/src/theme/palette.rs b/style/src/theme/palette.rs
index aaeb799d..76977a29 100644
--- a/style/src/theme/palette.rs
+++ b/style/src/theme/palette.rs
@@ -82,6 +82,8 @@ pub struct Extended {
pub success: Success,
/// The set of danger colors.
pub danger: Danger,
+ /// Whether the palette is dark or not.
+ pub is_dark: bool,
}
/// The built-in light variant of an [`Extended`] palette.
@@ -113,6 +115,7 @@ impl Extended {
palette.background,
palette.text,
),
+ is_dark: is_dark(palette.background),
}
}
}
diff --git a/tiny_skia/src/backend.rs b/tiny_skia/src/backend.rs
index 3e9bd2a5..d1393b4d 100644
--- a/tiny_skia/src/backend.rs
+++ b/tiny_skia/src/backend.rs
@@ -1,5 +1,6 @@
use crate::core::{Background, Color, Gradient, Rectangle, Vector};
use crate::graphics::backend;
+use crate::graphics::text;
use crate::graphics::Viewport;
use crate::primitive::{self, Primitive};
@@ -444,6 +445,35 @@ impl Backend {
clip_mask,
);
}
+ Primitive::RawText(text::Raw {
+ buffer,
+ position,
+ color,
+ clip_bounds: text_clip_bounds,
+ }) => {
+ let Some(buffer) = buffer.upgrade() else {
+ return;
+ };
+
+ let physical_bounds =
+ (*text_clip_bounds + translation) * scale_factor;
+
+ if !clip_bounds.intersects(&physical_bounds) {
+ return;
+ }
+
+ let clip_mask = (!physical_bounds.is_within(&clip_bounds))
+ .then_some(clip_mask as &_);
+
+ self.text_pipeline.draw_raw(
+ &buffer,
+ *position + translation,
+ *color,
+ scale_factor,
+ pixels,
+ clip_mask,
+ );
+ }
#[cfg(feature = "image")]
Primitive::Image {
handle,
@@ -513,7 +543,6 @@ impl Backend {
path,
paint,
rule,
- transform,
}) => {
let bounds = path.bounds();
@@ -536,9 +565,11 @@ impl Backend {
path,
paint,
*rule,
- transform
- .post_translate(translation.x, translation.y)
- .post_scale(scale_factor, scale_factor),
+ tiny_skia::Transform::from_translate(
+ translation.x,
+ translation.y,
+ )
+ .post_scale(scale_factor, scale_factor),
clip_mask,
);
}
@@ -546,7 +577,6 @@ impl Backend {
path,
paint,
stroke,
- transform,
}) => {
let bounds = path.bounds();
@@ -569,9 +599,11 @@ impl Backend {
path,
paint,
stroke,
- transform
- .post_translate(translation.x, translation.y)
- .post_scale(scale_factor, scale_factor),
+ tiny_skia::Transform::from_translate(
+ translation.x,
+ translation.y,
+ )
+ .post_scale(scale_factor, scale_factor),
clip_mask,
);
}
diff --git a/tiny_skia/src/geometry.rs b/tiny_skia/src/geometry.rs
index 5f28b737..74a08d38 100644
--- a/tiny_skia/src/geometry.rs
+++ b/tiny_skia/src/geometry.rs
@@ -1,4 +1,5 @@
-use crate::core::{Point, Rectangle, Size, Vector};
+use crate::core::text::LineHeight;
+use crate::core::{Pixels, Point, Rectangle, Size, Vector};
use crate::graphics::geometry::fill::{self, Fill};
use crate::graphics::geometry::stroke::{self, Stroke};
use crate::graphics::geometry::{Path, Style, Text};
@@ -39,17 +40,22 @@ impl Frame {
}
pub fn fill(&mut self, path: &Path, fill: impl Into<Fill>) {
- let Some(path) = convert_path(path) else {
+ let Some(path) =
+ convert_path(path).and_then(|path| path.transform(self.transform))
+ else {
return;
};
+
let fill = fill.into();
+ let mut paint = into_paint(fill.style);
+ paint.shader.transform(self.transform);
+
self.primitives
.push(Primitive::Custom(primitive::Custom::Fill {
path,
- paint: into_paint(fill.style),
+ paint,
rule: into_fill_rule(fill.rule),
- transform: self.transform,
}));
}
@@ -59,76 +65,111 @@ impl Frame {
size: Size,
fill: impl Into<Fill>,
) {
- let Some(path) = convert_path(&Path::rectangle(top_left, size)) else {
+ let Some(path) = convert_path(&Path::rectangle(top_left, size))
+ .and_then(|path| path.transform(self.transform))
+ else {
return;
};
+
let fill = fill.into();
+ let mut paint = tiny_skia::Paint {
+ anti_alias: false,
+ ..into_paint(fill.style)
+ };
+ paint.shader.transform(self.transform);
+
self.primitives
.push(Primitive::Custom(primitive::Custom::Fill {
path,
- paint: tiny_skia::Paint {
- anti_alias: false,
- ..into_paint(fill.style)
- },
+ paint,
rule: into_fill_rule(fill.rule),
- transform: self.transform,
}));
}
pub fn stroke<'a>(&mut self, path: &Path, stroke: impl Into<Stroke<'a>>) {
- let Some(path) = convert_path(path) else {
+ let Some(path) =
+ convert_path(path).and_then(|path| path.transform(self.transform))
+ else {
return;
};
let stroke = stroke.into();
let skia_stroke = into_stroke(&stroke);
+ let mut paint = into_paint(stroke.style);
+ paint.shader.transform(self.transform);
+
self.primitives
.push(Primitive::Custom(primitive::Custom::Stroke {
path,
- paint: into_paint(stroke.style),
+ paint,
stroke: skia_stroke,
- transform: self.transform,
}));
}
pub fn fill_text(&mut self, text: impl Into<Text>) {
let text = text.into();
- let position = if self.transform.is_identity() {
- text.position
+ let (scale_x, scale_y) = self.transform.get_scale();
+
+ if self.transform.is_scale_translate()
+ && scale_x == scale_y
+ && scale_x > 0.0
+ && scale_y > 0.0
+ {
+ let (position, size, line_height) = if self.transform.is_identity()
+ {
+ (text.position, text.size, text.line_height)
+ } else {
+ let mut position = [tiny_skia::Point {
+ x: text.position.x,
+ y: text.position.y,
+ }];
+
+ self.transform.map_points(&mut position);
+
+ let size = text.size.0 * scale_y;
+
+ let line_height = match text.line_height {
+ LineHeight::Absolute(size) => {
+ LineHeight::Absolute(Pixels(size.0 * scale_y))
+ }
+ LineHeight::Relative(factor) => {
+ LineHeight::Relative(factor)
+ }
+ };
+
+ (
+ Point::new(position[0].x, position[0].y),
+ size.into(),
+ line_height,
+ )
+ };
+
+ let bounds = Rectangle {
+ x: position.x,
+ y: position.y,
+ width: f32::INFINITY,
+ height: f32::INFINITY,
+ };
+
+ // TODO: Honor layering!
+ self.primitives.push(Primitive::Text {
+ content: text.content,
+ bounds,
+ color: text.color,
+ size,
+ line_height,
+ font: text.font,
+ horizontal_alignment: text.horizontal_alignment,
+ vertical_alignment: text.vertical_alignment,
+ shaping: text.shaping,
+ clip_bounds: Rectangle::with_size(Size::INFINITY),
+ });
} else {
- let mut transformed = [tiny_skia::Point {
- x: text.position.x,
- y: text.position.y,
- }];
-
- self.transform.map_points(&mut transformed);
-
- Point::new(transformed[0].x, transformed[0].y)
- };
-
- let bounds = Rectangle {
- x: position.x,
- y: position.y,
- width: f32::INFINITY,
- height: f32::INFINITY,
- };
-
- // TODO: Use vectorial text instead of primitive
- self.primitives.push(Primitive::Text {
- content: text.content,
- bounds,
- color: text.color,
- size: text.size,
- line_height: text.line_height,
- font: text.font,
- horizontal_alignment: text.horizontal_alignment,
- vertical_alignment: text.vertical_alignment,
- shaping: text.shaping,
- clip_bounds: Rectangle::with_size(Size::INFINITY),
- });
+ text.draw_with(|path, color| self.fill(&path, color));
+ }
}
pub fn push_transform(&mut self) {
diff --git a/tiny_skia/src/primitive.rs b/tiny_skia/src/primitive.rs
index 0ed24969..7718d542 100644
--- a/tiny_skia/src/primitive.rs
+++ b/tiny_skia/src/primitive.rs
@@ -13,8 +13,6 @@ pub enum Custom {
paint: tiny_skia::Paint<'static>,
/// The fill rule to follow.
rule: tiny_skia::FillRule,
- /// The transform to apply to the path.
- transform: tiny_skia::Transform,
},
/// A path stroked with some paint.
Stroke {
@@ -24,8 +22,6 @@ pub enum Custom {
paint: tiny_skia::Paint<'static>,
/// The stroke settings.
stroke: tiny_skia::Stroke,
- /// The transform to apply to the path.
- transform: tiny_skia::Transform,
},
}
diff --git a/tiny_skia/src/text.rs b/tiny_skia/src/text.rs
index 70e95d01..9413e311 100644
--- a/tiny_skia/src/text.rs
+++ b/tiny_skia/src/text.rs
@@ -1,7 +1,6 @@
use crate::core::alignment;
use crate::core::text::{LineHeight, Shaping};
-use crate::core::{Color, Font, Pixels, Point, Rectangle};
-use crate::graphics::color;
+use crate::core::{Color, Font, Pixels, Point, Rectangle, Size};
use crate::graphics::text::cache::{self, Cache};
use crate::graphics::text::editor;
use crate::graphics::text::font_system;
@@ -149,6 +148,33 @@ impl Pipeline {
);
}
+ pub fn draw_raw(
+ &mut self,
+ buffer: &cosmic_text::Buffer,
+ position: Point,
+ color: Color,
+ scale_factor: f32,
+ pixels: &mut tiny_skia::PixmapMut<'_>,
+ clip_mask: Option<&tiny_skia::Mask>,
+ ) {
+ let mut font_system = font_system().write().expect("Write font system");
+
+ let (width, height) = buffer.size();
+
+ draw(
+ font_system.raw(),
+ &mut self.glyph_cache,
+ buffer,
+ Rectangle::new(position, Size::new(width, height)),
+ color,
+ alignment::Horizontal::Left,
+ alignment::Vertical::Top,
+ scale_factor,
+ pixels,
+ clip_mask,
+ );
+ }
+
pub fn trim_cache(&mut self) {
self.cache.get_mut().trim();
self.glyph_cache.trim();
@@ -217,18 +243,7 @@ fn draw(
fn from_color(color: cosmic_text::Color) -> Color {
let [r, g, b, a] = color.as_rgba();
- if color::GAMMA_CORRECTION {
- // `cosmic_text::Color` is linear RGB in this case, so we
- // need to convert back to sRGB
- Color::from_linear_rgba(
- r as f32 / 255.0,
- g as f32 / 255.0,
- b as f32 / 255.0,
- a as f32 / 255.0,
- )
- } else {
- Color::from_rgba8(r, g, b, a as f32 / 255.0)
- }
+ Color::from_rgba8(r, g, b, a as f32 / 255.0)
}
#[derive(Debug, Clone, Default)]
diff --git a/wgpu/src/geometry.rs b/wgpu/src/geometry.rs
index e0bff67e..4d7f443e 100644
--- a/wgpu/src/geometry.rs
+++ b/wgpu/src/geometry.rs
@@ -1,5 +1,6 @@
//! Build and draw geometry.
-use crate::core::{Point, Rectangle, Size, Vector};
+use crate::core::text::LineHeight;
+use crate::core::{Pixels, Point, Rectangle, Size, Vector};
use crate::graphics::color;
use crate::graphics::geometry::fill::{self, Fill};
use crate::graphics::geometry::{
@@ -115,19 +116,31 @@ struct Transforms {
}
#[derive(Debug, Clone, Copy)]
-struct Transform {
- raw: lyon::math::Transform,
- is_identity: bool,
-}
+struct Transform(lyon::math::Transform);
impl Transform {
- /// Transforms the given [Point] by the transformation matrix.
- fn transform_point(&self, point: &mut Point) {
+ fn is_identity(&self) -> bool {
+ self.0 == lyon::math::Transform::identity()
+ }
+
+ fn is_scale_translation(&self) -> bool {
+ self.0.m12.abs() < 2.0 * f32::EPSILON
+ && self.0.m21.abs() < 2.0 * f32::EPSILON
+ }
+
+ fn scale(&self) -> (f32, f32) {
+ (self.0.m11, self.0.m22)
+ }
+
+ fn transform_point(&self, point: Point) -> Point {
let transformed = self
- .raw
+ .0
.transform_point(euclid::Point2D::new(point.x, point.y));
- point.x = transformed.x;
- point.y = transformed.y;
+
+ Point {
+ x: transformed.x,
+ y: transformed.y,
+ }
}
fn transform_style(&self, style: Style) -> Style {
@@ -142,8 +155,8 @@ impl Transform {
fn transform_gradient(&self, mut gradient: Gradient) -> Gradient {
match &mut gradient {
Gradient::Linear(linear) => {
- self.transform_point(&mut linear.start);
- self.transform_point(&mut linear.end);
+ linear.start = self.transform_point(linear.start);
+ linear.end = self.transform_point(linear.end);
}
}
@@ -163,10 +176,7 @@ impl Frame {
primitives: Vec::new(),
transforms: Transforms {
previous: Vec::new(),
- current: Transform {
- raw: lyon::math::Transform::identity(),
- is_identity: true,
- },
+ current: Transform(lyon::math::Transform::identity()),
},
fill_tessellator: tessellation::FillTessellator::new(),
stroke_tessellator: tessellation::StrokeTessellator::new(),
@@ -209,14 +219,14 @@ impl Frame {
let options = tessellation::FillOptions::default()
.with_fill_rule(into_fill_rule(rule));
- if self.transforms.current.is_identity {
+ if self.transforms.current.is_identity() {
self.fill_tessellator.tessellate_path(
path.raw(),
&options,
buffer.as_mut(),
)
} else {
- let path = path.transform(&self.transforms.current.raw);
+ let path = path.transform(&self.transforms.current.0);
self.fill_tessellator.tessellate_path(
path.raw(),
@@ -241,13 +251,14 @@ impl Frame {
.buffers
.get_fill(&self.transforms.current.transform_style(style));
- let top_left =
- self.transforms.current.raw.transform_point(
- lyon::math::Point::new(top_left.x, top_left.y),
- );
+ let top_left = self
+ .transforms
+ .current
+ .0
+ .transform_point(lyon::math::Point::new(top_left.x, top_left.y));
let size =
- self.transforms.current.raw.transform_vector(
+ self.transforms.current.0.transform_vector(
lyon::math::Vector::new(size.width, size.height),
);
@@ -284,14 +295,14 @@ impl Frame {
Cow::Owned(dashed(path, stroke.line_dash))
};
- if self.transforms.current.is_identity {
+ if self.transforms.current.is_identity() {
self.stroke_tessellator.tessellate_path(
path.raw(),
&options,
buffer.as_mut(),
)
} else {
- let path = path.transform(&self.transforms.current.raw);
+ let path = path.transform(&self.transforms.current.0);
self.stroke_tessellator.tessellate_path(
path.raw(),
@@ -318,36 +329,57 @@ impl Frame {
pub fn fill_text(&mut self, text: impl Into<Text>) {
let text = text.into();
- let position = if self.transforms.current.is_identity {
- text.position
- } else {
- let transformed = self.transforms.current.raw.transform_point(
- lyon::math::Point::new(text.position.x, text.position.y),
- );
-
- Point::new(transformed.x, transformed.y)
- };
-
- let bounds = Rectangle {
- x: position.x,
- y: position.y,
- width: f32::INFINITY,
- height: f32::INFINITY,
- };
+ let (scale_x, scale_y) = self.transforms.current.scale();
+
+ if self.transforms.current.is_scale_translation()
+ && scale_x == scale_y
+ && scale_x > 0.0
+ && scale_y > 0.0
+ {
+ let (position, size, line_height) =
+ if self.transforms.current.is_identity() {
+ (text.position, text.size, text.line_height)
+ } else {
+ let position =
+ self.transforms.current.transform_point(text.position);
+
+ let size = Pixels(text.size.0 * scale_y);
+
+ let line_height = match text.line_height {
+ LineHeight::Absolute(size) => {
+ LineHeight::Absolute(Pixels(size.0 * scale_y))
+ }
+ LineHeight::Relative(factor) => {
+ LineHeight::Relative(factor)
+ }
+ };
- // TODO: Use vectorial text instead of primitive
- self.primitives.push(Primitive::Text {
- content: text.content,
- bounds,
- color: text.color,
- size: text.size,
- line_height: text.line_height,
- font: text.font,
- horizontal_alignment: text.horizontal_alignment,
- vertical_alignment: text.vertical_alignment,
- shaping: text.shaping,
- clip_bounds: Rectangle::with_size(Size::INFINITY),
- });
+ (position, size, line_height)
+ };
+
+ let bounds = Rectangle {
+ x: position.x,
+ y: position.y,
+ width: f32::INFINITY,
+ height: f32::INFINITY,
+ };
+
+ // TODO: Honor layering!
+ self.primitives.push(Primitive::Text {
+ content: text.content,
+ bounds,
+ color: text.color,
+ size,
+ line_height,
+ font: text.font,
+ horizontal_alignment: text.horizontal_alignment,
+ vertical_alignment: text.vertical_alignment,
+ shaping: text.shaping,
+ clip_bounds: Rectangle::with_size(Size::INFINITY),
+ });
+ } else {
+ text.draw_with(|path, color| self.fill(&path, color));
+ }
}
/// Stores the current transform of the [`Frame`] and executes the given
@@ -423,26 +455,24 @@ impl Frame {
/// Applies a translation to the current transform of the [`Frame`].
#[inline]
pub fn translate(&mut self, translation: Vector) {
- self.transforms.current.raw = self
- .transforms
- .current
- .raw
- .pre_translate(lyon::math::Vector::new(
- translation.x,
- translation.y,
- ));
- self.transforms.current.is_identity = false;
+ self.transforms.current.0 =
+ self.transforms
+ .current
+ .0
+ .pre_translate(lyon::math::Vector::new(
+ translation.x,
+ translation.y,
+ ));
}
/// Applies a rotation in radians to the current transform of the [`Frame`].
#[inline]
pub fn rotate(&mut self, angle: f32) {
- self.transforms.current.raw = self
+ self.transforms.current.0 = self
.transforms
.current
- .raw
+ .0
.pre_rotate(lyon::math::Angle::radians(angle));
- self.transforms.current.is_identity = false;
}
/// Applies a uniform scaling to the current transform of the [`Frame`].
@@ -458,9 +488,8 @@ impl Frame {
pub fn scale_nonuniform(&mut self, scale: impl Into<Vector>) {
let scale = scale.into();
- self.transforms.current.raw =
- self.transforms.current.raw.pre_scale(scale.x, scale.y);
- self.transforms.current.is_identity = false;
+ self.transforms.current.0 =
+ self.transforms.current.0.pre_scale(scale.x, scale.y);
}
/// Produces the [`Primitive`] representing everything drawn on the [`Frame`].
diff --git a/wgpu/src/layer.rs b/wgpu/src/layer.rs
index 557a7633..4ad12a88 100644
--- a/wgpu/src/layer.rs
+++ b/wgpu/src/layer.rs
@@ -177,6 +177,21 @@ impl<'a> Layer<'a> {
clip_bounds: *clip_bounds + translation,
}));
}
+ graphics::Primitive::RawText(graphics::text::Raw {
+ buffer,
+ position,
+ color,
+ clip_bounds,
+ }) => {
+ let layer = &mut layers[current_layer];
+
+ layer.text.push(Text::Raw(graphics::text::Raw {
+ buffer: buffer.clone(),
+ position: *position + translation,
+ color: *color,
+ clip_bounds: *clip_bounds + translation,
+ }));
+ }
Primitive::Quad {
bounds,
background,
diff --git a/wgpu/src/layer/text.rs b/wgpu/src/layer/text.rs
index df2f2875..37ee5247 100644
--- a/wgpu/src/layer/text.rs
+++ b/wgpu/src/layer/text.rs
@@ -1,6 +1,7 @@
use crate::core::alignment;
use crate::core::text;
use crate::core::{Color, Font, Pixels, Point, Rectangle};
+use crate::graphics;
use crate::graphics::text::editor;
use crate::graphics::text::paragraph;
@@ -23,8 +24,10 @@ pub enum Text<'a> {
color: Color,
clip_bounds: Rectangle,
},
- /// A cached text.
+ /// Some cached text.
Cached(Cached<'a>),
+ /// Some raw text.
+ Raw(graphics::text::Raw),
}
#[derive(Debug, Clone)]
diff --git a/wgpu/src/primitive/pipeline.rs b/wgpu/src/primitive/pipeline.rs
index 302e38f6..c8e45458 100644
--- a/wgpu/src/primitive/pipeline.rs
+++ b/wgpu/src/primitive/pipeline.rs
@@ -82,7 +82,7 @@ impl<Theme> Renderer for crate::Renderer<Theme> {
/// Stores custom, user-provided pipelines.
#[derive(Default, Debug)]
pub struct Storage {
- pipelines: HashMap<TypeId, Box<dyn Any>>,
+ pipelines: HashMap<TypeId, Box<dyn Any + Send>>,
}
impl Storage {
@@ -92,7 +92,7 @@ impl Storage {
}
/// Inserts the pipeline `T` in to [`Storage`].
- pub fn store<T: 'static>(&mut self, pipeline: T) {
+ pub fn store<T: 'static + Send>(&mut self, pipeline: T) {
let _ = self.pipelines.insert(TypeId::of::<T>(), Box::new(pipeline));
}
diff --git a/wgpu/src/text.rs b/wgpu/src/text.rs
index 888b1924..dca09cb8 100644
--- a/wgpu/src/text.rs
+++ b/wgpu/src/text.rs
@@ -7,6 +7,7 @@ use crate::layer::Text;
use std::borrow::Cow;
use std::cell::RefCell;
+use std::sync::Arc;
#[allow(missing_debug_implementations)]
pub struct Pipeline {
@@ -76,6 +77,7 @@ impl Pipeline {
Paragraph(Paragraph),
Editor(Editor),
Cache(cache::KeyHash),
+ Raw(Arc<glyphon::Buffer>),
}
let allocations: Vec<_> = sections
@@ -107,6 +109,7 @@ impl Pipeline {
Some(Allocation::Cache(key))
}
+ Text::Raw(text) => text.buffer.upgrade().map(Allocation::Raw),
})
.collect();
@@ -185,6 +188,25 @@ impl Pipeline {
text.clip_bounds,
)
}
+ Text::Raw(text) => {
+ let Some(Allocation::Raw(buffer)) = allocation else {
+ return None;
+ };
+
+ let (width, height) = buffer.size();
+
+ (
+ buffer.as_ref(),
+ Rectangle::new(
+ text.position,
+ Size::new(width, height),
+ ),
+ alignment::Horizontal::Left,
+ alignment::Vertical::Top,
+ text.color,
+ text.clip_bounds,
+ )
+ }
};
let bounds = bounds * scale_factor;
diff --git a/widget/src/button.rs b/widget/src/button.rs
index 384a3156..0ebb8dcc 100644
--- a/widget/src/button.rs
+++ b/widget/src/button.rs
@@ -10,8 +10,8 @@ use crate::core::touch;
use crate::core::widget::tree::{self, Tree};
use crate::core::widget::Operation;
use crate::core::{
- Background, Clipboard, Color, Element, Layout, Length, Padding, Point,
- Rectangle, Shell, Vector, Widget,
+ Background, Clipboard, Color, Element, Layout, Length, Padding, Rectangle,
+ Shell, Size, Vector, Widget,
};
pub use iced_style::button::{Appearance, StyleSheet};
@@ -71,11 +71,14 @@ where
{
/// Creates a new [`Button`] with the given content.
pub fn new(content: impl Into<Element<'a, Message, Renderer>>) -> Self {
+ let content = content.into();
+ let size = content.as_widget().size_hint();
+
Button {
- content: content.into(),
+ content,
on_press: None,
- width: Length::Shrink,
- height: Length::Shrink,
+ width: size.width.fluid(),
+ height: size.height.fluid(),
padding: Padding::new(5.0),
style: <Renderer::Theme as StyleSheet>::Style::default(),
}
@@ -149,12 +152,11 @@ where
tree.diff_children(std::slice::from_ref(&self.content));
}
- fn width(&self) -> Length {
- self.width
- }
-
- fn height(&self) -> Length {
- self.height
+ fn size(&self) -> Size<Length> {
+ Size {
+ width: self.width,
+ height: self.height,
+ }
}
fn layout(
@@ -431,15 +433,7 @@ pub fn layout(
padding: Padding,
layout_content: impl FnOnce(&layout::Limits) -> layout::Node,
) -> layout::Node {
- let limits = limits.width(width).height(height);
-
- let mut content = layout_content(&limits.pad(padding));
- let padding = padding.fit(content.size(), limits.max());
- let size = limits.pad(padding).resolve(content.size()).pad(padding);
-
- content.move_to(Point::new(padding.left, padding.top));
-
- layout::Node::with_children(size, vec![content])
+ layout::padded(limits, width, height, padding, layout_content)
}
/// Returns the [`mouse::Interaction`] of a [`Button`].
diff --git a/widget/src/canvas.rs b/widget/src/canvas.rs
index 390f4d92..4e42a671 100644
--- a/widget/src/canvas.rs
+++ b/widget/src/canvas.rs
@@ -14,8 +14,9 @@ use crate::core::layout::{self, Layout};
use crate::core::mouse;
use crate::core::renderer;
use crate::core::widget::tree::{self, Tree};
-use crate::core::{Clipboard, Element, Shell, Widget};
-use crate::core::{Length, Rectangle, Size, Vector};
+use crate::core::{
+ Clipboard, Element, Length, Rectangle, Shell, Size, Vector, Widget,
+};
use crate::graphics::geometry;
use std::marker::PhantomData;
@@ -119,12 +120,11 @@ where
tree::State::new(P::State::default())
}
- fn width(&self) -> Length {
- self.width
- }
-
- fn height(&self) -> Length {
- self.height
+ fn size(&self) -> Size<Length> {
+ Size {
+ width: self.width,
+ height: self.height,
+ }
}
fn layout(
@@ -133,10 +133,7 @@ where
_renderer: &Renderer,
limits: &layout::Limits,
) -> layout::Node {
- let limits = limits.width(self.width).height(self.height);
- let size = limits.resolve(Size::ZERO);
-
- layout::Node::new(size)
+ layout::atomic(limits, self.width, self.height)
}
fn on_event(
diff --git a/widget/src/canvas/event.rs b/widget/src/canvas/event.rs
index 1288365f..a8eb47f7 100644
--- a/widget/src/canvas/event.rs
+++ b/widget/src/canvas/event.rs
@@ -8,7 +8,7 @@ pub use crate::core::event::Status;
/// A [`Canvas`] event.
///
/// [`Canvas`]: crate::Canvas
-#[derive(Debug, Clone, Copy, PartialEq)]
+#[derive(Debug, Clone, PartialEq)]
pub enum Event {
/// A mouse event.
Mouse(mouse::Event),
diff --git a/widget/src/checkbox.rs b/widget/src/checkbox.rs
index a0d9559b..0353b3ad 100644
--- a/widget/src/checkbox.rs
+++ b/widget/src/checkbox.rs
@@ -174,12 +174,11 @@ where
tree::State::new(widget::text::State::<Renderer::Paragraph>::default())
}
- fn width(&self) -> Length {
- self.width
- }
-
- fn height(&self) -> Length {
- Length::Shrink
+ fn size(&self) -> Size<Length> {
+ Size {
+ width: self.width,
+ height: Length::Shrink,
+ }
}
fn layout(
diff --git a/widget/src/column.rs b/widget/src/column.rs
index abb522be..d6eea84b 100644
--- a/widget/src/column.rs
+++ b/widget/src/column.rs
@@ -7,7 +7,7 @@ use crate::core::renderer;
use crate::core::widget::{Operation, Tree};
use crate::core::{
Alignment, Clipboard, Element, Layout, Length, Padding, Pixels, Rectangle,
- Shell, Widget,
+ Shell, Size, Widget,
};
/// A container that distributes its contents vertically.
@@ -22,16 +22,12 @@ pub struct Column<'a, Message, Renderer = crate::Renderer> {
children: Vec<Element<'a, Message, Renderer>>,
}
-impl<'a, Message, Renderer> Column<'a, Message, Renderer> {
+impl<'a, Message, Renderer> Column<'a, Message, Renderer>
+where
+ Renderer: crate::core::Renderer,
+{
/// Creates an empty [`Column`].
pub fn new() -> Self {
- Self::with_children(Vec::new())
- }
-
- /// Creates a [`Column`] with the given elements.
- pub fn with_children(
- children: Vec<Element<'a, Message, Renderer>>,
- ) -> Self {
Column {
spacing: 0.0,
padding: Padding::ZERO,
@@ -39,10 +35,17 @@ impl<'a, Message, Renderer> Column<'a, Message, Renderer> {
height: Length::Shrink,
max_width: f32::INFINITY,
align_items: Alignment::Start,
- children,
+ children: Vec::new(),
}
}
+ /// Creates a [`Column`] with the given elements.
+ pub fn with_children(
+ children: impl IntoIterator<Item = Element<'a, Message, Renderer>>,
+ ) -> Self {
+ children.into_iter().fold(Self::new(), Self::push)
+ }
+
/// Sets the vertical spacing _between_ elements.
///
/// Custom margins per element do not exist in iced. You should use this
@@ -88,12 +91,26 @@ impl<'a, Message, Renderer> Column<'a, Message, Renderer> {
mut self,
child: impl Into<Element<'a, Message, Renderer>>,
) -> Self {
- self.children.push(child.into());
+ let child = child.into();
+ let size = child.as_widget().size_hint();
+
+ if size.width.is_fill() {
+ self.width = Length::Fill;
+ }
+
+ if size.height.is_fill() {
+ self.height = Length::Fill;
+ }
+
+ self.children.push(child);
self
}
}
-impl<'a, Message, Renderer> Default for Column<'a, Message, Renderer> {
+impl<'a, Message, Renderer> Default for Column<'a, Message, Renderer>
+where
+ Renderer: crate::core::Renderer,
+{
fn default() -> Self {
Self::new()
}
@@ -112,12 +129,11 @@ where
tree.diff_children(&self.children);
}
- fn width(&self) -> Length {
- self.width
- }
-
- fn height(&self) -> Length {
- self.height
+ fn size(&self) -> Size<Length> {
+ Size {
+ width: self.width,
+ height: self.height,
+ }
}
fn layout(
@@ -126,15 +142,14 @@ where
renderer: &Renderer,
limits: &layout::Limits,
) -> layout::Node {
- let limits = limits
- .max_width(self.max_width)
- .width(self.width)
- .height(self.height);
+ let limits = limits.max_width(self.max_width);
layout::flex::resolve(
layout::flex::Axis::Vertical,
renderer,
&limits,
+ self.width,
+ self.height,
self.padding,
self.spacing,
self.align_items,
diff --git a/widget/src/combo_box.rs b/widget/src/combo_box.rs
index 31ec27fc..73beeac3 100644
--- a/widget/src/combo_box.rs
+++ b/widget/src/combo_box.rs
@@ -1,6 +1,7 @@
//! Display a dropdown list of searchable and selectable options.
use crate::core::event::{self, Event};
use crate::core::keyboard;
+use crate::core::keyboard::key;
use crate::core::layout::{self, Layout};
use crate::core::mouse;
use crate::core::overlay;
@@ -8,7 +9,9 @@ use crate::core::renderer;
use crate::core::text;
use crate::core::time::Instant;
use crate::core::widget::{self, Widget};
-use crate::core::{Clipboard, Element, Length, Padding, Rectangle, Shell};
+use crate::core::{
+ Clipboard, Element, Length, Padding, Rectangle, Shell, Size,
+};
use crate::overlay::menu;
use crate::text::LineHeight;
use crate::{container, scrollable, text_input, TextInput};
@@ -297,12 +300,8 @@ where
+ scrollable::StyleSheet
+ menu::StyleSheet,
{
- fn width(&self) -> Length {
- Widget::<TextInputEvent, Renderer>::width(&self.text_input)
- }
-
- fn height(&self) -> Length {
- Widget::<TextInputEvent, Renderer>::height(&self.text_input)
+ fn size(&self) -> Size<Length> {
+ Widget::<TextInputEvent, Renderer>::size(&self.text_input)
}
fn layout(
@@ -438,14 +437,14 @@ where
}
if let Event::Keyboard(keyboard::Event::KeyPressed {
- key_code,
+ key: keyboard::Key::Named(named_key),
modifiers,
..
}) = event
{
let shift_modifer = modifiers.shift();
- match (key_code, shift_modifer) {
- (keyboard::KeyCode::Enter, _) => {
+ match (named_key, shift_modifer) {
+ (key::Named::Enter, _) => {
if let Some(index) = &menu.hovered_option {
if let Some(option) =
state.filtered_options.options.get(*index)
@@ -457,8 +456,7 @@ where
event_status = event::Status::Captured;
}
- (keyboard::KeyCode::Up, _)
- | (keyboard::KeyCode::Tab, true) => {
+ (key::Named::ArrowUp, _) | (key::Named::Tab, true) => {
if let Some(index) = &mut menu.hovered_option {
if *index == 0 {
*index = state
@@ -494,8 +492,8 @@ where
event_status = event::Status::Captured;
}
- (keyboard::KeyCode::Down, _)
- | (keyboard::KeyCode::Tab, false)
+ (key::Named::ArrowDown, _)
+ | (key::Named::Tab, false)
if !modifiers.shift() =>
{
if let Some(index) = &mut menu.hovered_option {
diff --git a/widget/src/container.rs b/widget/src/container.rs
index 5dd7705b..cffb0458 100644
--- a/widget/src/container.rs
+++ b/widget/src/container.rs
@@ -46,17 +46,20 @@ where
where
T: Into<Element<'a, Message, Renderer>>,
{
+ let content = content.into();
+ let size = content.as_widget().size_hint();
+
Container {
id: None,
padding: Padding::ZERO,
- width: Length::Shrink,
- height: Length::Shrink,
+ width: size.width.fluid(),
+ height: size.height.fluid(),
max_width: f32::INFINITY,
max_height: f32::INFINITY,
horizontal_alignment: alignment::Horizontal::Left,
vertical_alignment: alignment::Vertical::Top,
style: Default::default(),
- content: content.into(),
+ content,
}
}
@@ -152,12 +155,11 @@ where
self.content.as_widget().diff(tree);
}
- fn width(&self) -> Length {
- self.width
- }
-
- fn height(&self) -> Length {
- self.height
+ fn size(&self) -> Size<Length> {
+ Size {
+ width: self.width,
+ height: self.height,
+ }
}
fn layout(
@@ -311,25 +313,20 @@ pub fn layout(
vertical_alignment: alignment::Vertical,
layout_content: impl FnOnce(&layout::Limits) -> layout::Node,
) -> layout::Node {
- let limits = limits
- .loose()
- .max_width(max_width)
- .max_height(max_height)
- .width(width)
- .height(height);
-
- let mut content = layout_content(&limits.pad(padding).loose());
- let padding = padding.fit(content.size(), limits.max());
- let size = limits.pad(padding).resolve(content.size());
-
- content.move_to(Point::new(padding.left, padding.top));
- content.align(
- Alignment::from(horizontal_alignment),
- Alignment::from(vertical_alignment),
- size,
- );
-
- layout::Node::with_children(size.pad(padding), vec![content])
+ layout::positioned(
+ &limits.max_width(max_width).max_height(max_height),
+ width,
+ height,
+ padding,
+ |limits| layout_content(&limits.loose()),
+ |content, size| {
+ content.align(
+ Alignment::from(horizontal_alignment),
+ Alignment::from(vertical_alignment),
+ size,
+ )
+ },
+ )
}
/// Draws the background of a [`Container`] given its [`Appearance`] and its `bounds`.
diff --git a/widget/src/helpers.rs b/widget/src/helpers.rs
index 115198fb..498dd76c 100644
--- a/widget/src/helpers.rs
+++ b/widget/src/helpers.rs
@@ -34,7 +34,7 @@ macro_rules! column {
$crate::Column::new()
);
($($x:expr),+ $(,)?) => (
- $crate::Column::with_children(vec![$($crate::core::Element::from($x)),+])
+ $crate::Column::with_children([$($crate::core::Element::from($x)),+])
);
}
@@ -47,7 +47,7 @@ macro_rules! row {
$crate::Row::new()
);
($($x:expr),+ $(,)?) => (
- $crate::Row::with_children(vec![$($crate::core::Element::from($x)),+])
+ $crate::Row::with_children([$($crate::core::Element::from($x)),+])
);
}
@@ -65,9 +65,12 @@ where
}
/// Creates a new [`Column`] with the given children.
-pub fn column<Message, Renderer>(
- children: Vec<Element<'_, Message, Renderer>>,
-) -> Column<'_, Message, Renderer> {
+pub fn column<'a, Message, Renderer>(
+ children: impl IntoIterator<Item = Element<'a, Message, Renderer>>,
+) -> Column<'a, Message, Renderer>
+where
+ Renderer: core::Renderer,
+{
Column::with_children(children)
}
@@ -77,6 +80,7 @@ pub fn keyed_column<'a, Key, Message, Renderer>(
) -> keyed::Column<'a, Key, Message, Renderer>
where
Key: Copy + PartialEq,
+ Renderer: core::Renderer,
{
keyed::Column::with_children(children)
}
@@ -84,9 +88,12 @@ where
/// Creates a new [`Row`] with the given children.
///
/// [`Row`]: crate::Row
-pub fn row<Message, Renderer>(
- children: Vec<Element<'_, Message, Renderer>>,
-) -> Row<'_, Message, Renderer> {
+pub fn row<'a, Message, Renderer>(
+ children: impl IntoIterator<Item = Element<'a, Message, Renderer>>,
+) -> Row<'a, Message, Renderer>
+where
+ Renderer: core::Renderer,
+{
Row::with_children(children)
}
@@ -264,7 +271,7 @@ pub fn pick_list<'a, Message, Renderer, T>(
on_selected: impl Fn(T) -> Message + 'a,
) -> PickList<'a, T, Message, Renderer>
where
- T: ToString + Eq + 'static,
+ T: ToString + PartialEq + 'static,
[T]: ToOwned<Owned = Vec<T>>,
Renderer: core::text::Renderer,
Renderer::Theme: pick_list::StyleSheet
diff --git a/widget/src/image.rs b/widget/src/image.rs
index 67699102..e906ac13 100644
--- a/widget/src/image.rs
+++ b/widget/src/image.rs
@@ -99,7 +99,7 @@ where
};
// The size to be available to the widget prior to `Shrink`ing
- let raw_size = limits.width(width).height(height).resolve(image_size);
+ let raw_size = limits.resolve(width, height, image_size);
// The uncropped size of the image when fit to the bounds above
let full_size = content_fit.fit(image_size, raw_size);
@@ -164,12 +164,11 @@ where
Renderer: image::Renderer<Handle = Handle>,
Handle: Clone + Hash,
{
- fn width(&self) -> Length {
- self.width
- }
-
- fn height(&self) -> Length {
- self.height
+ fn size(&self) -> Size<Length> {
+ Size {
+ width: self.width,
+ height: self.height,
+ }
}
fn layout(
diff --git a/widget/src/image/viewer.rs b/widget/src/image/viewer.rs
index 68015ba8..98080577 100644
--- a/widget/src/image/viewer.rs
+++ b/widget/src/image/viewer.rs
@@ -97,12 +97,11 @@ where
tree::State::new(State::new())
}
- fn width(&self) -> Length {
- self.width
- }
-
- fn height(&self) -> Length {
- self.height
+ fn size(&self) -> Size<Length> {
+ Size {
+ width: self.width,
+ height: self.height,
+ }
}
fn layout(
@@ -113,10 +112,11 @@ where
) -> layout::Node {
let Size { width, height } = renderer.dimensions(&self.handle);
- let mut size = limits
- .width(self.width)
- .height(self.height)
- .resolve(Size::new(width as f32, height as f32));
+ let mut size = limits.resolve(
+ self.width,
+ self.height,
+ Size::new(width as f32, height as f32),
+ );
let expansion_size = if height > width {
self.width
diff --git a/widget/src/keyed/column.rs b/widget/src/keyed/column.rs
index 0ef82407..7f05a81e 100644
--- a/widget/src/keyed/column.rs
+++ b/widget/src/keyed/column.rs
@@ -8,7 +8,7 @@ use crate::core::widget::tree::{self, Tree};
use crate::core::widget::Operation;
use crate::core::{
Alignment, Clipboard, Element, Layout, Length, Padding, Pixels, Rectangle,
- Shell, Widget,
+ Shell, Size, Widget,
};
/// A container that distributes its contents vertically.
@@ -30,26 +30,10 @@ where
impl<'a, Key, Message, Renderer> Column<'a, Key, Message, Renderer>
where
Key: Copy + PartialEq,
+ Renderer: crate::core::Renderer,
{
/// Creates an empty [`Column`].
pub fn new() -> Self {
- Self::with_children(Vec::new())
- }
-
- /// Creates a [`Column`] with the given elements.
- pub fn with_children(
- children: impl IntoIterator<Item = (Key, Element<'a, Message, Renderer>)>,
- ) -> Self {
- let (keys, children) = children.into_iter().fold(
- (Vec::new(), Vec::new()),
- |(mut keys, mut children), (key, child)| {
- keys.push(key);
- children.push(child);
-
- (keys, children)
- },
- );
-
Column {
spacing: 0.0,
padding: Padding::ZERO,
@@ -57,11 +41,20 @@ where
height: Length::Shrink,
max_width: f32::INFINITY,
align_items: Alignment::Start,
- keys,
- children,
+ keys: Vec::new(),
+ children: Vec::new(),
}
}
+ /// Creates a [`Column`] with the given elements.
+ pub fn with_children(
+ children: impl IntoIterator<Item = (Key, Element<'a, Message, Renderer>)>,
+ ) -> Self {
+ children
+ .into_iter()
+ .fold(Self::new(), |column, (key, child)| column.push(key, child))
+ }
+
/// Sets the vertical spacing _between_ elements.
///
/// Custom margins per element do not exist in iced. You should use this
@@ -108,8 +101,19 @@ where
key: Key,
child: impl Into<Element<'a, Message, Renderer>>,
) -> Self {
+ let child = child.into();
+ let size = child.as_widget().size_hint();
+
+ if size.width.is_fill() {
+ self.width = Length::Fill;
+ }
+
+ if size.height.is_fill() {
+ self.height = Length::Fill;
+ }
+
self.keys.push(key);
- self.children.push(child.into());
+ self.children.push(child);
self
}
}
@@ -117,6 +121,7 @@ where
impl<'a, Key, Message, Renderer> Default for Column<'a, Key, Message, Renderer>
where
Key: Copy + PartialEq,
+ Renderer: crate::core::Renderer,
{
fn default() -> Self {
Self::new()
@@ -173,12 +178,11 @@ where
}
}
- fn width(&self) -> Length {
- self.width
- }
-
- fn height(&self) -> Length {
- self.height
+ fn size(&self) -> Size<Length> {
+ Size {
+ width: self.width,
+ height: self.height,
+ }
}
fn layout(
@@ -196,6 +200,8 @@ where
layout::flex::Axis::Vertical,
renderer,
&limits,
+ self.width,
+ self.height,
self.padding,
self.spacing,
self.align_items,
diff --git a/widget/src/lazy.rs b/widget/src/lazy.rs
index 167a055d..e9edbb4c 100644
--- a/widget/src/lazy.rs
+++ b/widget/src/lazy.rs
@@ -142,12 +142,15 @@ where
}
}
- fn width(&self) -> Length {
- self.with_element(|element| element.as_widget().width())
+ fn size(&self) -> Size<Length> {
+ self.with_element(|element| element.as_widget().size())
}
- fn height(&self) -> Length {
- self.with_element(|element| element.as_widget().height())
+ fn size_hint(&self) -> Size<Length> {
+ Size {
+ width: Length::Shrink,
+ height: Length::Shrink,
+ }
}
fn layout(
diff --git a/widget/src/lazy/component.rs b/widget/src/lazy/component.rs
index ad0c3823..3684e0c9 100644
--- a/widget/src/lazy/component.rs
+++ b/widget/src/lazy/component.rs
@@ -244,12 +244,15 @@ where
self.rebuild_element_if_necessary();
}
- fn width(&self) -> Length {
- self.with_element(|element| element.as_widget().width())
+ fn size(&self) -> Size<Length> {
+ self.with_element(|element| element.as_widget().size())
}
- fn height(&self) -> Length {
- self.with_element(|element| element.as_widget().height())
+ fn size_hint(&self) -> Size<Length> {
+ Size {
+ width: Length::Shrink,
+ height: Length::Shrink,
+ }
}
fn layout(
diff --git a/widget/src/lazy/helpers.rs b/widget/src/lazy/helpers.rs
index 8ca9cb86..5dc60d52 100644
--- a/widget/src/lazy/helpers.rs
+++ b/widget/src/lazy/helpers.rs
@@ -6,6 +6,7 @@ use std::hash::Hash;
/// Creates a new [`Lazy`] widget with the given data `Dependency` and a
/// closure that can turn this data into a widget tree.
+#[cfg(feature = "lazy")]
pub fn lazy<'a, Message, Renderer, Dependency, View>(
dependency: Dependency,
view: impl Fn(&Dependency) -> View + 'a,
@@ -19,6 +20,7 @@ where
/// Turns an implementor of [`Component`] into an [`Element`] that can be
/// embedded in any application.
+#[cfg(feature = "lazy")]
pub fn component<'a, C, Message, Renderer>(
component: C,
) -> Element<'a, Message, Renderer>
@@ -37,6 +39,7 @@ where
/// The `view` closure will be provided with the current [`Size`] of
/// the [`Responsive`] widget and, therefore, can be used to build the
/// contents of the widget in a responsive way.
+#[cfg(feature = "lazy")]
pub fn responsive<'a, Message, Renderer>(
f: impl Fn(Size) -> Element<'a, Message, Renderer> + 'a,
) -> Responsive<'a, Message, Renderer>
diff --git a/widget/src/lazy/responsive.rs b/widget/src/lazy/responsive.rs
index 86d37b6c..1df0866f 100644
--- a/widget/src/lazy/responsive.rs
+++ b/widget/src/lazy/responsive.rs
@@ -135,12 +135,11 @@ where
})
}
- fn width(&self) -> Length {
- Length::Fill
- }
-
- fn height(&self) -> Length {
- Length::Fill
+ fn size(&self) -> Size<Length> {
+ Size {
+ width: Length::Fill,
+ height: Length::Fill,
+ }
}
fn layout(
diff --git a/widget/src/mouse_area.rs b/widget/src/mouse_area.rs
index 3a5b01a3..87cac3a7 100644
--- a/widget/src/mouse_area.rs
+++ b/widget/src/mouse_area.rs
@@ -8,7 +8,7 @@ use crate::core::renderer;
use crate::core::touch;
use crate::core::widget::{tree, Operation, Tree};
use crate::core::{
- Clipboard, Element, Layout, Length, Rectangle, Shell, Widget,
+ Clipboard, Element, Layout, Length, Rectangle, Shell, Size, Widget,
};
/// Emit messages on mouse events.
@@ -110,12 +110,8 @@ where
tree.diff_children(std::slice::from_ref(&self.content));
}
- fn width(&self) -> Length {
- self.content.as_widget().width()
- }
-
- fn height(&self) -> Length {
- self.content.as_widget().height()
+ fn size(&self) -> Size<Length> {
+ self.content.as_widget().size()
}
fn layout(
diff --git a/widget/src/overlay/menu.rs b/widget/src/overlay/menu.rs
index e45b44ae..f83eebea 100644
--- a/widget/src/overlay/menu.rs
+++ b/widget/src/overlay/menu.rs
@@ -254,15 +254,14 @@ where
)
.width(self.width);
- let mut node = self.container.layout(self.state, renderer, &limits);
+ let node = self.container.layout(self.state, renderer, &limits);
+ let size = node.size();
node.move_to(if space_below > space_above {
position + Vector::new(0.0, self.target_height)
} else {
- position - Vector::new(0.0, node.size().height)
- });
-
- node
+ position - Vector::new(0.0, size.height)
+ })
}
fn on_event(
@@ -343,12 +342,11 @@ where
Renderer: text::Renderer,
Renderer::Theme: StyleSheet,
{
- fn width(&self) -> Length {
- Length::Fill
- }
-
- fn height(&self) -> Length {
- Length::Shrink
+ fn size(&self) -> Size<Length> {
+ Size {
+ width: Length::Fill,
+ height: Length::Shrink,
+ }
}
fn layout(
@@ -359,7 +357,6 @@ where
) -> layout::Node {
use std::f32;
- let limits = limits.width(Length::Fill).height(Length::Shrink);
let text_size =
self.text_size.unwrap_or_else(|| renderer.default_size());
@@ -372,7 +369,7 @@ where
* self.options.len() as f32,
);
- limits.resolve(intrinsic)
+ limits.resolve(Length::Fill, Length::Shrink, intrinsic)
};
layout::Node::new(size)
diff --git a/widget/src/pane_grid.rs b/widget/src/pane_grid.rs
index 2d25a543..cf1f0455 100644
--- a/widget/src/pane_grid.rs
+++ b/widget/src/pane_grid.rs
@@ -265,12 +265,11 @@ where
}
}
- fn width(&self) -> Length {
- self.width
- }
-
- fn height(&self) -> Length {
- self.height
+ fn size(&self) -> Size<Length> {
+ Size {
+ width: self.width,
+ height: self.height,
+ }
}
fn layout(
@@ -490,8 +489,7 @@ pub fn layout<Renderer, T>(
&layout::Limits,
) -> layout::Node,
) -> layout::Node {
- let limits = limits.width(width).height(height);
- let size = limits.resolve(Size::ZERO);
+ let size = limits.resolve(width, height, Size::ZERO);
let regions = node.pane_regions(spacing, size);
let children = contents
@@ -500,16 +498,14 @@ pub fn layout<Renderer, T>(
let region = regions.get(&pane)?;
let size = Size::new(region.width, region.height);
- let mut node = layout_content(
+ let node = layout_content(
content,
tree,
renderer,
&layout::Limits::new(size, size),
);
- node.move_to(Point::new(region.x, region.y));
-
- Some(node)
+ Some(node.move_to(Point::new(region.x, region.y)))
})
.collect();
@@ -531,6 +527,8 @@ pub fn update<'a, Message, T: Draggable>(
on_drag: &Option<Box<dyn Fn(DragEvent) -> Message + 'a>>,
on_resize: &Option<(f32, Box<dyn Fn(ResizeEvent) -> Message + 'a>)>,
) -> event::Status {
+ const DRAG_DEADBAND_DISTANCE: f32 = 10.0;
+
let mut event_status = event::Status::Ignored;
match event {
@@ -572,7 +570,6 @@ pub fn update<'a, Message, T: Draggable>(
shell,
contents,
on_click,
- on_drag,
);
}
}
@@ -584,7 +581,6 @@ pub fn update<'a, Message, T: Draggable>(
shell,
contents,
on_click,
- on_drag,
);
}
}
@@ -637,7 +633,49 @@ pub fn update<'a, Message, T: Draggable>(
}
Event::Mouse(mouse::Event::CursorMoved { .. })
| Event::Touch(touch::Event::FingerMoved { .. }) => {
- if let Some((_, on_resize)) = on_resize {
+ if let Some((_, origin)) = action.clicked_pane() {
+ if let Some(on_drag) = &on_drag {
+ let bounds = layout.bounds();
+
+ if let Some(cursor_position) = cursor.position_over(bounds)
+ {
+ let mut clicked_region = contents
+ .zip(layout.children())
+ .filter(|(_, layout)| {
+ layout.bounds().contains(cursor_position)
+ });
+
+ if let Some(((pane, content), layout)) =
+ clicked_region.next()
+ {
+ if content
+ .can_be_dragged_at(layout, cursor_position)
+ {
+ let pane_position = layout.position();
+
+ let new_origin = cursor_position
+ - Vector::new(
+ pane_position.x,
+ pane_position.y,
+ );
+
+ if new_origin.distance(origin)
+ > DRAG_DEADBAND_DISTANCE
+ {
+ *action = state::Action::Dragging {
+ pane,
+ origin,
+ };
+
+ shell.publish(on_drag(DragEvent::Picked {
+ pane,
+ }));
+ }
+ }
+ }
+ }
+ }
+ } else if let Some((_, on_resize)) = on_resize {
if let Some((split, _)) = action.picked_split() {
let bounds = layout.bounds();
@@ -712,7 +750,6 @@ fn click_pane<'a, Message, T>(
shell: &mut Shell<'_, Message>,
contents: impl Iterator<Item = (Pane, T)>,
on_click: &Option<Box<dyn Fn(Pane) -> Message + 'a>>,
- on_drag: &Option<Box<dyn Fn(DragEvent) -> Message + 'a>>,
) where
T: Draggable,
{
@@ -720,23 +757,15 @@ fn click_pane<'a, Message, T>(
.zip(layout.children())
.filter(|(_, layout)| layout.bounds().contains(cursor_position));
- if let Some(((pane, content), layout)) = clicked_region.next() {
+ if let Some(((pane, _), layout)) = clicked_region.next() {
if let Some(on_click) = &on_click {
shell.publish(on_click(pane));
}
- if let Some(on_drag) = &on_drag {
- if content.can_be_dragged_at(layout, cursor_position) {
- let pane_position = layout.position();
-
- let origin = cursor_position
- - Vector::new(pane_position.x, pane_position.y);
-
- *action = state::Action::Dragging { pane, origin };
-
- shell.publish(on_drag(DragEvent::Picked { pane }));
- }
- }
+ let pane_position = layout.position();
+ let origin =
+ cursor_position - Vector::new(pane_position.x, pane_position.y);
+ *action = state::Action::Clicking { pane, origin };
}
}
@@ -749,7 +778,7 @@ pub fn mouse_interaction(
spacing: f32,
resize_leeway: Option<f32>,
) -> Option<mouse::Interaction> {
- if action.picked_pane().is_some() {
+ if action.clicked_pane().is_some() || action.picked_pane().is_some() {
return Some(mouse::Interaction::Grabbing);
}
diff --git a/widget/src/pane_grid/content.rs b/widget/src/pane_grid/content.rs
index 826ea663..ee00f186 100644
--- a/widget/src/pane_grid/content.rs
+++ b/widget/src/pane_grid/content.rs
@@ -165,7 +165,7 @@ where
let title_bar_size = title_bar_layout.size();
- let mut body_layout = self.body.as_widget().layout(
+ let body_layout = self.body.as_widget().layout(
&mut tree.children[0],
renderer,
&layout::Limits::new(
@@ -177,11 +177,12 @@ where
),
);
- body_layout.move_to(Point::new(0.0, title_bar_size.height));
-
layout::Node::with_children(
max_size,
- vec![title_bar_layout, body_layout],
+ vec![
+ title_bar_layout,
+ body_layout.move_to(Point::new(0.0, title_bar_size.height)),
+ ],
)
} else {
self.body.as_widget().layout(
diff --git a/widget/src/pane_grid/state.rs b/widget/src/pane_grid/state.rs
index 481cd770..5d1fe254 100644
--- a/widget/src/pane_grid/state.rs
+++ b/widget/src/pane_grid/state.rs
@@ -403,6 +403,15 @@ pub enum Action {
///
/// [`PaneGrid`]: super::PaneGrid
Idle,
+ /// A [`Pane`] in the [`PaneGrid`] is being clicked.
+ ///
+ /// [`PaneGrid`]: super::PaneGrid
+ Clicking {
+ /// The [`Pane`] being clicked.
+ pane: Pane,
+ /// The starting [`Point`] of the click interaction.
+ origin: Point,
+ },
/// A [`Pane`] in the [`PaneGrid`] is being dragged.
///
/// [`PaneGrid`]: super::PaneGrid
@@ -432,6 +441,14 @@ impl Action {
}
}
+ /// Returns the current [`Pane`] that is being clicked, if any.
+ pub fn clicked_pane(&self) -> Option<(Pane, Point)> {
+ match *self {
+ Action::Clicking { pane, origin, .. } => Some((pane, origin)),
+ _ => None,
+ }
+ }
+
/// Returns the current [`Split`] that is being dragged, if any.
pub fn picked_split(&self) -> Option<(Split, Axis)> {
match *self {
diff --git a/widget/src/pane_grid/title_bar.rs b/widget/src/pane_grid/title_bar.rs
index f4dbb6b1..eb21b743 100644
--- a/widget/src/pane_grid/title_bar.rs
+++ b/widget/src/pane_grid/title_bar.rs
@@ -217,7 +217,7 @@ where
renderer: &Renderer,
limits: &layout::Limits,
) -> layout::Node {
- let limits = limits.pad(self.padding);
+ let limits = limits.shrink(self.padding);
let max_size = limits.max();
let title_layout = self.content.as_widget().layout(
@@ -228,8 +228,8 @@ where
let title_size = title_layout.size();
- let mut node = if let Some(controls) = &self.controls {
- let mut controls_layout = controls.as_widget().layout(
+ let node = if let Some(controls) = &self.controls {
+ let controls_layout = controls.as_widget().layout(
&mut tree.children[1],
renderer,
&layout::Limits::new(Size::ZERO, max_size),
@@ -240,11 +240,13 @@ where
let height = title_size.height.max(controls_size.height);
- controls_layout.move_to(Point::new(space_before_controls, 0.0));
-
layout::Node::with_children(
Size::new(max_size.width, height),
- vec![title_layout, controls_layout],
+ vec![
+ title_layout,
+ controls_layout
+ .move_to(Point::new(space_before_controls, 0.0)),
+ ],
)
} else {
layout::Node::with_children(
@@ -253,9 +255,7 @@ where
)
};
- node.move_to(Point::new(self.padding.left, self.padding.top));
-
- layout::Node::with_children(node.size().pad(self.padding), vec![node])
+ layout::Node::container(node, self.padding)
}
pub(crate) fn operate(
diff --git a/widget/src/pick_list.rs b/widget/src/pick_list.rs
index 022ca8d9..2e3aab6f 100644
--- a/widget/src/pick_list.rs
+++ b/widget/src/pick_list.rs
@@ -45,7 +45,7 @@ where
impl<'a, T: 'a, Message, Renderer> PickList<'a, T, Message, Renderer>
where
- T: ToString + Eq,
+ T: ToString + PartialEq,
[T]: ToOwned<Owned = Vec<T>>,
Renderer: text::Renderer,
Renderer::Theme: StyleSheet
@@ -145,7 +145,7 @@ where
impl<'a, T: 'a, Message, Renderer> Widget<Message, Renderer>
for PickList<'a, T, Message, Renderer>
where
- T: Clone + ToString + Eq + 'static,
+ T: Clone + ToString + PartialEq + 'static,
[T]: ToOwned<Owned = Vec<T>>,
Message: 'a,
Renderer: text::Renderer + 'a,
@@ -164,12 +164,11 @@ where
tree::State::new(State::<Renderer::Paragraph>::new())
}
- fn width(&self) -> Length {
- self.width
- }
-
- fn height(&self) -> Length {
- Length::Shrink
+ fn size(&self) -> Size<Length> {
+ Size {
+ width: self.width,
+ height: Length::Shrink,
+ }
}
fn layout(
@@ -282,7 +281,7 @@ where
impl<'a, T: 'a, Message, Renderer> From<PickList<'a, T, Message, Renderer>>
for Element<'a, Message, Renderer>
where
- T: Clone + ToString + Eq + 'static,
+ T: Clone + ToString + PartialEq + 'static,
[T]: ToOwned<Owned = Vec<T>>,
Message: 'a,
Renderer: text::Renderer + 'a,
@@ -393,7 +392,6 @@ where
{
use std::f32;
- let limits = limits.width(width).height(Length::Shrink).pad(padding);
let font = font.unwrap_or_else(|| renderer.default_font());
let text_size = text_size.unwrap_or_else(|| renderer.default_size());
@@ -451,7 +449,11 @@ where
f32::from(text_line_height.to_absolute(text_size)),
);
- limits.resolve(intrinsic).pad(padding)
+ limits
+ .width(width)
+ .shrink(padding)
+ .resolve(width, Length::Shrink, intrinsic)
+ .expand(padding)
};
layout::Node::new(size)
diff --git a/widget/src/progress_bar.rs b/widget/src/progress_bar.rs
index 07de72d5..15f1277b 100644
--- a/widget/src/progress_bar.rs
+++ b/widget/src/progress_bar.rs
@@ -85,12 +85,11 @@ where
Renderer: crate::core::Renderer,
Renderer::Theme: StyleSheet,
{
- fn width(&self) -> Length {
- self.width
- }
-
- fn height(&self) -> Length {
- self.height.unwrap_or(Length::Fixed(Self::DEFAULT_HEIGHT))
+ fn size(&self) -> Size<Length> {
+ Size {
+ width: self.width,
+ height: self.height.unwrap_or(Length::Fixed(Self::DEFAULT_HEIGHT)),
+ }
}
fn layout(
@@ -99,13 +98,11 @@ where
_renderer: &Renderer,
limits: &layout::Limits,
) -> layout::Node {
- let limits = limits
- .width(self.width)
- .height(self.height.unwrap_or(Length::Fixed(Self::DEFAULT_HEIGHT)));
-
- let size = limits.resolve(Size::ZERO);
-
- layout::Node::new(size)
+ layout::atomic(
+ limits,
+ self.width,
+ self.height.unwrap_or(Length::Fixed(Self::DEFAULT_HEIGHT)),
+ )
}
fn draw(
diff --git a/widget/src/qr_code.rs b/widget/src/qr_code.rs
index 1dc4da7f..a229eb59 100644
--- a/widget/src/qr_code.rs
+++ b/widget/src/qr_code.rs
@@ -50,12 +50,11 @@ impl<'a> QRCode<'a> {
}
impl<'a, Message, Theme> Widget<Message, Renderer<Theme>> for QRCode<'a> {
- fn width(&self) -> Length {
- Length::Shrink
- }
-
- fn height(&self) -> Length {
- Length::Shrink
+ fn size(&self) -> Size<Length> {
+ Size {
+ width: Length::Shrink,
+ height: Length::Shrink,
+ }
}
fn layout(
diff --git a/widget/src/radio.rs b/widget/src/radio.rs
index ae2365dd..f91b20b1 100644
--- a/widget/src/radio.rs
+++ b/widget/src/radio.rs
@@ -201,12 +201,11 @@ where
tree::State::new(widget::text::State::<Renderer::Paragraph>::default())
}
- fn width(&self) -> Length {
- self.width
- }
-
- fn height(&self) -> Length {
- Length::Shrink
+ fn size(&self) -> Size<Length> {
+ Size {
+ width: self.width,
+ height: Length::Shrink,
+ }
}
fn layout(
diff --git a/widget/src/row.rs b/widget/src/row.rs
index d52b8c43..90fd2926 100644
--- a/widget/src/row.rs
+++ b/widget/src/row.rs
@@ -7,7 +7,7 @@ use crate::core::renderer;
use crate::core::widget::{Operation, Tree};
use crate::core::{
Alignment, Clipboard, Element, Length, Padding, Pixels, Rectangle, Shell,
- Widget,
+ Size, Widget,
};
/// A container that distributes its contents horizontally.
@@ -21,26 +21,29 @@ pub struct Row<'a, Message, Renderer = crate::Renderer> {
children: Vec<Element<'a, Message, Renderer>>,
}
-impl<'a, Message, Renderer> Row<'a, Message, Renderer> {
+impl<'a, Message, Renderer> Row<'a, Message, Renderer>
+where
+ Renderer: crate::core::Renderer,
+{
/// Creates an empty [`Row`].
pub fn new() -> Self {
- Self::with_children(Vec::new())
- }
-
- /// Creates a [`Row`] with the given elements.
- pub fn with_children(
- children: Vec<Element<'a, Message, Renderer>>,
- ) -> Self {
Row {
spacing: 0.0,
padding: Padding::ZERO,
width: Length::Shrink,
height: Length::Shrink,
align_items: Alignment::Start,
- children,
+ children: Vec::new(),
}
}
+ /// Creates a [`Row`] with the given elements.
+ pub fn with_children(
+ children: impl IntoIterator<Item = Element<'a, Message, Renderer>>,
+ ) -> Self {
+ children.into_iter().fold(Self::new(), Self::push)
+ }
+
/// Sets the horizontal spacing _between_ elements.
///
/// Custom margins per element do not exist in iced. You should use this
@@ -80,12 +83,26 @@ impl<'a, Message, Renderer> Row<'a, Message, Renderer> {
mut self,
child: impl Into<Element<'a, Message, Renderer>>,
) -> Self {
- self.children.push(child.into());
+ let child = child.into();
+ let size = child.as_widget().size_hint();
+
+ if size.width.is_fill() {
+ self.width = Length::Fill;
+ }
+
+ if size.height.is_fill() {
+ self.height = Length::Fill;
+ }
+
+ self.children.push(child);
self
}
}
-impl<'a, Message, Renderer> Default for Row<'a, Message, Renderer> {
+impl<'a, Message, Renderer> Default for Row<'a, Message, Renderer>
+where
+ Renderer: crate::core::Renderer,
+{
fn default() -> Self {
Self::new()
}
@@ -104,12 +121,11 @@ where
tree.diff_children(&self.children);
}
- fn width(&self) -> Length {
- self.width
- }
-
- fn height(&self) -> Length {
- self.height
+ fn size(&self) -> Size<Length> {
+ Size {
+ width: self.width,
+ height: self.height,
+ }
}
fn layout(
@@ -118,12 +134,12 @@ where
renderer: &Renderer,
limits: &layout::Limits,
) -> layout::Node {
- let limits = limits.width(self.width).height(self.height);
-
layout::flex::resolve(
layout::flex::Axis::Horizontal,
renderer,
- &limits,
+ limits,
+ self.width,
+ self.height,
self.padding,
self.spacing,
self.align_items,
diff --git a/widget/src/rule.rs b/widget/src/rule.rs
index b5c5fa55..cded9cb1 100644
--- a/widget/src/rule.rs
+++ b/widget/src/rule.rs
@@ -62,12 +62,11 @@ where
Renderer: crate::core::Renderer,
Renderer::Theme: StyleSheet,
{
- fn width(&self) -> Length {
- self.width
- }
-
- fn height(&self) -> Length {
- self.height
+ fn size(&self) -> Size<Length> {
+ Size {
+ width: self.width,
+ height: self.height,
+ }
}
fn layout(
@@ -76,9 +75,7 @@ where
_renderer: &Renderer,
limits: &layout::Limits,
) -> layout::Node {
- let limits = limits.width(self.width).height(self.height);
-
- layout::Node::new(limits.resolve(Size::ZERO))
+ layout::atomic(limits, self.width, self.height)
}
fn draw(
diff --git a/widget/src/scrollable.rs b/widget/src/scrollable.rs
index 49aed2f0..70db490a 100644
--- a/widget/src/scrollable.rs
+++ b/widget/src/scrollable.rs
@@ -220,12 +220,11 @@ where
tree.diff_children(std::slice::from_ref(&self.content));
}
- fn width(&self) -> Length {
- self.width
- }
-
- fn height(&self) -> Length {
- self.height
+ fn size(&self) -> Size<Length> {
+ Size {
+ width: self.width,
+ height: self.height,
+ }
}
fn layout(
@@ -470,28 +469,25 @@ pub fn layout<Renderer>(
direction: &Direction,
layout_content: impl FnOnce(&Renderer, &layout::Limits) -> layout::Node,
) -> layout::Node {
- let limits = limits.width(width).height(height);
-
- let child_limits = layout::Limits::new(
- Size::new(limits.min().width, limits.min().height),
- Size::new(
- if direction.horizontal().is_some() {
- f32::INFINITY
- } else {
- limits.max().width
- },
- if direction.vertical().is_some() {
- f32::MAX
- } else {
- limits.max().height
- },
- ),
- );
-
- let content = layout_content(renderer, &child_limits);
- let size = limits.resolve(content.size());
+ layout::contained(limits, width, height, |limits| {
+ let child_limits = layout::Limits::new(
+ Size::new(limits.min().width, limits.min().height),
+ Size::new(
+ if direction.horizontal().is_some() {
+ f32::INFINITY
+ } else {
+ limits.max().width
+ },
+ if direction.vertical().is_some() {
+ f32::MAX
+ } else {
+ limits.max().height
+ },
+ ),
+ );
- layout::Node::with_children(size, vec![content])
+ layout_content(renderer, &child_limits)
+ })
}
/// Processes an [`Event`] and updates the [`State`] of a [`Scrollable`]
diff --git a/widget/src/shader.rs b/widget/src/shader.rs
index 8e334693..16b68c55 100644
--- a/widget/src/shader.rs
+++ b/widget/src/shader.rs
@@ -70,12 +70,11 @@ where
tree::State::new(P::State::default())
}
- fn width(&self) -> Length {
- self.width
- }
-
- fn height(&self) -> Length {
- self.height
+ fn size(&self) -> Size<Length> {
+ Size {
+ width: self.width,
+ height: self.height,
+ }
}
fn layout(
@@ -84,10 +83,7 @@ where
_renderer: &Renderer,
limits: &layout::Limits,
) -> layout::Node {
- let limits = limits.width(self.width).height(self.height);
- let size = limits.resolve(Size::ZERO);
-
- layout::Node::new(size)
+ layout::atomic(limits, self.width, self.height)
}
fn on_event(
diff --git a/widget/src/shader/event.rs b/widget/src/shader/event.rs
index 1cc484fb..005c8725 100644
--- a/widget/src/shader/event.rs
+++ b/widget/src/shader/event.rs
@@ -9,7 +9,7 @@ pub use crate::core::event::Status;
/// A [`Shader`] event.
///
/// [`Shader`]: crate::Shader
-#[derive(Debug, Clone, Copy, PartialEq)]
+#[derive(Debug, Clone, PartialEq)]
pub enum Event {
/// A mouse event.
Mouse(mouse::Event),
diff --git a/widget/src/slider.rs b/widget/src/slider.rs
index ac0982c8..1bc94661 100644
--- a/widget/src/slider.rs
+++ b/widget/src/slider.rs
@@ -159,12 +159,11 @@ where
tree::State::new(State::new())
}
- fn width(&self) -> Length {
- self.width
- }
-
- fn height(&self) -> Length {
- Length::Shrink
+ fn size(&self) -> Size<Length> {
+ Size {
+ width: self.width,
+ height: Length::Shrink,
+ }
}
fn layout(
@@ -173,10 +172,7 @@ where
_renderer: &Renderer,
limits: &layout::Limits,
) -> layout::Node {
- let limits = limits.width(self.width).height(self.height);
- let size = limits.resolve(Size::ZERO);
-
- layout::Node::new(size)
+ layout::atomic(limits, self.width, self.height)
}
fn on_event(
diff --git a/widget/src/space.rs b/widget/src/space.rs
index e5a8f169..eef990d1 100644
--- a/widget/src/space.rs
+++ b/widget/src/space.rs
@@ -45,12 +45,11 @@ impl<Message, Renderer> Widget<Message, Renderer> for Space
where
Renderer: core::Renderer,
{
- fn width(&self) -> Length {
- self.width
- }
-
- fn height(&self) -> Length {
- self.height
+ fn size(&self) -> Size<Length> {
+ Size {
+ width: self.width,
+ height: self.height,
+ }
}
fn layout(
@@ -59,9 +58,7 @@ where
_renderer: &Renderer,
limits: &layout::Limits,
) -> layout::Node {
- let limits = limits.width(self.width).height(self.height);
-
- layout::Node::new(limits.resolve(Size::ZERO))
+ layout::atomic(limits, self.width, self.height)
}
fn draw(
diff --git a/widget/src/svg.rs b/widget/src/svg.rs
index 05c9265b..2357cf65 100644
--- a/widget/src/svg.rs
+++ b/widget/src/svg.rs
@@ -96,12 +96,11 @@ where
Renderer: svg::Renderer,
Renderer::Theme: iced_style::svg::StyleSheet,
{
- fn width(&self) -> Length {
- self.width
- }
-
- fn height(&self) -> Length {
- self.height
+ fn size(&self) -> Size<Length> {
+ Size {
+ width: self.width,
+ height: self.height,
+ }
}
fn layout(
@@ -115,10 +114,7 @@ where
let image_size = Size::new(width as f32, height as f32);
// The size to be available to the widget prior to `Shrink`ing
- let raw_size = limits
- .width(self.width)
- .height(self.height)
- .resolve(image_size);
+ let raw_size = limits.resolve(self.width, self.height, image_size);
// The uncropped size of the image when fit to the bounds above
let full_size = self.content_fit.fit(image_size, raw_size);
diff --git a/widget/src/text_editor.rs b/widget/src/text_editor.rs
index a2a186f0..09a0cac0 100644
--- a/widget/src/text_editor.rs
+++ b/widget/src/text_editor.rs
@@ -1,6 +1,7 @@
//! Display a multi-line text input for text editing.
use crate::core::event::{self, Event};
use crate::core::keyboard;
+use crate::core::keyboard::key;
use crate::core::layout::{self, Layout};
use crate::core::mouse;
use crate::core::renderer;
@@ -9,7 +10,7 @@ use crate::core::text::highlighter::{self, Highlighter};
use crate::core::text::{self, LineHeight};
use crate::core::widget::{self, Widget};
use crate::core::{
- Clipboard, Color, Element, Length, Padding, Pixels, Rectangle, Shell,
+ Clipboard, Color, Element, Length, Padding, Pixels, Rectangle, Shell, Size,
Vector,
};
@@ -316,12 +317,11 @@ where
})
}
- fn width(&self) -> Length {
- self.width
- }
-
- fn height(&self) -> Length {
- self.height
+ fn size(&self) -> Size<Length> {
+ Size {
+ width: self.width,
+ height: self.height,
+ }
}
fn layout(
@@ -350,7 +350,7 @@ where
}
internal.editor.update(
- limits.pad(self.padding).max(),
+ limits.shrink(self.padding).max(),
self.font.unwrap_or_else(|| renderer.default_font()),
self.text_size.unwrap_or_else(|| renderer.default_size()),
self.line_height,
@@ -647,43 +647,61 @@ impl Update {
},
Event::Keyboard(event) => match event {
keyboard::Event::KeyPressed {
- key_code,
+ key,
modifiers,
+ text,
+ ..
} if state.is_focused => {
- if let Some(motion) = motion(key_code) {
- let motion =
- if platform::is_jump_modifier_pressed(modifiers) {
+ if let keyboard::Key::Named(named_key) = key.as_ref() {
+ if let Some(motion) = motion(named_key) {
+ let motion = if platform::is_jump_modifier_pressed(
+ modifiers,
+ ) {
motion.widen()
} else {
motion
};
- return action(if modifiers.shift() {
- Action::Select(motion)
- } else {
- Action::Move(motion)
- });
+ return action(if modifiers.shift() {
+ Action::Select(motion)
+ } else {
+ Action::Move(motion)
+ });
+ }
}
- match key_code {
- keyboard::KeyCode::Enter => edit(Edit::Enter),
- keyboard::KeyCode::Backspace => edit(Edit::Backspace),
- keyboard::KeyCode::Delete => edit(Edit::Delete),
- keyboard::KeyCode::Escape => Some(Self::Unfocus),
- keyboard::KeyCode::C if modifiers.command() => {
+ match key.as_ref() {
+ keyboard::Key::Named(key::Named::Enter) => {
+ edit(Edit::Enter)
+ }
+ keyboard::Key::Named(key::Named::Backspace) => {
+ edit(Edit::Backspace)
+ }
+ keyboard::Key::Named(key::Named::Delete) => {
+ edit(Edit::Delete)
+ }
+ keyboard::Key::Named(key::Named::Escape) => {
+ Some(Self::Unfocus)
+ }
+ keyboard::Key::Character("c")
+ if modifiers.command() =>
+ {
Some(Self::Copy)
}
- keyboard::KeyCode::V
+ keyboard::Key::Character("v")
if modifiers.command() && !modifiers.alt() =>
{
Some(Self::Paste)
}
- _ => None,
+ _ => {
+ let text = text?;
+
+ edit(Edit::Insert(
+ text.chars().next().unwrap_or_default(),
+ ))
+ }
}
}
- keyboard::Event::CharacterReceived(c) if state.is_focused => {
- edit(Edit::Insert(c))
- }
_ => None,
},
_ => None,
@@ -691,16 +709,16 @@ impl Update {
}
}
-fn motion(key_code: keyboard::KeyCode) -> Option<Motion> {
- match key_code {
- keyboard::KeyCode::Left => Some(Motion::Left),
- keyboard::KeyCode::Right => Some(Motion::Right),
- keyboard::KeyCode::Up => Some(Motion::Up),
- keyboard::KeyCode::Down => Some(Motion::Down),
- keyboard::KeyCode::Home => Some(Motion::Home),
- keyboard::KeyCode::End => Some(Motion::End),
- keyboard::KeyCode::PageUp => Some(Motion::PageUp),
- keyboard::KeyCode::PageDown => Some(Motion::PageDown),
+fn motion(key: key::Named) -> Option<Motion> {
+ match key {
+ key::Named::ArrowLeft => Some(Motion::Left),
+ key::Named::ArrowRight => Some(Motion::Right),
+ key::Named::ArrowUp => Some(Motion::Up),
+ key::Named::ArrowDown => Some(Motion::Down),
+ key::Named::Home => Some(Motion::Home),
+ key::Named::End => Some(Motion::End),
+ key::Named::PageUp => Some(Motion::PageUp),
+ key::Named::PageDown => Some(Motion::PageDown),
_ => None,
}
}
diff --git a/widget/src/text_input.rs b/widget/src/text_input.rs
index 65d3e1eb..c3dce8be 100644
--- a/widget/src/text_input.rs
+++ b/widget/src/text_input.rs
@@ -14,6 +14,7 @@ use editor::Editor;
use crate::core::alignment;
use crate::core::event::{self, Event};
use crate::core::keyboard;
+use crate::core::keyboard::key;
use crate::core::layout;
use crate::core::mouse::{self, click};
use crate::core::renderer;
@@ -283,12 +284,11 @@ where
}
}
- fn width(&self) -> Length {
- self.width
- }
-
- fn height(&self) -> Length {
- Length::Shrink
+ fn size(&self) -> Size<Length> {
+ Size {
+ width: self.width,
+ height: Length::Shrink,
+ }
}
fn layout(
@@ -506,14 +506,11 @@ where
{
let font = font.unwrap_or_else(|| renderer.default_font());
let text_size = size.unwrap_or_else(|| renderer.default_size());
-
let padding = padding.fit(Size::ZERO, limits.max());
- let limits = limits
- .width(width)
- .pad(padding)
- .height(line_height.to_absolute(text_size));
+ let height = line_height.to_absolute(text_size);
- let text_bounds = limits.resolve(Size::ZERO);
+ let limits = limits.width(width).shrink(padding);
+ let text_bounds = limits.resolve(width, height, Size::ZERO);
let placeholder_text = Text {
font,
@@ -552,41 +549,41 @@ where
let icon_width = state.icon.min_width();
- let mut text_node = layout::Node::new(
- text_bounds - Size::new(icon_width + icon.spacing, 0.0),
- );
-
- let mut icon_node =
- layout::Node::new(Size::new(icon_width, text_bounds.height));
-
- match icon.side {
- Side::Left => {
- text_node.move_to(Point::new(
+ let (text_position, icon_position) = match icon.side {
+ Side::Left => (
+ Point::new(
padding.left + icon_width + icon.spacing,
padding.top,
- ));
-
- icon_node.move_to(Point::new(padding.left, padding.top));
- }
- Side::Right => {
- text_node.move_to(Point::new(padding.left, padding.top));
-
- icon_node.move_to(Point::new(
+ ),
+ Point::new(padding.left, padding.top),
+ ),
+ Side::Right => (
+ Point::new(padding.left, padding.top),
+ Point::new(
padding.left + text_bounds.width - icon_width,
padding.top,
- ));
- }
+ ),
+ ),
};
+ let text_node = layout::Node::new(
+ text_bounds - Size::new(icon_width + icon.spacing, 0.0),
+ )
+ .move_to(text_position);
+
+ let icon_node =
+ layout::Node::new(Size::new(icon_width, text_bounds.height))
+ .move_to(icon_position);
+
layout::Node::with_children(
- text_bounds.pad(padding),
+ text_bounds.expand(padding),
vec![text_node, icon_node],
)
} else {
- let mut text = layout::Node::new(text_bounds);
- text.move_to(Point::new(padding.left, padding.top));
+ let text = layout::Node::new(text_bounds)
+ .move_to(Point::new(padding.left, padding.top));
- layout::Node::with_children(text_bounds.pad(padding), vec![text])
+ layout::Node::with_children(text_bounds.expand(padding), vec![text])
}
}
@@ -752,34 +749,7 @@ where
return event::Status::Captured;
}
}
- Event::Keyboard(keyboard::Event::CharacterReceived(c)) => {
- let state = state();
-
- if let Some(focus) = &mut state.is_focused {
- let Some(on_input) = on_input else {
- return event::Status::Ignored;
- };
-
- if state.is_pasting.is_none()
- && !state.keyboard_modifiers.command()
- && !c.is_control()
- {
- let mut editor = Editor::new(value, &mut state.cursor);
-
- editor.insert(c);
-
- let message = (on_input)(editor.contents());
- shell.publish(message);
-
- focus.updated_at = Instant::now();
-
- update_cache(state, value);
-
- return event::Status::Captured;
- }
- }
- }
- Event::Keyboard(keyboard::Event::KeyPressed { key_code, .. }) => {
+ Event::Keyboard(keyboard::Event::KeyPressed { key, text, .. }) => {
let state = state();
if let Some(focus) = &mut state.is_focused {
@@ -790,14 +760,13 @@ where
let modifiers = state.keyboard_modifiers;
focus.updated_at = Instant::now();
- match key_code {
- keyboard::KeyCode::Enter
- | keyboard::KeyCode::NumpadEnter => {
+ match key.as_ref() {
+ keyboard::Key::Named(key::Named::Enter) => {
if let Some(on_submit) = on_submit.clone() {
shell.publish(on_submit);
}
}
- keyboard::KeyCode::Backspace => {
+ keyboard::Key::Named(key::Named::Backspace) => {
if platform::is_jump_modifier_pressed(modifiers)
&& state.cursor.selection(value).is_none()
{
@@ -817,7 +786,7 @@ where
update_cache(state, value);
}
- keyboard::KeyCode::Delete => {
+ keyboard::Key::Named(key::Named::Delete) => {
if platform::is_jump_modifier_pressed(modifiers)
&& state.cursor.selection(value).is_none()
{
@@ -839,7 +808,7 @@ where
update_cache(state, value);
}
- keyboard::KeyCode::Left => {
+ keyboard::Key::Named(key::Named::ArrowLeft) => {
if platform::is_jump_modifier_pressed(modifiers)
&& !is_secure
{
@@ -854,7 +823,7 @@ where
state.cursor.move_left(value);
}
}
- keyboard::KeyCode::Right => {
+ keyboard::Key::Named(key::Named::ArrowRight) => {
if platform::is_jump_modifier_pressed(modifiers)
&& !is_secure
{
@@ -869,7 +838,7 @@ where
state.cursor.move_right(value);
}
}
- keyboard::KeyCode::Home => {
+ keyboard::Key::Named(key::Named::Home) => {
if modifiers.shift() {
state
.cursor
@@ -878,7 +847,7 @@ where
state.cursor.move_to(0);
}
}
- keyboard::KeyCode::End => {
+ keyboard::Key::Named(key::Named::End) => {
if modifiers.shift() {
state.cursor.select_range(
state.cursor.start(value),
@@ -888,7 +857,7 @@ where
state.cursor.move_to(value.len());
}
}
- keyboard::KeyCode::C
+ keyboard::Key::Character("c")
if state.keyboard_modifiers.command() =>
{
if let Some((start, end)) =
@@ -898,7 +867,7 @@ where
.write(value.select(start, end).to_string());
}
}
- keyboard::KeyCode::X
+ keyboard::Key::Character("x")
if state.keyboard_modifiers.command() =>
{
if let Some((start, end)) =
@@ -916,7 +885,7 @@ where
update_cache(state, value);
}
- keyboard::KeyCode::V => {
+ keyboard::Key::Character("v") => {
if state.keyboard_modifiers.command()
&& !state.keyboard_modifiers.alt()
{
@@ -953,12 +922,12 @@ where
state.is_pasting = None;
}
}
- keyboard::KeyCode::A
+ keyboard::Key::Character("a")
if state.keyboard_modifiers.command() =>
{
state.cursor.select_all(value);
}
- keyboard::KeyCode::Escape => {
+ keyboard::Key::Named(key::Named::Escape) => {
state.is_focused = None;
state.is_dragging = false;
state.is_pasting = None;
@@ -966,28 +935,55 @@ where
state.keyboard_modifiers =
keyboard::Modifiers::default();
}
- keyboard::KeyCode::Tab
- | keyboard::KeyCode::Up
- | keyboard::KeyCode::Down => {
+ keyboard::Key::Named(
+ key::Named::Tab
+ | key::Named::ArrowUp
+ | key::Named::ArrowDown,
+ ) => {
return event::Status::Ignored;
}
- _ => {}
+ _ => {
+ if let Some(text) = text {
+ let c = text.chars().next().unwrap_or_default();
+
+ if state.is_pasting.is_none()
+ && !state.keyboard_modifiers.command()
+ && !c.is_control()
+ {
+ let mut editor =
+ Editor::new(value, &mut state.cursor);
+
+ editor.insert(c);
+
+ let message = (on_input)(editor.contents());
+ shell.publish(message);
+
+ focus.updated_at = Instant::now();
+
+ update_cache(state, value);
+
+ return event::Status::Captured;
+ }
+ }
+ }
}
return event::Status::Captured;
}
}
- Event::Keyboard(keyboard::Event::KeyReleased { key_code, .. }) => {
+ Event::Keyboard(keyboard::Event::KeyReleased { key, .. }) => {
let state = state();
if state.is_focused.is_some() {
- match key_code {
- keyboard::KeyCode::V => {
+ match key.as_ref() {
+ keyboard::Key::Character("v") => {
state.is_pasting = None;
}
- keyboard::KeyCode::Tab
- | keyboard::KeyCode::Up
- | keyboard::KeyCode::Down => {
+ keyboard::Key::Named(
+ key::Named::Tab
+ | key::Named::ArrowUp
+ | key::Named::ArrowDown,
+ ) => {
return event::Status::Ignored;
}
_ => {}
@@ -1194,31 +1190,39 @@ pub fn draw<Renderer>(
(None, 0.0)
};
- if let Some((cursor, color)) = cursor {
- renderer.with_translation(Vector::new(-offset, 0.0), |renderer| {
- renderer.fill_quad(cursor, color);
- });
+ let draw = |renderer: &mut Renderer, viewport| {
+ if let Some((cursor, color)) = cursor {
+ renderer.with_translation(Vector::new(-offset, 0.0), |renderer| {
+ renderer.fill_quad(cursor, color);
+ });
+ } else {
+ renderer.with_translation(Vector::ZERO, |_| {});
+ }
+
+ renderer.fill_paragraph(
+ if text.is_empty() {
+ &state.placeholder
+ } else {
+ &state.value
+ },
+ Point::new(text_bounds.x, text_bounds.center_y())
+ - Vector::new(offset, 0.0),
+ if text.is_empty() {
+ theme.placeholder_color(style)
+ } else if is_disabled {
+ theme.disabled_color(style)
+ } else {
+ theme.value_color(style)
+ },
+ viewport,
+ );
+ };
+
+ if cursor.is_some() {
+ renderer.with_layer(text_bounds, |renderer| draw(renderer, *viewport));
} else {
- renderer.with_translation(Vector::ZERO, |_| {});
+ draw(renderer, text_bounds);
}
-
- renderer.fill_paragraph(
- if text.is_empty() {
- &state.placeholder
- } else {
- &state.value
- },
- Point::new(text_bounds.x, text_bounds.center_y())
- - Vector::new(offset, 0.0),
- if text.is_empty() {
- theme.placeholder_color(style)
- } else if is_disabled {
- theme.disabled_color(style)
- } else {
- theme.value_color(style)
- },
- text_bounds,
- );
}
/// Computes the current [`mouse::Interaction`] of the [`TextInput`].
diff --git a/widget/src/toggler.rs b/widget/src/toggler.rs
index d8723080..941159ea 100644
--- a/widget/src/toggler.rs
+++ b/widget/src/toggler.rs
@@ -168,12 +168,11 @@ where
tree::State::new(widget::text::State::<Renderer::Paragraph>::default())
}
- fn width(&self) -> Length {
- self.width
- }
-
- fn height(&self) -> Length {
- Length::Shrink
+ fn size(&self) -> Size<Length> {
+ Size {
+ width: self.width,
+ height: Length::Shrink,
+ }
}
fn layout(
diff --git a/widget/src/tooltip.rs b/widget/src/tooltip.rs
index 9e102c56..d09a9255 100644
--- a/widget/src/tooltip.rs
+++ b/widget/src/tooltip.rs
@@ -64,6 +64,12 @@ where
self
}
+ /// Sets the [`text::Shaping`] strategy of the [`Tooltip`].
+ pub fn text_shaping(mut self, shaping: text::Shaping) -> Self {
+ self.tooltip = self.tooltip.shaping(shaping);
+ self
+ }
+
/// Sets the font of the [`Tooltip`].
///
/// [`Font`]: Renderer::Font
@@ -125,12 +131,8 @@ where
widget::tree::Tag::of::<State>()
}
- fn width(&self) -> Length {
- self.content.as_widget().width()
- }
-
- fn height(&self) -> Length {
- self.content.as_widget().height()
+ fn size(&self) -> Size<Length> {
+ self.content.as_widget().size()
}
fn layout(
@@ -347,7 +349,7 @@ where
.then(|| viewport.size())
.unwrap_or(Size::INFINITY),
)
- .pad(Padding::new(self.padding)),
+ .shrink(Padding::new(self.padding)),
);
let text_bounds = text_layout.bounds();
diff --git a/widget/src/vertical_slider.rs b/widget/src/vertical_slider.rs
index 01d3359c..a3029d76 100644
--- a/widget/src/vertical_slider.rs
+++ b/widget/src/vertical_slider.rs
@@ -156,12 +156,11 @@ where
tree::State::new(State::new())
}
- fn width(&self) -> Length {
- Length::Shrink
- }
-
- fn height(&self) -> Length {
- self.height
+ fn size(&self) -> Size<Length> {
+ Size {
+ width: Length::Shrink,
+ height: self.height,
+ }
}
fn layout(
@@ -170,10 +169,7 @@ where
_renderer: &Renderer,
limits: &layout::Limits,
) -> layout::Node {
- let limits = limits.width(self.width).height(self.height);
- let size = limits.resolve(Size::ZERO);
-
- layout::Node::new(size)
+ layout::atomic(limits, self.width, self.height)
}
fn on_event(
diff --git a/winit/src/application.rs b/winit/src/application.rs
index d9700075..bf48538d 100644
--- a/winit/src/application.rs
+++ b/winit/src/application.rs
@@ -115,7 +115,9 @@ where
let mut debug = Debug::new();
debug.startup_started();
- let event_loop = EventLoopBuilder::with_user_event().build();
+ let event_loop = EventLoopBuilder::with_user_event()
+ .build()
+ .expect("Create event loop");
let proxy = event_loop.create_proxy();
let runtime = {
@@ -155,7 +157,7 @@ where
{
use winit::platform::web::WindowExtWebSys;
- let canvas = window.canvas();
+ let canvas = window.canvas().expect("Get window canvas");
let window = web_sys::window().unwrap();
let document = window.document().unwrap();
@@ -210,45 +212,28 @@ where
let mut context = task::Context::from_waker(task::noop_waker_ref());
- platform::run(event_loop, move |event, _, control_flow| {
- use winit::event_loop::ControlFlow;
-
- if let ControlFlow::ExitWithCode(_) = control_flow {
+ let _ = event_loop.run(move |event, event_loop| {
+ if event_loop.exiting() {
return;
}
- let event = match event {
- winit::event::Event::WindowEvent {
- event:
- winit::event::WindowEvent::ScaleFactorChanged {
- new_inner_size,
- ..
- },
- window_id,
- } => Some(winit::event::Event::WindowEvent {
- event: winit::event::WindowEvent::Resized(*new_inner_size),
- window_id,
- }),
- _ => event.to_static(),
- };
-
- if let Some(event) = event {
- event_sender.start_send(event).expect("Send event");
+ event_sender.start_send(event).expect("Send event");
- let poll = instance.as_mut().poll(&mut context);
+ let poll = instance.as_mut().poll(&mut context);
- match poll {
- task::Poll::Pending => {
- if let Ok(Some(flow)) = control_receiver.try_next() {
- *control_flow = flow;
- }
- }
- task::Poll::Ready(_) => {
- *control_flow = ControlFlow::Exit;
+ match poll {
+ task::Poll::Pending => {
+ if let Ok(Some(flow)) = control_receiver.try_next() {
+ event_loop.set_control_flow(flow);
}
- };
- }
- })
+ }
+ task::Poll::Ready(_) => {
+ event_loop.exit();
+ }
+ };
+ });
+
+ Ok(())
}
async fn run_instance<A, E, C>(
@@ -259,7 +244,7 @@ async fn run_instance<A, E, C>(
mut proxy: winit::event_loop::EventLoopProxy<A::Message>,
mut debug: Debug,
mut event_receiver: mpsc::UnboundedReceiver<
- winit::event::Event<'_, A::Message>,
+ winit::event::Event<A::Message>,
>,
mut control_sender: mpsc::UnboundedSender<winit::event_loop::ControlFlow>,
init_command: Command<A::Message>,
@@ -327,77 +312,56 @@ async fn run_instance<A, E, C>(
while let Some(event) = event_receiver.next().await {
match event {
- event::Event::NewEvents(start_cause) => {
- redraw_pending = matches!(
- start_cause,
- event::StartCause::Init
- | event::StartCause::Poll
- | event::StartCause::ResumeTimeReached { .. }
- );
+ event::Event::NewEvents(
+ event::StartCause::Init
+ | event::StartCause::ResumeTimeReached { .. },
+ ) if !redraw_pending => {
+ window.request_redraw();
+ redraw_pending = true;
}
- event::Event::MainEventsCleared => {
- if !redraw_pending && events.is_empty() && messages.is_empty() {
- continue;
- }
+ event::Event::PlatformSpecific(event::PlatformSpecific::MacOS(
+ event::MacOS::ReceivedUrl(url),
+ )) => {
+ use crate::core::event;
- debug.event_processing_started();
+ events.push(Event::PlatformSpecific(
+ event::PlatformSpecific::MacOS(event::MacOS::ReceivedUrl(
+ url,
+ )),
+ ));
+ }
+ event::Event::UserEvent(message) => {
+ messages.push(message);
+ }
+ event::Event::WindowEvent {
+ event: event::WindowEvent::RedrawRequested { .. },
+ ..
+ } => {
+ let physical_size = state.physical_size();
- let (interface_state, statuses) = user_interface.update(
- &events,
- state.cursor(),
- &mut renderer,
- &mut clipboard,
- &mut messages,
- );
+ if physical_size.width == 0 || physical_size.height == 0 {
+ continue;
+ }
- debug.event_processing_finished();
+ let current_viewport_version = state.viewport_version();
- for (event, status) in
- events.drain(..).zip(statuses.into_iter())
- {
- runtime.broadcast(event, status);
- }
+ if viewport_version != current_viewport_version {
+ let logical_size = state.logical_size();
- if !messages.is_empty()
- || matches!(
- interface_state,
- user_interface::State::Outdated
- )
- {
- let mut cache =
- ManuallyDrop::into_inner(user_interface).into_cache();
+ debug.layout_started();
+ user_interface = ManuallyDrop::new(
+ ManuallyDrop::into_inner(user_interface)
+ .relayout(logical_size, &mut renderer),
+ );
+ debug.layout_finished();
- // Update application
- update(
- &mut application,
- &mut compositor,
+ compositor.configure_surface(
&mut surface,
- &mut cache,
- &state,
- &mut renderer,
- &mut runtime,
- &mut clipboard,
- &mut should_exit,
- &mut proxy,
- &mut debug,
- &mut messages,
- &window,
+ physical_size.width,
+ physical_size.height,
);
- // Update window
- state.synchronize(&application, &window);
-
- user_interface = ManuallyDrop::new(build_user_interface(
- &application,
- cache,
- &mut renderer,
- state.logical_size(),
- &mut debug,
- ));
-
- if should_exit {
- break;
- }
+ viewport_version = current_viewport_version;
}
// TODO: Avoid redrawing all the time by forcing widgets to
@@ -418,6 +382,24 @@ async fn run_instance<A, E, C>(
&mut messages,
);
+ let _ = control_sender.start_send(match interface_state {
+ user_interface::State::Updated {
+ redraw_request: Some(redraw_request),
+ } => match redraw_request {
+ window::RedrawRequest::NextFrame => {
+ window.request_redraw();
+
+ ControlFlow::Wait
+ }
+ window::RedrawRequest::At(at) => {
+ ControlFlow::WaitUntil(at)
+ }
+ },
+ _ => ControlFlow::Wait,
+ });
+
+ runtime.broadcast(redraw_event, core::event::Status::Ignored);
+
debug.draw_started();
let new_mouse_interaction = user_interface.draw(
&mut renderer,
@@ -427,6 +409,7 @@ async fn run_instance<A, E, C>(
},
state.cursor(),
);
+ redraw_pending = false;
debug.draw_finished();
if new_mouse_interaction != mouse_interaction {
@@ -437,85 +420,7 @@ async fn run_instance<A, E, C>(
mouse_interaction = new_mouse_interaction;
}
- window.request_redraw();
- runtime.broadcast(redraw_event, core::event::Status::Ignored);
-
- let _ = control_sender.start_send(match interface_state {
- user_interface::State::Updated {
- redraw_request: Some(redraw_request),
- } => match redraw_request {
- window::RedrawRequest::NextFrame => ControlFlow::Poll,
- window::RedrawRequest::At(at) => {
- ControlFlow::WaitUntil(at)
- }
- },
- _ => ControlFlow::Wait,
- });
-
- redraw_pending = false;
- }
- event::Event::PlatformSpecific(event::PlatformSpecific::MacOS(
- event::MacOS::ReceivedUrl(url),
- )) => {
- use crate::core::event;
-
- events.push(Event::PlatformSpecific(
- event::PlatformSpecific::MacOS(event::MacOS::ReceivedUrl(
- url,
- )),
- ));
- }
- event::Event::UserEvent(message) => {
- messages.push(message);
- }
- event::Event::RedrawRequested(_) => {
- let physical_size = state.physical_size();
-
- if physical_size.width == 0 || physical_size.height == 0 {
- continue;
- }
-
debug.render_started();
- let current_viewport_version = state.viewport_version();
-
- if viewport_version != current_viewport_version {
- let logical_size = state.logical_size();
-
- debug.layout_started();
- user_interface = ManuallyDrop::new(
- ManuallyDrop::into_inner(user_interface)
- .relayout(logical_size, &mut renderer),
- );
- debug.layout_finished();
-
- debug.draw_started();
- let new_mouse_interaction = user_interface.draw(
- &mut renderer,
- state.theme(),
- &renderer::Style {
- text_color: state.text_color(),
- },
- state.cursor(),
- );
-
- if new_mouse_interaction != mouse_interaction {
- window.set_cursor_icon(conversion::mouse_interaction(
- new_mouse_interaction,
- ));
-
- mouse_interaction = new_mouse_interaction;
- }
- debug.draw_finished();
-
- compositor.configure_surface(
- &mut surface,
- physical_size.width,
- physical_size.height,
- );
-
- viewport_version = current_viewport_version;
- }
-
match compositor.present(
&mut renderer,
&mut surface,
@@ -557,13 +462,80 @@ async fn run_instance<A, E, C>(
if let Some(event) = conversion::window_event(
window::Id::MAIN,
- &window_event,
+ window_event,
state.scale_factor(),
state.modifiers(),
) {
events.push(event);
}
}
+ event::Event::AboutToWait => {
+ if events.is_empty() && messages.is_empty() {
+ continue;
+ }
+
+ debug.event_processing_started();
+
+ let (interface_state, statuses) = user_interface.update(
+ &events,
+ state.cursor(),
+ &mut renderer,
+ &mut clipboard,
+ &mut messages,
+ );
+
+ debug.event_processing_finished();
+
+ for (event, status) in
+ events.drain(..).zip(statuses.into_iter())
+ {
+ runtime.broadcast(event, status);
+ }
+
+ if !messages.is_empty()
+ || matches!(
+ interface_state,
+ user_interface::State::Outdated
+ )
+ {
+ let mut cache =
+ ManuallyDrop::into_inner(user_interface).into_cache();
+
+ // Update application
+ update(
+ &mut application,
+ &mut compositor,
+ &mut surface,
+ &mut cache,
+ &mut state,
+ &mut renderer,
+ &mut runtime,
+ &mut clipboard,
+ &mut should_exit,
+ &mut proxy,
+ &mut debug,
+ &mut messages,
+ &window,
+ );
+
+ user_interface = ManuallyDrop::new(build_user_interface(
+ &application,
+ cache,
+ &mut renderer,
+ state.logical_size(),
+ &mut debug,
+ ));
+
+ if should_exit {
+ break;
+ }
+ }
+
+ if !redraw_pending {
+ window.request_redraw();
+ redraw_pending = true;
+ }
+ }
_ => {}
}
}
@@ -575,8 +547,8 @@ async fn run_instance<A, E, C>(
/// Returns true if the provided event should cause an [`Application`] to
/// exit.
pub fn requests_exit(
- event: &winit::event::WindowEvent<'_>,
- _modifiers: winit::event::ModifiersState,
+ event: &winit::event::WindowEvent,
+ _modifiers: winit::keyboard::ModifiersState,
) -> bool {
use winit::event::WindowEvent;
@@ -584,14 +556,14 @@ pub fn requests_exit(
WindowEvent::CloseRequested => true,
#[cfg(target_os = "macos")]
WindowEvent::KeyboardInput {
- input:
- winit::event::KeyboardInput {
- virtual_keycode: Some(winit::event::VirtualKeyCode::Q),
+ event:
+ winit::event::KeyEvent {
+ logical_key: winit::keyboard::Key::Character(c),
state: winit::event::ElementState::Pressed,
..
},
..
- } if _modifiers.logo() => true,
+ } if c == "q" && _modifiers.super_key() => true,
_ => false,
}
}
@@ -626,7 +598,7 @@ pub fn update<A: Application, C, E: Executor>(
compositor: &mut C,
surface: &mut C::Surface,
cache: &mut user_interface::Cache,
- state: &State<A>,
+ state: &mut State<A>,
renderer: &mut A::Renderer,
runtime: &mut Runtime<E, Proxy<A::Message>, A::Message>,
clipboard: &mut Clipboard,
@@ -663,6 +635,8 @@ pub fn update<A: Application, C, E: Executor>(
);
}
+ state.synchronize(application, window);
+
let subscription = application.subscription();
runtime.track(subscription.into_recipes());
}
@@ -726,10 +700,11 @@ pub fn run_command<A, C, E>(
);
}
window::Action::Resize(_id, size) => {
- window.set_inner_size(winit::dpi::LogicalSize {
- width: size.width,
- height: size.height,
- });
+ let _ =
+ window.request_inner_size(winit::dpi::LogicalSize {
+ width: size.width,
+ height: size.height,
+ });
}
window::Action::FetchSize(_id, callback) => {
let size =
@@ -742,9 +717,19 @@ pub fn run_command<A, C, E>(
)))
.expect("Send message to event loop");
}
+ window::Action::FetchMaximized(_id, callback) => {
+ proxy
+ .send_event(callback(window.is_maximized()))
+ .expect("Send message to event loop");
+ }
window::Action::Maximize(_id, maximized) => {
window.set_maximized(maximized);
}
+ window::Action::FetchMinimized(_id, callback) => {
+ proxy
+ .send_event(callback(window.is_minimized()))
+ .expect("Send message to event loop");
+ }
window::Action::Minimize(_id, minimized) => {
window.set_minimized(minimized);
}
@@ -878,43 +863,3 @@ pub fn run_command<A, C, E>(
}
}
}
-
-#[cfg(not(target_arch = "wasm32"))]
-mod platform {
- pub fn run<T, F>(
- mut event_loop: winit::event_loop::EventLoop<T>,
- event_handler: F,
- ) -> Result<(), super::Error>
- where
- F: 'static
- + FnMut(
- winit::event::Event<'_, T>,
- &winit::event_loop::EventLoopWindowTarget<T>,
- &mut winit::event_loop::ControlFlow,
- ),
- {
- use winit::platform::run_return::EventLoopExtRunReturn;
-
- let _ = event_loop.run_return(event_handler);
-
- Ok(())
- }
-}
-
-#[cfg(target_arch = "wasm32")]
-mod platform {
- pub fn run<T, F>(
- event_loop: winit::event_loop::EventLoop<T>,
- event_handler: F,
- ) -> !
- where
- F: 'static
- + FnMut(
- winit::event::Event<'_, T>,
- &winit::event_loop::EventLoopWindowTarget<T>,
- &mut winit::event_loop::ControlFlow,
- ),
- {
- event_loop.run(event_handler)
- }
-}
diff --git a/winit/src/application/state.rs b/winit/src/application/state.rs
index e655529a..8c9b20e0 100644
--- a/winit/src/application/state.rs
+++ b/winit/src/application/state.rs
@@ -22,7 +22,7 @@ where
viewport: Viewport,
viewport_version: usize,
cursor_position: Option<winit::dpi::PhysicalPosition<f64>>,
- modifiers: winit::event::ModifiersState,
+ modifiers: winit::keyboard::ModifiersState,
theme: <A::Renderer as core::Renderer>::Theme,
appearance: application::Appearance,
application: PhantomData<A>,
@@ -54,7 +54,7 @@ where
viewport,
viewport_version: 0,
cursor_position: None,
- modifiers: winit::event::ModifiersState::default(),
+ modifiers: winit::keyboard::ModifiersState::default(),
theme,
appearance,
application: PhantomData,
@@ -102,7 +102,7 @@ where
}
/// Returns the current keyboard modifiers of the [`State`].
- pub fn modifiers(&self) -> winit::event::ModifiersState {
+ pub fn modifiers(&self) -> winit::keyboard::ModifiersState {
self.modifiers
}
@@ -126,7 +126,7 @@ where
pub fn update(
&mut self,
window: &Window,
- event: &WindowEvent<'_>,
+ event: &WindowEvent,
_debug: &mut Debug,
) {
match event {
@@ -142,10 +142,9 @@ where
}
WindowEvent::ScaleFactorChanged {
scale_factor: new_scale_factor,
- new_inner_size,
+ ..
} => {
- let size =
- Size::new(new_inner_size.width, new_inner_size.height);
+ let size = self.viewport.physical_size();
self.viewport = Viewport::with_physical_size(
size,
@@ -164,13 +163,16 @@ where
self.cursor_position = None;
}
WindowEvent::ModifiersChanged(new_modifiers) => {
- self.modifiers = *new_modifiers;
+ self.modifiers = new_modifiers.state();
}
#[cfg(feature = "debug")]
WindowEvent::KeyboardInput {
- input:
- winit::event::KeyboardInput {
- virtual_keycode: Some(winit::event::VirtualKeyCode::F12),
+ event:
+ winit::event::KeyEvent {
+ logical_key:
+ winit::keyboard::Key::Named(
+ winit::keyboard::NamedKey::F12,
+ ),
state: winit::event::ElementState::Pressed,
..
},
diff --git a/winit/src/conversion.rs b/winit/src/conversion.rs
index 7e51a2d4..90a5d27f 100644
--- a/winit/src/conversion.rs
+++ b/winit/src/conversion.rs
@@ -128,9 +128,9 @@ pub fn window_settings(
/// Converts a winit window event into an iced event.
pub fn window_event(
id: window::Id,
- event: &winit::event::WindowEvent<'_>,
+ event: winit::event::WindowEvent,
scale_factor: f64,
- modifiers: winit::event::ModifiersState,
+ modifiers: winit::keyboard::ModifiersState,
) -> Option<Event> {
use winit::event::WindowEvent;
@@ -146,17 +146,6 @@ pub fn window_event(
},
))
}
- WindowEvent::ScaleFactorChanged { new_inner_size, .. } => {
- let logical_size = new_inner_size.to_logical(scale_factor);
-
- Some(Event::Window(
- id,
- window::Event::Resized {
- width: logical_size.width,
- height: logical_size.height,
- },
- ))
- }
WindowEvent::CloseRequested => {
Some(Event::Window(id, window::Event::CloseRequested))
}
@@ -174,7 +163,7 @@ pub fn window_event(
Some(Event::Mouse(mouse::Event::CursorLeft))
}
WindowEvent::MouseInput { button, state, .. } => {
- let button = mouse_button(*button);
+ let button = mouse_button(button);
Some(Event::Mouse(match state {
winit::event::ElementState::Pressed => {
@@ -189,8 +178,8 @@ pub fn window_event(
winit::event::MouseScrollDelta::LineDelta(delta_x, delta_y) => {
Some(Event::Mouse(mouse::Event::WheelScrolled {
delta: mouse::ScrollDelta::Lines {
- x: *delta_x,
- y: *delta_y,
+ x: delta_x,
+ y: delta_y,
},
}))
}
@@ -203,42 +192,59 @@ pub fn window_event(
}))
}
},
- WindowEvent::ReceivedCharacter(c) if !is_private_use_character(*c) => {
- Some(Event::Keyboard(keyboard::Event::CharacterReceived(*c)))
- }
WindowEvent::KeyboardInput {
- input:
- winit::event::KeyboardInput {
- virtual_keycode: Some(virtual_keycode),
+ event:
+ winit::event::KeyEvent {
+ logical_key,
state,
+ text,
+ location,
..
},
..
} => Some(Event::Keyboard({
- let key_code = key_code(*virtual_keycode);
+ let key = key(logical_key);
let modifiers = self::modifiers(modifiers);
+ let location = match location {
+ winit::keyboard::KeyLocation::Standard => {
+ keyboard::Location::Standard
+ }
+ winit::keyboard::KeyLocation::Left => keyboard::Location::Left,
+ winit::keyboard::KeyLocation::Right => {
+ keyboard::Location::Right
+ }
+ winit::keyboard::KeyLocation::Numpad => {
+ keyboard::Location::Numpad
+ }
+ };
+
match state {
winit::event::ElementState::Pressed => {
keyboard::Event::KeyPressed {
- key_code,
+ key,
modifiers,
+ location,
+ text,
}
}
winit::event::ElementState::Released => {
keyboard::Event::KeyReleased {
- key_code,
+ key,
modifiers,
+ location,
}
}
}
})),
- WindowEvent::ModifiersChanged(new_modifiers) => Some(Event::Keyboard(
- keyboard::Event::ModifiersChanged(self::modifiers(*new_modifiers)),
- )),
+ WindowEvent::ModifiersChanged(new_modifiers) => {
+ Some(Event::Keyboard(keyboard::Event::ModifiersChanged(
+ self::modifiers(new_modifiers.state()),
+ )))
+ }
WindowEvent::Focused(focused) => Some(Event::Window(
id,
- if *focused {
+ if focused {
window::Event::Focused
} else {
window::Event::Unfocused
@@ -254,7 +260,7 @@ pub fn window_event(
Some(Event::Window(id, window::Event::FilesHoveredLeft))
}
WindowEvent::Touch(touch) => {
- Some(Event::Touch(touch_event(*touch, scale_factor)))
+ Some(Event::Touch(touch_event(touch, scale_factor)))
}
WindowEvent::Moved(position) => {
let winit::dpi::LogicalPosition { x, y } =
@@ -365,7 +371,7 @@ pub fn mouse_interaction(
match interaction {
Interaction::Idle => winit::window::CursorIcon::Default,
- Interaction::Pointer => winit::window::CursorIcon::Hand,
+ Interaction::Pointer => winit::window::CursorIcon::Pointer,
Interaction::Working => winit::window::CursorIcon::Progress,
Interaction::Grab => winit::window::CursorIcon::Grab,
Interaction::Grabbing => winit::window::CursorIcon::Grabbing,
@@ -388,6 +394,8 @@ pub fn mouse_button(mouse_button: winit::event::MouseButton) -> mouse::Button {
winit::event::MouseButton::Left => mouse::Button::Left,
winit::event::MouseButton::Right => mouse::Button::Right,
winit::event::MouseButton::Middle => mouse::Button::Middle,
+ winit::event::MouseButton::Back => mouse::Button::Back,
+ winit::event::MouseButton::Forward => mouse::Button::Forward,
winit::event::MouseButton::Other(other) => mouse::Button::Other(other),
}
}
@@ -398,14 +406,14 @@ pub fn mouse_button(mouse_button: winit::event::MouseButton) -> mouse::Button {
/// [`winit`]: https://github.com/rust-windowing/winit
/// [`iced`]: https://github.com/iced-rs/iced/tree/0.10
pub fn modifiers(
- modifiers: winit::event::ModifiersState,
+ modifiers: winit::keyboard::ModifiersState,
) -> keyboard::Modifiers {
let mut result = keyboard::Modifiers::empty();
- result.set(keyboard::Modifiers::SHIFT, modifiers.shift());
- result.set(keyboard::Modifiers::CTRL, modifiers.ctrl());
- result.set(keyboard::Modifiers::ALT, modifiers.alt());
- result.set(keyboard::Modifiers::LOGO, modifiers.logo());
+ result.set(keyboard::Modifiers::SHIFT, modifiers.shift_key());
+ result.set(keyboard::Modifiers::CTRL, modifiers.control_key());
+ result.set(keyboard::Modifiers::ALT, modifiers.alt_key());
+ result.set(keyboard::Modifiers::LOGO, modifiers.super_key());
result
}
@@ -455,179 +463,328 @@ pub fn touch_event(
///
/// [`winit`]: https://github.com/rust-windowing/winit
/// [`iced`]: https://github.com/iced-rs/iced/tree/0.10
-pub fn key_code(
- virtual_keycode: winit::event::VirtualKeyCode,
-) -> keyboard::KeyCode {
- use keyboard::KeyCode;
-
- match virtual_keycode {
- winit::event::VirtualKeyCode::Key1 => KeyCode::Key1,
- winit::event::VirtualKeyCode::Key2 => KeyCode::Key2,
- winit::event::VirtualKeyCode::Key3 => KeyCode::Key3,
- winit::event::VirtualKeyCode::Key4 => KeyCode::Key4,
- winit::event::VirtualKeyCode::Key5 => KeyCode::Key5,
- winit::event::VirtualKeyCode::Key6 => KeyCode::Key6,
- winit::event::VirtualKeyCode::Key7 => KeyCode::Key7,
- winit::event::VirtualKeyCode::Key8 => KeyCode::Key8,
- winit::event::VirtualKeyCode::Key9 => KeyCode::Key9,
- winit::event::VirtualKeyCode::Key0 => KeyCode::Key0,
- winit::event::VirtualKeyCode::A => KeyCode::A,
- winit::event::VirtualKeyCode::B => KeyCode::B,
- winit::event::VirtualKeyCode::C => KeyCode::C,
- winit::event::VirtualKeyCode::D => KeyCode::D,
- winit::event::VirtualKeyCode::E => KeyCode::E,
- winit::event::VirtualKeyCode::F => KeyCode::F,
- winit::event::VirtualKeyCode::G => KeyCode::G,
- winit::event::VirtualKeyCode::H => KeyCode::H,
- winit::event::VirtualKeyCode::I => KeyCode::I,
- winit::event::VirtualKeyCode::J => KeyCode::J,
- winit::event::VirtualKeyCode::K => KeyCode::K,
- winit::event::VirtualKeyCode::L => KeyCode::L,
- winit::event::VirtualKeyCode::M => KeyCode::M,
- winit::event::VirtualKeyCode::N => KeyCode::N,
- winit::event::VirtualKeyCode::O => KeyCode::O,
- winit::event::VirtualKeyCode::P => KeyCode::P,
- winit::event::VirtualKeyCode::Q => KeyCode::Q,
- winit::event::VirtualKeyCode::R => KeyCode::R,
- winit::event::VirtualKeyCode::S => KeyCode::S,
- winit::event::VirtualKeyCode::T => KeyCode::T,
- winit::event::VirtualKeyCode::U => KeyCode::U,
- winit::event::VirtualKeyCode::V => KeyCode::V,
- winit::event::VirtualKeyCode::W => KeyCode::W,
- winit::event::VirtualKeyCode::X => KeyCode::X,
- winit::event::VirtualKeyCode::Y => KeyCode::Y,
- winit::event::VirtualKeyCode::Z => KeyCode::Z,
- winit::event::VirtualKeyCode::Escape => KeyCode::Escape,
- winit::event::VirtualKeyCode::F1 => KeyCode::F1,
- winit::event::VirtualKeyCode::F2 => KeyCode::F2,
- winit::event::VirtualKeyCode::F3 => KeyCode::F3,
- winit::event::VirtualKeyCode::F4 => KeyCode::F4,
- winit::event::VirtualKeyCode::F5 => KeyCode::F5,
- winit::event::VirtualKeyCode::F6 => KeyCode::F6,
- winit::event::VirtualKeyCode::F7 => KeyCode::F7,
- winit::event::VirtualKeyCode::F8 => KeyCode::F8,
- winit::event::VirtualKeyCode::F9 => KeyCode::F9,
- winit::event::VirtualKeyCode::F10 => KeyCode::F10,
- winit::event::VirtualKeyCode::F11 => KeyCode::F11,
- winit::event::VirtualKeyCode::F12 => KeyCode::F12,
- winit::event::VirtualKeyCode::F13 => KeyCode::F13,
- winit::event::VirtualKeyCode::F14 => KeyCode::F14,
- winit::event::VirtualKeyCode::F15 => KeyCode::F15,
- winit::event::VirtualKeyCode::F16 => KeyCode::F16,
- winit::event::VirtualKeyCode::F17 => KeyCode::F17,
- winit::event::VirtualKeyCode::F18 => KeyCode::F18,
- winit::event::VirtualKeyCode::F19 => KeyCode::F19,
- winit::event::VirtualKeyCode::F20 => KeyCode::F20,
- winit::event::VirtualKeyCode::F21 => KeyCode::F21,
- winit::event::VirtualKeyCode::F22 => KeyCode::F22,
- winit::event::VirtualKeyCode::F23 => KeyCode::F23,
- winit::event::VirtualKeyCode::F24 => KeyCode::F24,
- winit::event::VirtualKeyCode::Snapshot => KeyCode::Snapshot,
- winit::event::VirtualKeyCode::Scroll => KeyCode::Scroll,
- winit::event::VirtualKeyCode::Pause => KeyCode::Pause,
- winit::event::VirtualKeyCode::Insert => KeyCode::Insert,
- winit::event::VirtualKeyCode::Home => KeyCode::Home,
- winit::event::VirtualKeyCode::Delete => KeyCode::Delete,
- winit::event::VirtualKeyCode::End => KeyCode::End,
- winit::event::VirtualKeyCode::PageDown => KeyCode::PageDown,
- winit::event::VirtualKeyCode::PageUp => KeyCode::PageUp,
- winit::event::VirtualKeyCode::Left => KeyCode::Left,
- winit::event::VirtualKeyCode::Up => KeyCode::Up,
- winit::event::VirtualKeyCode::Right => KeyCode::Right,
- winit::event::VirtualKeyCode::Down => KeyCode::Down,
- winit::event::VirtualKeyCode::Back => KeyCode::Backspace,
- winit::event::VirtualKeyCode::Return => KeyCode::Enter,
- winit::event::VirtualKeyCode::Space => KeyCode::Space,
- winit::event::VirtualKeyCode::Compose => KeyCode::Compose,
- winit::event::VirtualKeyCode::Caret => KeyCode::Caret,
- winit::event::VirtualKeyCode::Numlock => KeyCode::Numlock,
- winit::event::VirtualKeyCode::Numpad0 => KeyCode::Numpad0,
- winit::event::VirtualKeyCode::Numpad1 => KeyCode::Numpad1,
- winit::event::VirtualKeyCode::Numpad2 => KeyCode::Numpad2,
- winit::event::VirtualKeyCode::Numpad3 => KeyCode::Numpad3,
- winit::event::VirtualKeyCode::Numpad4 => KeyCode::Numpad4,
- winit::event::VirtualKeyCode::Numpad5 => KeyCode::Numpad5,
- winit::event::VirtualKeyCode::Numpad6 => KeyCode::Numpad6,
- winit::event::VirtualKeyCode::Numpad7 => KeyCode::Numpad7,
- winit::event::VirtualKeyCode::Numpad8 => KeyCode::Numpad8,
- winit::event::VirtualKeyCode::Numpad9 => KeyCode::Numpad9,
- winit::event::VirtualKeyCode::AbntC1 => KeyCode::AbntC1,
- winit::event::VirtualKeyCode::AbntC2 => KeyCode::AbntC2,
- winit::event::VirtualKeyCode::NumpadAdd => KeyCode::NumpadAdd,
- winit::event::VirtualKeyCode::Plus => KeyCode::Plus,
- winit::event::VirtualKeyCode::Apostrophe => KeyCode::Apostrophe,
- winit::event::VirtualKeyCode::Apps => KeyCode::Apps,
- winit::event::VirtualKeyCode::At => KeyCode::At,
- winit::event::VirtualKeyCode::Ax => KeyCode::Ax,
- winit::event::VirtualKeyCode::Backslash => KeyCode::Backslash,
- winit::event::VirtualKeyCode::Calculator => KeyCode::Calculator,
- winit::event::VirtualKeyCode::Capital => KeyCode::Capital,
- winit::event::VirtualKeyCode::Colon => KeyCode::Colon,
- winit::event::VirtualKeyCode::Comma => KeyCode::Comma,
- winit::event::VirtualKeyCode::Convert => KeyCode::Convert,
- winit::event::VirtualKeyCode::NumpadDecimal => KeyCode::NumpadDecimal,
- winit::event::VirtualKeyCode::NumpadDivide => KeyCode::NumpadDivide,
- winit::event::VirtualKeyCode::Equals => KeyCode::Equals,
- winit::event::VirtualKeyCode::Grave => KeyCode::Grave,
- winit::event::VirtualKeyCode::Kana => KeyCode::Kana,
- winit::event::VirtualKeyCode::Kanji => KeyCode::Kanji,
- winit::event::VirtualKeyCode::LAlt => KeyCode::LAlt,
- winit::event::VirtualKeyCode::LBracket => KeyCode::LBracket,
- winit::event::VirtualKeyCode::LControl => KeyCode::LControl,
- winit::event::VirtualKeyCode::LShift => KeyCode::LShift,
- winit::event::VirtualKeyCode::LWin => KeyCode::LWin,
- winit::event::VirtualKeyCode::Mail => KeyCode::Mail,
- winit::event::VirtualKeyCode::MediaSelect => KeyCode::MediaSelect,
- winit::event::VirtualKeyCode::MediaStop => KeyCode::MediaStop,
- winit::event::VirtualKeyCode::Minus => KeyCode::Minus,
- winit::event::VirtualKeyCode::NumpadMultiply => KeyCode::NumpadMultiply,
- winit::event::VirtualKeyCode::Mute => KeyCode::Mute,
- winit::event::VirtualKeyCode::MyComputer => KeyCode::MyComputer,
- winit::event::VirtualKeyCode::NavigateForward => {
- KeyCode::NavigateForward
- }
- winit::event::VirtualKeyCode::NavigateBackward => {
- KeyCode::NavigateBackward
+pub fn key(key: winit::keyboard::Key) -> keyboard::Key {
+ use keyboard::key::Named;
+ use winit::keyboard::NamedKey;
+
+ match key {
+ winit::keyboard::Key::Character(c) => keyboard::Key::Character(c),
+ winit::keyboard::Key::Named(named_key) => {
+ keyboard::Key::Named(match named_key {
+ NamedKey::Alt => Named::Alt,
+ NamedKey::AltGraph => Named::AltGraph,
+ NamedKey::CapsLock => Named::CapsLock,
+ NamedKey::Control => Named::Control,
+ NamedKey::Fn => Named::Fn,
+ NamedKey::FnLock => Named::FnLock,
+ NamedKey::NumLock => Named::NumLock,
+ NamedKey::ScrollLock => Named::ScrollLock,
+ NamedKey::Shift => Named::Shift,
+ NamedKey::Symbol => Named::Symbol,
+ NamedKey::SymbolLock => Named::SymbolLock,
+ NamedKey::Meta => Named::Meta,
+ NamedKey::Hyper => Named::Hyper,
+ NamedKey::Super => Named::Super,
+ NamedKey::Enter => Named::Enter,
+ NamedKey::Tab => Named::Tab,
+ NamedKey::Space => Named::Space,
+ NamedKey::ArrowDown => Named::ArrowDown,
+ NamedKey::ArrowLeft => Named::ArrowLeft,
+ NamedKey::ArrowRight => Named::ArrowRight,
+ NamedKey::ArrowUp => Named::ArrowUp,
+ NamedKey::End => Named::End,
+ NamedKey::Home => Named::Home,
+ NamedKey::PageDown => Named::PageDown,
+ NamedKey::PageUp => Named::PageUp,
+ NamedKey::Backspace => Named::Backspace,
+ NamedKey::Clear => Named::Clear,
+ NamedKey::Copy => Named::Copy,
+ NamedKey::CrSel => Named::CrSel,
+ NamedKey::Cut => Named::Cut,
+ NamedKey::Delete => Named::Delete,
+ NamedKey::EraseEof => Named::EraseEof,
+ NamedKey::ExSel => Named::ExSel,
+ NamedKey::Insert => Named::Insert,
+ NamedKey::Paste => Named::Paste,
+ NamedKey::Redo => Named::Redo,
+ NamedKey::Undo => Named::Undo,
+ NamedKey::Accept => Named::Accept,
+ NamedKey::Again => Named::Again,
+ NamedKey::Attn => Named::Attn,
+ NamedKey::Cancel => Named::Cancel,
+ NamedKey::ContextMenu => Named::ContextMenu,
+ NamedKey::Escape => Named::Escape,
+ NamedKey::Execute => Named::Execute,
+ NamedKey::Find => Named::Find,
+ NamedKey::Help => Named::Help,
+ NamedKey::Pause => Named::Pause,
+ NamedKey::Play => Named::Play,
+ NamedKey::Props => Named::Props,
+ NamedKey::Select => Named::Select,
+ NamedKey::ZoomIn => Named::ZoomIn,
+ NamedKey::ZoomOut => Named::ZoomOut,
+ NamedKey::BrightnessDown => Named::BrightnessDown,
+ NamedKey::BrightnessUp => Named::BrightnessUp,
+ NamedKey::Eject => Named::Eject,
+ NamedKey::LogOff => Named::LogOff,
+ NamedKey::Power => Named::Power,
+ NamedKey::PowerOff => Named::PowerOff,
+ NamedKey::PrintScreen => Named::PrintScreen,
+ NamedKey::Hibernate => Named::Hibernate,
+ NamedKey::Standby => Named::Standby,
+ NamedKey::WakeUp => Named::WakeUp,
+ NamedKey::AllCandidates => Named::AllCandidates,
+ NamedKey::Alphanumeric => Named::Alphanumeric,
+ NamedKey::CodeInput => Named::CodeInput,
+ NamedKey::Compose => Named::Compose,
+ NamedKey::Convert => Named::Convert,
+ NamedKey::FinalMode => Named::FinalMode,
+ NamedKey::GroupFirst => Named::GroupFirst,
+ NamedKey::GroupLast => Named::GroupLast,
+ NamedKey::GroupNext => Named::GroupNext,
+ NamedKey::GroupPrevious => Named::GroupPrevious,
+ NamedKey::ModeChange => Named::ModeChange,
+ NamedKey::NextCandidate => Named::NextCandidate,
+ NamedKey::NonConvert => Named::NonConvert,
+ NamedKey::PreviousCandidate => Named::PreviousCandidate,
+ NamedKey::Process => Named::Process,
+ NamedKey::SingleCandidate => Named::SingleCandidate,
+ NamedKey::HangulMode => Named::HangulMode,
+ NamedKey::HanjaMode => Named::HanjaMode,
+ NamedKey::JunjaMode => Named::JunjaMode,
+ NamedKey::Eisu => Named::Eisu,
+ NamedKey::Hankaku => Named::Hankaku,
+ NamedKey::Hiragana => Named::Hiragana,
+ NamedKey::HiraganaKatakana => Named::HiraganaKatakana,
+ NamedKey::KanaMode => Named::KanaMode,
+ NamedKey::KanjiMode => Named::KanjiMode,
+ NamedKey::Katakana => Named::Katakana,
+ NamedKey::Romaji => Named::Romaji,
+ NamedKey::Zenkaku => Named::Zenkaku,
+ NamedKey::ZenkakuHankaku => Named::ZenkakuHankaku,
+ NamedKey::Soft1 => Named::Soft1,
+ NamedKey::Soft2 => Named::Soft2,
+ NamedKey::Soft3 => Named::Soft3,
+ NamedKey::Soft4 => Named::Soft4,
+ NamedKey::ChannelDown => Named::ChannelDown,
+ NamedKey::ChannelUp => Named::ChannelUp,
+ NamedKey::Close => Named::Close,
+ NamedKey::MailForward => Named::MailForward,
+ NamedKey::MailReply => Named::MailReply,
+ NamedKey::MailSend => Named::MailSend,
+ NamedKey::MediaClose => Named::MediaClose,
+ NamedKey::MediaFastForward => Named::MediaFastForward,
+ NamedKey::MediaPause => Named::MediaPause,
+ NamedKey::MediaPlay => Named::MediaPlay,
+ NamedKey::MediaPlayPause => Named::MediaPlayPause,
+ NamedKey::MediaRecord => Named::MediaRecord,
+ NamedKey::MediaRewind => Named::MediaRewind,
+ NamedKey::MediaStop => Named::MediaStop,
+ NamedKey::MediaTrackNext => Named::MediaTrackNext,
+ NamedKey::MediaTrackPrevious => Named::MediaTrackPrevious,
+ NamedKey::New => Named::New,
+ NamedKey::Open => Named::Open,
+ NamedKey::Print => Named::Print,
+ NamedKey::Save => Named::Save,
+ NamedKey::SpellCheck => Named::SpellCheck,
+ NamedKey::Key11 => Named::Key11,
+ NamedKey::Key12 => Named::Key12,
+ NamedKey::AudioBalanceLeft => Named::AudioBalanceLeft,
+ NamedKey::AudioBalanceRight => Named::AudioBalanceRight,
+ NamedKey::AudioBassBoostDown => Named::AudioBassBoostDown,
+ NamedKey::AudioBassBoostToggle => Named::AudioBassBoostToggle,
+ NamedKey::AudioBassBoostUp => Named::AudioBassBoostUp,
+ NamedKey::AudioFaderFront => Named::AudioFaderFront,
+ NamedKey::AudioFaderRear => Named::AudioFaderRear,
+ NamedKey::AudioSurroundModeNext => Named::AudioSurroundModeNext,
+ NamedKey::AudioTrebleDown => Named::AudioTrebleDown,
+ NamedKey::AudioTrebleUp => Named::AudioTrebleUp,
+ NamedKey::AudioVolumeDown => Named::AudioVolumeDown,
+ NamedKey::AudioVolumeUp => Named::AudioVolumeUp,
+ NamedKey::AudioVolumeMute => Named::AudioVolumeMute,
+ NamedKey::MicrophoneToggle => Named::MicrophoneToggle,
+ NamedKey::MicrophoneVolumeDown => Named::MicrophoneVolumeDown,
+ NamedKey::MicrophoneVolumeUp => Named::MicrophoneVolumeUp,
+ NamedKey::MicrophoneVolumeMute => Named::MicrophoneVolumeMute,
+ NamedKey::SpeechCorrectionList => Named::SpeechCorrectionList,
+ NamedKey::SpeechInputToggle => Named::SpeechInputToggle,
+ NamedKey::LaunchApplication1 => Named::LaunchApplication1,
+ NamedKey::LaunchApplication2 => Named::LaunchApplication2,
+ NamedKey::LaunchCalendar => Named::LaunchCalendar,
+ NamedKey::LaunchContacts => Named::LaunchContacts,
+ NamedKey::LaunchMail => Named::LaunchMail,
+ NamedKey::LaunchMediaPlayer => Named::LaunchMediaPlayer,
+ NamedKey::LaunchMusicPlayer => Named::LaunchMusicPlayer,
+ NamedKey::LaunchPhone => Named::LaunchPhone,
+ NamedKey::LaunchScreenSaver => Named::LaunchScreenSaver,
+ NamedKey::LaunchSpreadsheet => Named::LaunchSpreadsheet,
+ NamedKey::LaunchWebBrowser => Named::LaunchWebBrowser,
+ NamedKey::LaunchWebCam => Named::LaunchWebCam,
+ NamedKey::LaunchWordProcessor => Named::LaunchWordProcessor,
+ NamedKey::BrowserBack => Named::BrowserBack,
+ NamedKey::BrowserFavorites => Named::BrowserFavorites,
+ NamedKey::BrowserForward => Named::BrowserForward,
+ NamedKey::BrowserHome => Named::BrowserHome,
+ NamedKey::BrowserRefresh => Named::BrowserRefresh,
+ NamedKey::BrowserSearch => Named::BrowserSearch,
+ NamedKey::BrowserStop => Named::BrowserStop,
+ NamedKey::AppSwitch => Named::AppSwitch,
+ NamedKey::Call => Named::Call,
+ NamedKey::Camera => Named::Camera,
+ NamedKey::CameraFocus => Named::CameraFocus,
+ NamedKey::EndCall => Named::EndCall,
+ NamedKey::GoBack => Named::GoBack,
+ NamedKey::GoHome => Named::GoHome,
+ NamedKey::HeadsetHook => Named::HeadsetHook,
+ NamedKey::LastNumberRedial => Named::LastNumberRedial,
+ NamedKey::Notification => Named::Notification,
+ NamedKey::MannerMode => Named::MannerMode,
+ NamedKey::VoiceDial => Named::VoiceDial,
+ NamedKey::TV => Named::TV,
+ NamedKey::TV3DMode => Named::TV3DMode,
+ NamedKey::TVAntennaCable => Named::TVAntennaCable,
+ NamedKey::TVAudioDescription => Named::TVAudioDescription,
+ NamedKey::TVAudioDescriptionMixDown => {
+ Named::TVAudioDescriptionMixDown
+ }
+ NamedKey::TVAudioDescriptionMixUp => {
+ Named::TVAudioDescriptionMixUp
+ }
+ NamedKey::TVContentsMenu => Named::TVContentsMenu,
+ NamedKey::TVDataService => Named::TVDataService,
+ NamedKey::TVInput => Named::TVInput,
+ NamedKey::TVInputComponent1 => Named::TVInputComponent1,
+ NamedKey::TVInputComponent2 => Named::TVInputComponent2,
+ NamedKey::TVInputComposite1 => Named::TVInputComposite1,
+ NamedKey::TVInputComposite2 => Named::TVInputComposite2,
+ NamedKey::TVInputHDMI1 => Named::TVInputHDMI1,
+ NamedKey::TVInputHDMI2 => Named::TVInputHDMI2,
+ NamedKey::TVInputHDMI3 => Named::TVInputHDMI3,
+ NamedKey::TVInputHDMI4 => Named::TVInputHDMI4,
+ NamedKey::TVInputVGA1 => Named::TVInputVGA1,
+ NamedKey::TVMediaContext => Named::TVMediaContext,
+ NamedKey::TVNetwork => Named::TVNetwork,
+ NamedKey::TVNumberEntry => Named::TVNumberEntry,
+ NamedKey::TVPower => Named::TVPower,
+ NamedKey::TVRadioService => Named::TVRadioService,
+ NamedKey::TVSatellite => Named::TVSatellite,
+ NamedKey::TVSatelliteBS => Named::TVSatelliteBS,
+ NamedKey::TVSatelliteCS => Named::TVSatelliteCS,
+ NamedKey::TVSatelliteToggle => Named::TVSatelliteToggle,
+ NamedKey::TVTerrestrialAnalog => Named::TVTerrestrialAnalog,
+ NamedKey::TVTerrestrialDigital => Named::TVTerrestrialDigital,
+ NamedKey::TVTimer => Named::TVTimer,
+ NamedKey::AVRInput => Named::AVRInput,
+ NamedKey::AVRPower => Named::AVRPower,
+ NamedKey::ColorF0Red => Named::ColorF0Red,
+ NamedKey::ColorF1Green => Named::ColorF1Green,
+ NamedKey::ColorF2Yellow => Named::ColorF2Yellow,
+ NamedKey::ColorF3Blue => Named::ColorF3Blue,
+ NamedKey::ColorF4Grey => Named::ColorF4Grey,
+ NamedKey::ColorF5Brown => Named::ColorF5Brown,
+ NamedKey::ClosedCaptionToggle => Named::ClosedCaptionToggle,
+ NamedKey::Dimmer => Named::Dimmer,
+ NamedKey::DisplaySwap => Named::DisplaySwap,
+ NamedKey::DVR => Named::DVR,
+ NamedKey::Exit => Named::Exit,
+ NamedKey::FavoriteClear0 => Named::FavoriteClear0,
+ NamedKey::FavoriteClear1 => Named::FavoriteClear1,
+ NamedKey::FavoriteClear2 => Named::FavoriteClear2,
+ NamedKey::FavoriteClear3 => Named::FavoriteClear3,
+ NamedKey::FavoriteRecall0 => Named::FavoriteRecall0,
+ NamedKey::FavoriteRecall1 => Named::FavoriteRecall1,
+ NamedKey::FavoriteRecall2 => Named::FavoriteRecall2,
+ NamedKey::FavoriteRecall3 => Named::FavoriteRecall3,
+ NamedKey::FavoriteStore0 => Named::FavoriteStore0,
+ NamedKey::FavoriteStore1 => Named::FavoriteStore1,
+ NamedKey::FavoriteStore2 => Named::FavoriteStore2,
+ NamedKey::FavoriteStore3 => Named::FavoriteStore3,
+ NamedKey::Guide => Named::Guide,
+ NamedKey::GuideNextDay => Named::GuideNextDay,
+ NamedKey::GuidePreviousDay => Named::GuidePreviousDay,
+ NamedKey::Info => Named::Info,
+ NamedKey::InstantReplay => Named::InstantReplay,
+ NamedKey::Link => Named::Link,
+ NamedKey::ListProgram => Named::ListProgram,
+ NamedKey::LiveContent => Named::LiveContent,
+ NamedKey::Lock => Named::Lock,
+ NamedKey::MediaApps => Named::MediaApps,
+ NamedKey::MediaAudioTrack => Named::MediaAudioTrack,
+ NamedKey::MediaLast => Named::MediaLast,
+ NamedKey::MediaSkipBackward => Named::MediaSkipBackward,
+ NamedKey::MediaSkipForward => Named::MediaSkipForward,
+ NamedKey::MediaStepBackward => Named::MediaStepBackward,
+ NamedKey::MediaStepForward => Named::MediaStepForward,
+ NamedKey::MediaTopMenu => Named::MediaTopMenu,
+ NamedKey::NavigateIn => Named::NavigateIn,
+ NamedKey::NavigateNext => Named::NavigateNext,
+ NamedKey::NavigateOut => Named::NavigateOut,
+ NamedKey::NavigatePrevious => Named::NavigatePrevious,
+ NamedKey::NextFavoriteChannel => Named::NextFavoriteChannel,
+ NamedKey::NextUserProfile => Named::NextUserProfile,
+ NamedKey::OnDemand => Named::OnDemand,
+ NamedKey::Pairing => Named::Pairing,
+ NamedKey::PinPDown => Named::PinPDown,
+ NamedKey::PinPMove => Named::PinPMove,
+ NamedKey::PinPToggle => Named::PinPToggle,
+ NamedKey::PinPUp => Named::PinPUp,
+ NamedKey::PlaySpeedDown => Named::PlaySpeedDown,
+ NamedKey::PlaySpeedReset => Named::PlaySpeedReset,
+ NamedKey::PlaySpeedUp => Named::PlaySpeedUp,
+ NamedKey::RandomToggle => Named::RandomToggle,
+ NamedKey::RcLowBattery => Named::RcLowBattery,
+ NamedKey::RecordSpeedNext => Named::RecordSpeedNext,
+ NamedKey::RfBypass => Named::RfBypass,
+ NamedKey::ScanChannelsToggle => Named::ScanChannelsToggle,
+ NamedKey::ScreenModeNext => Named::ScreenModeNext,
+ NamedKey::Settings => Named::Settings,
+ NamedKey::SplitScreenToggle => Named::SplitScreenToggle,
+ NamedKey::STBInput => Named::STBInput,
+ NamedKey::STBPower => Named::STBPower,
+ NamedKey::Subtitle => Named::Subtitle,
+ NamedKey::Teletext => Named::Teletext,
+ NamedKey::VideoModeNext => Named::VideoModeNext,
+ NamedKey::Wink => Named::Wink,
+ NamedKey::ZoomToggle => Named::ZoomToggle,
+ NamedKey::F1 => Named::F1,
+ NamedKey::F2 => Named::F2,
+ NamedKey::F3 => Named::F3,
+ NamedKey::F4 => Named::F4,
+ NamedKey::F5 => Named::F5,
+ NamedKey::F6 => Named::F6,
+ NamedKey::F7 => Named::F7,
+ NamedKey::F8 => Named::F8,
+ NamedKey::F9 => Named::F9,
+ NamedKey::F10 => Named::F10,
+ NamedKey::F11 => Named::F11,
+ NamedKey::F12 => Named::F12,
+ NamedKey::F13 => Named::F13,
+ NamedKey::F14 => Named::F14,
+ NamedKey::F15 => Named::F15,
+ NamedKey::F16 => Named::F16,
+ NamedKey::F17 => Named::F17,
+ NamedKey::F18 => Named::F18,
+ NamedKey::F19 => Named::F19,
+ NamedKey::F20 => Named::F20,
+ NamedKey::F21 => Named::F21,
+ NamedKey::F22 => Named::F22,
+ NamedKey::F23 => Named::F23,
+ NamedKey::F24 => Named::F24,
+ NamedKey::F25 => Named::F25,
+ NamedKey::F26 => Named::F26,
+ NamedKey::F27 => Named::F27,
+ NamedKey::F28 => Named::F28,
+ NamedKey::F29 => Named::F29,
+ NamedKey::F30 => Named::F30,
+ NamedKey::F31 => Named::F31,
+ NamedKey::F32 => Named::F32,
+ NamedKey::F33 => Named::F33,
+ NamedKey::F34 => Named::F34,
+ NamedKey::F35 => Named::F35,
+ _ => return keyboard::Key::Unidentified,
+ })
}
- winit::event::VirtualKeyCode::NextTrack => KeyCode::NextTrack,
- winit::event::VirtualKeyCode::NoConvert => KeyCode::NoConvert,
- winit::event::VirtualKeyCode::NumpadComma => KeyCode::NumpadComma,
- winit::event::VirtualKeyCode::NumpadEnter => KeyCode::NumpadEnter,
- winit::event::VirtualKeyCode::NumpadEquals => KeyCode::NumpadEquals,
- winit::event::VirtualKeyCode::OEM102 => KeyCode::OEM102,
- winit::event::VirtualKeyCode::Period => KeyCode::Period,
- winit::event::VirtualKeyCode::PlayPause => KeyCode::PlayPause,
- winit::event::VirtualKeyCode::Power => KeyCode::Power,
- winit::event::VirtualKeyCode::PrevTrack => KeyCode::PrevTrack,
- winit::event::VirtualKeyCode::RAlt => KeyCode::RAlt,
- winit::event::VirtualKeyCode::RBracket => KeyCode::RBracket,
- winit::event::VirtualKeyCode::RControl => KeyCode::RControl,
- winit::event::VirtualKeyCode::RShift => KeyCode::RShift,
- winit::event::VirtualKeyCode::RWin => KeyCode::RWin,
- winit::event::VirtualKeyCode::Semicolon => KeyCode::Semicolon,
- winit::event::VirtualKeyCode::Slash => KeyCode::Slash,
- winit::event::VirtualKeyCode::Sleep => KeyCode::Sleep,
- winit::event::VirtualKeyCode::Stop => KeyCode::Stop,
- winit::event::VirtualKeyCode::NumpadSubtract => KeyCode::NumpadSubtract,
- winit::event::VirtualKeyCode::Sysrq => KeyCode::Sysrq,
- winit::event::VirtualKeyCode::Tab => KeyCode::Tab,
- winit::event::VirtualKeyCode::Underline => KeyCode::Underline,
- winit::event::VirtualKeyCode::Unlabeled => KeyCode::Unlabeled,
- winit::event::VirtualKeyCode::VolumeDown => KeyCode::VolumeDown,
- winit::event::VirtualKeyCode::VolumeUp => KeyCode::VolumeUp,
- winit::event::VirtualKeyCode::Wake => KeyCode::Wake,
- winit::event::VirtualKeyCode::WebBack => KeyCode::WebBack,
- winit::event::VirtualKeyCode::WebFavorites => KeyCode::WebFavorites,
- winit::event::VirtualKeyCode::WebForward => KeyCode::WebForward,
- winit::event::VirtualKeyCode::WebHome => KeyCode::WebHome,
- winit::event::VirtualKeyCode::WebRefresh => KeyCode::WebRefresh,
- winit::event::VirtualKeyCode::WebSearch => KeyCode::WebSearch,
- winit::event::VirtualKeyCode::WebStop => KeyCode::WebStop,
- winit::event::VirtualKeyCode::Yen => KeyCode::Yen,
- winit::event::VirtualKeyCode::Copy => KeyCode::Copy,
- winit::event::VirtualKeyCode::Paste => KeyCode::Paste,
- winit::event::VirtualKeyCode::Cut => KeyCode::Cut,
- winit::event::VirtualKeyCode::Asterisk => KeyCode::Asterisk,
+ _ => keyboard::Key::Unidentified,
}
}
@@ -655,13 +812,3 @@ pub fn icon(icon: window::Icon) -> Option<winit::window::Icon> {
winit::window::Icon::from_rgba(pixels, size.width, size.height).ok()
}
-
-// As defined in: http://www.unicode.org/faq/private_use.html
-pub(crate) fn is_private_use_character(c: char) -> bool {
- matches!(
- c,
- '\u{E000}'..='\u{F8FF}'
- | '\u{F0000}'..='\u{FFFFD}'
- | '\u{100000}'..='\u{10FFFD}'
- )
-}
diff --git a/winit/src/multi_window.rs b/winit/src/multi_window.rs
index 84651d40..84c81bea 100644
--- a/winit/src/multi_window.rs
+++ b/winit/src/multi_window.rs
@@ -118,7 +118,10 @@ where
let mut debug = Debug::new();
debug.startup_started();
- let event_loop = EventLoopBuilder::with_user_event().build();
+ let event_loop = EventLoopBuilder::with_user_event()
+ .build()
+ .expect("Create event loop");
+
let proxy = event_loop.create_proxy();
let runtime = {
@@ -210,78 +213,78 @@ where
let mut context = task::Context::from_waker(task::noop_waker_ref());
- platform::run(event_loop, move |event, window_target, control_flow| {
- use winit::event_loop::ControlFlow;
-
- if let ControlFlow::ExitWithCode(_) = control_flow {
+ let _ = event_loop.run(move |event, event_loop| {
+ if event_loop.exiting() {
return;
}
- let event = match event {
- winit::event::Event::WindowEvent {
- event:
- winit::event::WindowEvent::ScaleFactorChanged {
- new_inner_size,
- ..
- },
- window_id,
- } => Some(winit::event::Event::WindowEvent {
- event: winit::event::WindowEvent::Resized(*new_inner_size),
- window_id,
- }),
- _ => event.to_static(),
- };
-
- if let Some(event) = event {
- event_sender
- .start_send(Event::EventLoopAwakened(event))
- .expect("Send event");
-
- loop {
- let poll = instance.as_mut().poll(&mut context);
-
- match poll {
- task::Poll::Pending => match control_receiver.try_next() {
- Ok(Some(control)) => match control {
- Control::ChangeFlow(flow) => {
- *control_flow = flow;
- }
- Control::CreateWindow {
- id,
- settings,
- title,
- monitor,
- } => {
- let exit_on_close_request =
- settings.exit_on_close_request;
-
- let window = conversion::window_settings(
- settings, &title, monitor, None,
- )
- .build(window_target)
- .expect("Failed to build window");
-
- event_sender
- .start_send(Event::WindowCreated {
- id,
- window,
- exit_on_close_request,
- })
- .expect("Send event");
+ event_sender
+ .start_send(Event::EventLoopAwakened(event))
+ .expect("Send event");
+
+ loop {
+ let poll = instance.as_mut().poll(&mut context);
+
+ match poll {
+ task::Poll::Pending => match control_receiver.try_next() {
+ Ok(Some(control)) => match control {
+ Control::ChangeFlow(flow) => {
+ use winit::event_loop::ControlFlow;
+
+ match (event_loop.control_flow(), flow) {
+ (
+ ControlFlow::WaitUntil(current),
+ ControlFlow::WaitUntil(new),
+ ) if new < current => {}
+ (
+ ControlFlow::WaitUntil(target),
+ ControlFlow::Wait,
+ ) if target > Instant::now() => {}
+ _ => {
+ event_loop.set_control_flow(flow);
+ }
}
- },
- _ => {
- break;
+ }
+ Control::CreateWindow {
+ id,
+ settings,
+ title,
+ monitor,
+ } => {
+ let exit_on_close_request =
+ settings.exit_on_close_request;
+
+ let window = conversion::window_settings(
+ settings, &title, monitor, None,
+ )
+ .build(event_loop)
+ .expect("Failed to build window");
+
+ event_sender
+ .start_send(Event::WindowCreated {
+ id,
+ window,
+ exit_on_close_request,
+ })
+ .expect("Send event");
+ }
+ Control::Exit => {
+ event_loop.exit();
}
},
- task::Poll::Ready(_) => {
- *control_flow = ControlFlow::Exit;
+ _ => {
break;
}
- };
- }
+ },
+ task::Poll::Ready(_) => {
+ event_loop.exit();
+ break;
+ }
+ };
}
- })
+ });
+
+ Ok(())
}
enum Event<Message: 'static> {
@@ -290,11 +293,12 @@ enum Event<Message: 'static> {
window: winit::window::Window,
exit_on_close_request: bool,
},
- EventLoopAwakened(winit::event::Event<'static, Message>),
+ EventLoopAwakened(winit::event::Event<Message>),
}
enum Control {
ChangeFlow(winit::event_loop::ControlFlow),
+ Exit,
CreateWindow {
id: window::Id,
settings: window::Settings,
@@ -372,7 +376,6 @@ async fn run_instance<A, E, C>(
runtime.track(application.subscription().into_recipes());
let mut messages = Vec::new();
- let mut redraw_pending = false;
debug.startup_finished();
@@ -419,191 +422,15 @@ async fn run_instance<A, E, C>(
}
Event::EventLoopAwakened(event) => {
match event {
- event::Event::NewEvents(start_cause) => {
- redraw_pending = matches!(
- start_cause,
- event::StartCause::Init
- | event::StartCause::Poll
- | event::StartCause::ResumeTimeReached { .. }
- );
- }
- event::Event::MainEventsCleared => {
- debug.event_processing_started();
- let mut uis_stale = false;
-
- for (id, window) in window_manager.iter_mut() {
- let mut window_events = vec![];
-
- events.retain(|(window_id, event)| {
- if *window_id == Some(id) || window_id.is_none()
- {
- window_events.push(event.clone());
- false
- } else {
- true
- }
- });
-
- if !redraw_pending
- && window_events.is_empty()
- && messages.is_empty()
- {
- continue;
- }
-
- let (ui_state, statuses) = user_interfaces
- .get_mut(&id)
- .expect("Get user interface")
- .update(
- &window_events,
- window.state.cursor(),
- &mut window.renderer,
- &mut clipboard,
- &mut messages,
- );
-
- if !uis_stale {
- uis_stale = matches!(
- ui_state,
- user_interface::State::Outdated
- );
- }
-
- for (event, status) in window_events
- .into_iter()
- .zip(statuses.into_iter())
- {
- runtime.broadcast(event, status);
- }
- }
-
- debug.event_processing_finished();
-
- // TODO mw application update returns which window IDs to update
- if !messages.is_empty() || uis_stale {
- let mut cached_interfaces: HashMap<
- window::Id,
- user_interface::Cache,
- > = ManuallyDrop::into_inner(user_interfaces)
- .drain()
- .map(|(id, ui)| (id, ui.into_cache()))
- .collect();
-
- // Update application
- update(
- &mut application,
- &mut compositor,
- &mut runtime,
- &mut clipboard,
- &mut control_sender,
- &mut proxy,
- &mut debug,
- &mut messages,
- &mut window_manager,
- &mut cached_interfaces,
- );
-
- // we must synchronize all window states with application state after an
- // application update since we don't know what changed
- for (id, window) in window_manager.iter_mut() {
- window.state.synchronize(
- &application,
- id,
- &window.raw,
- );
- }
-
- // rebuild UIs with the synchronized states
- user_interfaces =
- ManuallyDrop::new(build_user_interfaces(
- &application,
- &mut debug,
- &mut window_manager,
- cached_interfaces,
- ));
- }
-
- debug.draw_started();
-
- for (id, window) in window_manager.iter_mut() {
- // TODO: Avoid redrawing all the time by forcing widgets to
- // request redraws on state changes
- //
- // Then, we can use the `interface_state` here to decide if a redraw
- // is needed right away, or simply wait until a specific time.
- let redraw_event = core::Event::Window(
- id,
- window::Event::RedrawRequested(Instant::now()),
- );
-
- let cursor = window.state.cursor();
-
- let ui = user_interfaces
- .get_mut(&id)
- .expect("Get user interface");
-
- let (ui_state, _) = ui.update(
- &[redraw_event.clone()],
- cursor,
- &mut window.renderer,
- &mut clipboard,
- &mut messages,
- );
-
- let new_mouse_interaction = {
- let state = &window.state;
-
- ui.draw(
- &mut window.renderer,
- state.theme(),
- &renderer::Style {
- text_color: state.text_color(),
- },
- cursor,
- )
- };
-
- if new_mouse_interaction != window.mouse_interaction
- {
- window.raw.set_cursor_icon(
- conversion::mouse_interaction(
- new_mouse_interaction,
- ),
- );
-
- window.mouse_interaction =
- new_mouse_interaction;
- }
-
+ event::Event::NewEvents(
+ event::StartCause::Init
+ | event::StartCause::ResumeTimeReached { .. },
+ ) => {
+ for (_id, window) in window_manager.iter_mut() {
// TODO once widgets can request to be redrawn, we can avoid always requesting a
// redraw
window.raw.request_redraw();
-
- runtime.broadcast(
- redraw_event.clone(),
- core::event::Status::Ignored,
- );
-
- let _ = control_sender.start_send(
- Control::ChangeFlow(match ui_state {
- user_interface::State::Updated {
- redraw_request: Some(redraw_request),
- } => match redraw_request {
- window::RedrawRequest::NextFrame => {
- ControlFlow::Poll
- }
- window::RedrawRequest::At(at) => {
- ControlFlow::WaitUntil(at)
- }
- },
- _ => ControlFlow::Wait,
- }),
- );
}
-
- redraw_pending = false;
-
- debug.draw_finished();
}
event::Event::PlatformSpecific(
event::PlatformSpecific::MacOS(
@@ -624,13 +451,85 @@ async fn run_instance<A, E, C>(
event::Event::UserEvent(message) => {
messages.push(message);
}
- event::Event::RedrawRequested(id) => {
+ event::Event::WindowEvent {
+ window_id: id,
+ event: event::WindowEvent::RedrawRequested,
+ ..
+ } => {
let Some((id, window)) =
window_manager.get_mut_alias(id)
else {
continue;
};
+ // TODO: Avoid redrawing all the time by forcing widgets to
+ // request redraws on state changes
+ //
+ // Then, we can use the `interface_state` here to decide if a redraw
+ // is needed right away, or simply wait until a specific time.
+ let redraw_event = core::Event::Window(
+ id,
+ window::Event::RedrawRequested(Instant::now()),
+ );
+
+ let cursor = window.state.cursor();
+
+ let ui = user_interfaces
+ .get_mut(&id)
+ .expect("Get user interface");
+
+ let (ui_state, _) = ui.update(
+ &[redraw_event.clone()],
+ cursor,
+ &mut window.renderer,
+ &mut clipboard,
+ &mut messages,
+ );
+
+ debug.draw_started();
+ let new_mouse_interaction = ui.draw(
+ &mut window.renderer,
+ window.state.theme(),
+ &renderer::Style {
+ text_color: window.state.text_color(),
+ },
+ cursor,
+ );
+ debug.draw_finished();
+
+ if new_mouse_interaction != window.mouse_interaction {
+ window.raw.set_cursor_icon(
+ conversion::mouse_interaction(
+ new_mouse_interaction,
+ ),
+ );
+
+ window.mouse_interaction = new_mouse_interaction;
+ }
+
+ runtime.broadcast(
+ redraw_event.clone(),
+ core::event::Status::Ignored,
+ );
+
+ let _ = control_sender.start_send(Control::ChangeFlow(
+ match ui_state {
+ user_interface::State::Updated {
+ redraw_request: Some(redraw_request),
+ } => match redraw_request {
+ window::RedrawRequest::NextFrame => {
+ window.raw.request_redraw();
+
+ ControlFlow::Wait
+ }
+ window::RedrawRequest::At(at) => {
+ ControlFlow::WaitUntil(at)
+ }
+ },
+ _ => ControlFlow::Wait,
+ },
+ ));
+
let physical_size = window.state.physical_size();
if physical_size.width == 0 || physical_size.height == 0
@@ -638,14 +537,12 @@ async fn run_instance<A, E, C>(
continue;
}
- debug.render_started();
if window.viewport_version
!= window.state.viewport_version()
{
let logical_size = window.state.logical_size();
debug.layout_started();
-
let ui = user_interfaces
.remove(&id)
.expect("Remove user interface");
@@ -654,7 +551,6 @@ async fn run_instance<A, E, C>(
id,
ui.relayout(logical_size, &mut window.renderer),
);
-
debug.layout_finished();
debug.draw_started();
@@ -669,6 +565,7 @@ async fn run_instance<A, E, C>(
},
window.state.cursor(),
);
+ debug.draw_finished();
if new_mouse_interaction != window.mouse_interaction
{
@@ -681,7 +578,6 @@ async fn run_instance<A, E, C>(
window.mouse_interaction =
new_mouse_interaction;
}
- debug.draw_finished();
compositor.configure_surface(
&mut window.surface,
@@ -693,6 +589,7 @@ async fn run_instance<A, E, C>(
window.state.viewport_version();
}
+ debug.render_started();
match compositor.present(
&mut window.renderer,
&mut window.surface,
@@ -713,9 +610,11 @@ async fn run_instance<A, E, C>(
}
_ => {
debug.render_finished();
+
log::error!(
- "Error {error:?} when presenting surface."
- );
+ "Error {error:?} when \
+ presenting surface."
+ );
// Try rendering all windows again next frame.
for (_id, window) in
@@ -763,7 +662,7 @@ async fn run_instance<A, E, C>(
if let Some(event) = conversion::window_event(
id,
- &window_event,
+ window_event,
window.state.scale_factor(),
window.state.modifiers(),
) {
@@ -771,6 +670,109 @@ async fn run_instance<A, E, C>(
}
}
}
+ event::Event::AboutToWait => {
+ if events.is_empty() && messages.is_empty() {
+ continue;
+ }
+
+ debug.event_processing_started();
+ let mut uis_stale = false;
+
+ for (id, window) in window_manager.iter_mut() {
+ let mut window_events = vec![];
+
+ events.retain(|(window_id, event)| {
+ if *window_id == Some(id) || window_id.is_none()
+ {
+ window_events.push(event.clone());
+ false
+ } else {
+ true
+ }
+ });
+
+ if window_events.is_empty() && messages.is_empty() {
+ continue;
+ }
+
+ let (ui_state, statuses) = user_interfaces
+ .get_mut(&id)
+ .expect("Get user interface")
+ .update(
+ &window_events,
+ window.state.cursor(),
+ &mut window.renderer,
+ &mut clipboard,
+ &mut messages,
+ );
+
+ window.raw.request_redraw();
+
+ if !uis_stale {
+ uis_stale = matches!(
+ ui_state,
+ user_interface::State::Outdated
+ );
+ }
+
+ for (event, status) in window_events
+ .into_iter()
+ .zip(statuses.into_iter())
+ {
+ runtime.broadcast(event, status);
+ }
+ }
+
+ debug.event_processing_finished();
+
+ // TODO mw application update returns which window IDs to update
+ if !messages.is_empty() || uis_stale {
+ let mut cached_interfaces: HashMap<
+ window::Id,
+ user_interface::Cache,
+ > = ManuallyDrop::into_inner(user_interfaces)
+ .drain()
+ .map(|(id, ui)| (id, ui.into_cache()))
+ .collect();
+
+ // Update application
+ update(
+ &mut application,
+ &mut compositor,
+ &mut runtime,
+ &mut clipboard,
+ &mut control_sender,
+ &mut proxy,
+ &mut debug,
+ &mut messages,
+ &mut window_manager,
+ &mut cached_interfaces,
+ );
+
+ // we must synchronize all window states with application state after an
+ // application update since we don't know what changed
+ for (id, window) in window_manager.iter_mut() {
+ window.state.synchronize(
+ &application,
+ id,
+ &window.raw,
+ );
+
+ // TODO once widgets can request to be redrawn, we can avoid always requesting a
+ // redraw
+ window.raw.request_redraw();
+ }
+
+ // rebuild UIs with the synchronized states
+ user_interfaces =
+ ManuallyDrop::new(build_user_interfaces(
+ &application,
+ &mut debug,
+ &mut window_manager,
+ cached_interfaces,
+ ));
+ }
+ }
_ => {}
}
}
@@ -901,16 +903,12 @@ fn run_command<A, C, E>(
.expect("Send control action");
}
window::Action::Close(id) => {
- use winit::event_loop::ControlFlow;
-
let _ = window_manager.remove(id);
let _ = ui_caches.remove(&id);
if window_manager.is_empty() {
control_sender
- .start_send(Control::ChangeFlow(
- ControlFlow::ExitWithCode(0),
- ))
+ .start_send(Control::Exit)
.expect("Send control action");
}
}
@@ -921,10 +919,12 @@ fn run_command<A, C, E>(
}
window::Action::Resize(id, size) => {
if let Some(window) = window_manager.get_mut(id) {
- window.raw.set_inner_size(winit::dpi::LogicalSize {
- width: size.width,
- height: size.height,
- });
+ let _ = window.raw.request_inner_size(
+ winit::dpi::LogicalSize {
+ width: size.width,
+ height: size.height,
+ },
+ );
}
}
window::Action::FetchSize(id, callback) => {
@@ -942,11 +942,25 @@ fn run_command<A, C, E>(
.expect("Send message to event loop");
}
}
+ window::Action::FetchMaximized(id, callback) => {
+ if let Some(window) = window_manager.get_mut(id) {
+ proxy
+ .send_event(callback(window.raw.is_maximized()))
+ .expect("Send message to event loop");
+ }
+ }
window::Action::Maximize(id, maximized) => {
if let Some(window) = window_manager.get_mut(id) {
window.raw.set_maximized(maximized);
}
}
+ window::Action::FetchMinimized(id, callback) => {
+ if let Some(window) = window_manager.get_mut(id) {
+ proxy
+ .send_event(callback(window.raw.is_minimized()))
+ .expect("Send message to event loop");
+ }
+ }
window::Action::Minimize(id, minimized) => {
if let Some(window) = window_manager.get_mut(id) {
window.raw.set_minimized(minimized);
@@ -1153,60 +1167,20 @@ where
/// Returns true if the provided event should cause an [`Application`] to
/// exit.
pub fn user_force_quit(
- event: &winit::event::WindowEvent<'_>,
- _modifiers: winit::event::ModifiersState,
+ event: &winit::event::WindowEvent,
+ _modifiers: winit::keyboard::ModifiersState,
) -> bool {
match event {
#[cfg(target_os = "macos")]
winit::event::WindowEvent::KeyboardInput {
- input:
- winit::event::KeyboardInput {
- virtual_keycode: Some(winit::event::VirtualKeyCode::Q),
+ event:
+ winit::event::KeyEvent {
+ logical_key: winit::keyboard::Key::Character(c),
state: winit::event::ElementState::Pressed,
..
},
..
- } if _modifiers.logo() => true,
+ } if c == "q" && _modifiers.super_key() => true,
_ => false,
}
}
-
-#[cfg(not(target_arch = "wasm32"))]
-mod platform {
- pub fn run<T, F>(
- mut event_loop: winit::event_loop::EventLoop<T>,
- event_handler: F,
- ) -> Result<(), super::Error>
- where
- F: 'static
- + FnMut(
- winit::event::Event<'_, T>,
- &winit::event_loop::EventLoopWindowTarget<T>,
- &mut winit::event_loop::ControlFlow,
- ),
- {
- use winit::platform::run_return::EventLoopExtRunReturn;
-
- let _ = event_loop.run_return(event_handler);
-
- Ok(())
- }
-}
-
-#[cfg(target_arch = "wasm32")]
-mod platform {
- pub fn run<T, F>(
- event_loop: winit::event_loop::EventLoop<T>,
- event_handler: F,
- ) -> !
- where
- F: 'static
- + FnMut(
- winit::event::Event<'_, T>,
- &winit::event_loop::EventLoopWindowTarget<T>,
- &mut winit::event_loop::ControlFlow,
- ),
- {
- event_loop.run(event_handler)
- }
-}
diff --git a/winit/src/multi_window/state.rs b/winit/src/multi_window/state.rs
index 03da5ad7..235771f4 100644
--- a/winit/src/multi_window/state.rs
+++ b/winit/src/multi_window/state.rs
@@ -21,7 +21,7 @@ where
viewport: Viewport,
viewport_version: u64,
cursor_position: Option<winit::dpi::PhysicalPosition<f64>>,
- modifiers: winit::event::ModifiersState,
+ modifiers: winit::keyboard::ModifiersState,
theme: <A::Renderer as core::Renderer>::Theme,
appearance: application::Appearance,
}
@@ -72,7 +72,7 @@ where
viewport,
viewport_version: 0,
cursor_position: None,
- modifiers: winit::event::ModifiersState::default(),
+ modifiers: winit::keyboard::ModifiersState::default(),
theme,
appearance,
}
@@ -119,7 +119,7 @@ where
}
/// Returns the current keyboard modifiers of the [`State`].
- pub fn modifiers(&self) -> winit::event::ModifiersState {
+ pub fn modifiers(&self) -> winit::keyboard::ModifiersState {
self.modifiers
}
@@ -142,7 +142,7 @@ where
pub fn update(
&mut self,
window: &Window,
- event: &WindowEvent<'_>,
+ event: &WindowEvent,
_debug: &mut crate::runtime::Debug,
) {
match event {
@@ -158,10 +158,9 @@ where
}
WindowEvent::ScaleFactorChanged {
scale_factor: new_scale_factor,
- new_inner_size,
+ ..
} => {
- let size =
- Size::new(new_inner_size.width, new_inner_size.height);
+ let size = self.viewport.physical_size();
self.viewport = Viewport::with_physical_size(
size,
@@ -180,13 +179,16 @@ where
self.cursor_position = None;
}
WindowEvent::ModifiersChanged(new_modifiers) => {
- self.modifiers = *new_modifiers;
+ self.modifiers = new_modifiers.state();
}
#[cfg(feature = "debug")]
WindowEvent::KeyboardInput {
- input:
- winit::event::KeyboardInput {
- virtual_keycode: Some(winit::event::VirtualKeyCode::F12),
+ event:
+ winit::event::KeyEvent {
+ logical_key:
+ winit::keyboard::Key::Named(
+ winit::keyboard::NamedKey::F12,
+ ),
state: winit::event::ElementState::Pressed,
..
},