Merge #597: Iced 0.12

52e32b6a69c751f68e461f4e858170e4ade6619d Update iced patch branch in guix build (edouardparis)
acea67bc35738843144204539833b5767f6c936e Remove transaparency from PickList for tiny-skia (edouardparis)
7801d8cb1f11511cd8e11a556add9e8c62656780 Change descriptor view in installer behind scrollable (edouardparis)
c39d544566a76b126fdc4613197d5b937f72fad3 Use wgpu with tiny-skia fallback (edouardparis)
4920291d04302a5d73196b1d8eeb6ce05d7753a2 fix ui button and badge width (edouardparis)
de371116371c61c5cd2e3bd0d44cdc849f14c5cf Change text size (edouardparis)
a24d9416f62712fc5a5c6fe5d8fa619d06f4006a gui: bump msrv 1.70 (edouardparis)
fdcc30236721f2cf7bdffe788127a35ca376d453 gui: iced-0.12 (edouardparis)
64a626d7e871fd82db27b8ddf773231fe1361755 gui: bump iced 0.10 (edouard)

Pull request description:

  This PR does the migration from iced 0.9 to iced 0.12.

  This new iced version has impact on the fonts size. I reverted the size according to the original UX figma file.

  The new backend renderer is the `wgpu` with `tiny-skia` as a fallback. `wgpu` is the first class citizen of the iced renderers, it supports everything. The `tiny-skia` has some layout problems and does not support some features that is the reason why this PR introduces tiny change in the theme or long string display (ad69711c4a, 88fd0f18e2).

  In order to keep the MSRV as low as possible, a custom patch of the crates `iced_winit`,`iced_style`, `iced_futures` is added to the Cargo.toml

ACKs for top commit:
  darosior:
    ACK 52e32b6a69c751f68e461f4e858170e4ade6619d -- it's been tested a bunch, in particular by Kevin.

Tree-SHA512: 6afda45c227f0dd864c59aee100895af3f0d511c5a425f4d032c5891f900f2e438de778adaa82c5213dba768e608bf7610b6c78d4cb10a2d7ae59f5b132938f4
This commit is contained in:
Antoine Poinsot 2024-05-03 13:32:55 +02:00
commit b602640d16
No known key found for this signature in database
GPG Key ID: E13FC145CD3F4304
43 changed files with 1979 additions and 1673 deletions

View File

@ -68,7 +68,7 @@ jobs:
strategy: strategy:
matrix: matrix:
toolchain: toolchain:
- 1.65.0 - 1.70.0
- nightly - nightly
os: os:
- ubuntu-latest - ubuntu-latest

View File

@ -20,7 +20,7 @@ replace-with = "vendored_sources"
[source."https://github.com/edouardparis/iced"] [source."https://github.com/edouardparis/iced"]
git = "https://github.com/edouardparis/iced" git = "https://github.com/edouardparis/iced"
branch = "fix-futures-recipe" branch = "patch-0.12.3"
replace-with = "vendored_sources" replace-with = "vendored_sources"
EOF EOF

2284
gui/Cargo.lock generated

File diff suppressed because it is too large Load Diff

View File

@ -21,9 +21,8 @@ liana_ui = { path = "ui" }
backtrace = "0.3" backtrace = "0.3"
hex = "0.4.3" hex = "0.4.3"
iced = { version = "0.9", default-features= false, features = ["tokio", "glow", "svg", "qr_code", "image"] } iced = { version = "0.12.1", default-features = false, features = ["tokio", "svg", "qr_code", "image", "lazy", "wgpu"] }
iced_native = "0.10" iced_runtime = "0.12.1"
iced_lazy = { version = "0.6"}
tokio = {version = "1.21.0", features = ["signal"]} tokio = {version = "1.21.0", features = ["signal"]}
serde = { version = "1.0", features = ["derive"] } serde = { version = "1.0", features = ["derive"] }
@ -48,8 +47,11 @@ bitcoin_hashes = "0.12"
reqwest = { version = "0.11", default-features=false, features = ["rustls-tls"] } reqwest = { version = "0.11", default-features=false, features = ["rustls-tls"] }
rust-ini = "0.19.0" rust-ini = "0.19.0"
[patch.crates-io] [patch.crates-io]
iced_futures = { git = "https://github.com/edouardparis/iced", branch = "fix-futures-recipe"} iced_style = { git = "https://github.com/edouardparis/iced", branch = "patch-0.12.3"}
iced_winit = { git = "https://github.com/edouardparis/iced", branch = "patch-0.12.3"}
iced_futures = { git = "https://github.com/edouardparis/iced", branch = "patch-0.12.3"}
[target.'cfg(windows)'.dependencies] [target.'cfg(windows)'.dependencies]
zip = { version = "0.6", default-features=false, features = ["bzip2", "deflate"] } zip = { version = "0.6", default-features=false, features = ["bzip2", "deflate"] }

View File

@ -9,7 +9,7 @@ let
pkgs = import <nixpkgs> {}; pkgs = import <nixpkgs> {};
in in
pkgs.mkShell rec { pkgs.mkShell rec {
buildInputs = [ buildInputs = with pkgs; [
pkgs.expat pkgs.expat
pkgs.fontconfig pkgs.fontconfig
pkgs.freetype pkgs.freetype
@ -17,6 +17,8 @@ pkgs.mkShell rec {
pkgs.libGL pkgs.libGL
pkgs.pkgconfig pkgs.pkgconfig
pkgs.udev pkgs.udev
pkgs.wayland
pkgs.libxkbcommon
pkgs.xorg.libX11 pkgs.xorg.libX11
pkgs.xorg.libXcursor pkgs.xorg.libXcursor
pkgs.xorg.libXi pkgs.xorg.libXi

View File

@ -302,13 +302,13 @@ impl VerifyAddressModal {
} }
pub struct ShowQrCodeModal { pub struct ShowQrCodeModal {
qr_code: qr_code::State, qr_code: qr_code::Data,
address: String, address: String,
} }
impl ShowQrCodeModal { impl ShowQrCodeModal {
pub fn new(address: &Address, index: ChildNumber) -> Option<Self> { pub fn new(address: &Address, index: ChildNumber) -> Option<Self> {
qr_code::State::new(format!("bitcoin:{}?index={}", address, index)) qr_code::Data::new(format!("bitcoin:{}?index={}", address, index))
.ok() .ok()
.map(|qr_code| Self { .map(|qr_code| Self {
qr_code, qr_code,

View File

@ -6,7 +6,6 @@ use liana_ui::{
color, color,
component::{amount::*, badge, button, form, text::*}, component::{amount::*, badge, button, form, text::*},
icon, theme, icon, theme,
util::Collection,
widget::*, widget::*,
}; };

View File

@ -8,7 +8,6 @@ use liana_ui::{
color, color,
component::{amount::*, button, card, event, form, text::*}, component::{amount::*, button, card, event, form, text::*},
icon, theme, icon, theme,
util::Collection,
widget::*, widget::*,
}; };

View File

@ -29,7 +29,6 @@ use liana_ui::{
}, },
image::*, image::*,
theme, theme,
util::Collection,
widget::*, widget::*,
}; };

View File

@ -23,7 +23,6 @@ use liana_ui::{
text::{self, *}, text::{self, *},
}, },
icon, theme, icon, theme,
util::Collection,
widget::*, widget::*,
}; };
@ -426,7 +425,7 @@ pub fn signatures<'a>(
Container::new(text(alias)) Container::new(text(alias))
.padding(10) .padding(10)
.style(theme::Container::Pill(theme::Pill::Simple)), .style(theme::Container::Pill(theme::Pill::Simple)),
value.to_string(), liana_ui::widget::Text::new(value.to_string()),
tooltip::Position::Bottom, tooltip::Position::Bottom,
) )
.style(theme::Container::Card(theme::Card::Simple)), .style(theme::Container::Card(theme::Card::Simple)),
@ -439,7 +438,9 @@ pub fn signatures<'a>(
}, },
)), )),
) )
.horizontal_scroll(scrollable::Properties::new().width(2).scroller_width(2)), .direction(scrollable::Direction::Horizontal(
scrollable::Properties::new().width(2).scroller_width(2),
)),
) )
.padding(15) .padding(15)
} else { } else {
@ -520,7 +521,7 @@ fn container_from_fg(
Container::new(text(alias)) Container::new(text(alias))
.padding(10) .padding(10)
.style(theme::Container::Pill(theme::Pill::Simple)), .style(theme::Container::Pill(theme::Pill::Simple)),
fg.to_string(), liana_ui::widget::Text::new(fg.to_string()),
tooltip::Position::Bottom, tooltip::Position::Bottom,
) )
.style(theme::Container::Card(theme::Card::Simple)), .style(theme::Container::Card(theme::Card::Simple)),
@ -594,7 +595,9 @@ pub fn path_view<'a>(
) )
.push(row_signed), .push(row_signed),
) )
.horizontal_scroll(scrollable::Properties::new().width(2).scroller_width(2)) .direction(scrollable::Direction::Horizontal(
scrollable::Properties::new().width(2).scroller_width(2),
))
.into() .into()
} }

View File

@ -4,7 +4,6 @@ use liana_ui::{
color, color,
component::{amount::*, badge, button, card, form, text::*}, component::{amount::*, badge, button, card, form, text::*},
icon, theme, icon, theme,
util::Collection,
widget::*, widget::*,
}; };

View File

@ -3,11 +3,10 @@ use std::collections::{HashMap, HashSet};
use iced::{ use iced::{
widget::{ widget::{
qr_code::{self, QRCode}, qr_code::{self, QRCode},
scrollable, scrollable, Space,
}, },
Alignment, Length, Alignment, Length,
}; };
use iced_native::widget::Space;
use liana::miniscript::bitcoin::{ use liana::miniscript::bitcoin::{
self, self,
@ -22,7 +21,6 @@ use liana_ui::{
text::{self, *}, text::{self, *},
}, },
icon, theme, icon, theme,
util::Collection,
widget::*, widget::*,
}; };
@ -94,10 +92,11 @@ pub fn receive<'a>(
Length::Fixed(10.0), Length::Fixed(10.0),
)), )),
) )
.horizontal_scroll( .direction(scrollable::Direction::Horizontal(
scrollable::Properties::new() scrollable::Properties::new()
.scroller_width(5), .width(2)
), .scroller_width(2),
)),
) )
.width(Length::Fill), .width(Length::Fill),
) )
@ -216,13 +215,13 @@ pub fn verify_address_modal<'a>(
.into() .into()
} }
pub fn qr_modal<'a>(qr: &'a qr_code::State, address: &'a String) -> Element<'a, Message> { pub fn qr_modal<'a>(qr: &'a qr_code::Data, address: &'a String) -> Element<'a, Message> {
Column::new() Column::new()
.push( .push(
Row::new() Row::new()
.push(Space::with_width(Length::Fill)) .push(Space::with_width(Length::Fill))
.push( .push(
Container::new(QRCode::new(qr).cell_size(8)) Container::new(QRCode::<liana_ui::theme::Theme>::new(qr).cell_size(8))
.padding(10) .padding(10)
.style(theme::Container::QrCode), .style(theme::Container::QrCode),
) )

View File

@ -13,7 +13,6 @@ use liana::miniscript::bitcoin::{
use liana_ui::{ use liana_ui::{
component::{amount::*, button, form, text::*}, component::{amount::*, button, form, text::*},
icon, theme, icon, theme,
util::*,
widget::*, widget::*,
}; };
@ -144,9 +143,10 @@ pub fn recovery_path_view<'a>(
selected: bool, selected: bool,
) -> Element<'a, Message> { ) -> Element<'a, Message> {
Row::new() Row::new()
.push(checkbox("", selected, move |_| { .push(
Message::CreateSpend(CreateSpendMessage::SelectPath(index)) checkbox("", selected)
})) .on_toggle(move |_| Message::CreateSpend(CreateSpendMessage::SelectPath(index))),
)
.push( .push(
Column::new() Column::new()
.push( .push(
@ -170,7 +170,7 @@ pub fn recovery_path_view<'a>(
Container::new(text(alias)) Container::new(text(alias))
.padding(5) .padding(5)
.style(theme::Container::Pill(theme::Pill::Simple)), .style(theme::Container::Pill(theme::Pill::Simple)),
fg.to_string(), liana_ui::widget::Text::new(fg.to_string()),
tooltip::Position::Bottom, tooltip::Position::Bottom,
) )
.style(theme::Container::Card(theme::Card::Simple)), .style(theme::Container::Card(theme::Card::Simple)),

View File

@ -18,7 +18,6 @@ use liana_ui::{
color, color,
component::{badge, button, card, form, separation, text::*, tooltip::tooltip}, component::{badge, button, card, form, separation, text::*, tooltip::tooltip},
icon, theme, icon, theme,
util::Collection,
widget::*, widget::*,
}; };
@ -633,9 +632,9 @@ pub fn wallet_settings<'a>(
.push(text(descriptor.to_owned()).small()) .push(text(descriptor.to_owned()).small())
.push(Space::with_height(Length::Fixed(5.0))), .push(Space::with_height(Length::Fixed(5.0))),
) )
.horizontal_scroll( .direction(scrollable::Direction::Horizontal(
scrollable::Properties::new().width(5).scroller_width(5), scrollable::Properties::new().width(5).scroller_width(5),
), )),
) )
.push( .push(
Row::new() Row::new()

View File

@ -15,7 +15,6 @@ use liana_ui::{
color, color,
component::{amount::*, badge, button, form, text::*}, component::{amount::*, badge, button, form, text::*},
icon, theme, icon, theme,
util::Collection,
widget::*, widget::*,
}; };
@ -405,9 +404,8 @@ pub fn recipient_view<'a>(
None None
}) })
.push(tooltip::Tooltip::new( .push(tooltip::Tooltip::new(
checkbox("MAX", is_max_selected, move |_| { checkbox("MAX", is_max_selected)
CreateSpendMessage::SendMaxToRecipient(index) .on_toggle(move |_| CreateSpendMessage::SendMaxToRecipient(index)),
}),
// Add spaces at end so that text is padded at screen edge. // Add spaces at end so that text is padded at screen edge.
"Total amount remaining after paying fee and any other recipients ", "Total amount remaining after paying fee and any other recipients ",
tooltip::Position::Bottom, tooltip::Position::Bottom,
@ -431,9 +429,11 @@ fn coin_list_view<'a>(
Row::new() Row::new()
.push( .push(
Row::new() Row::new()
.push(checkbox("", selected, move |_| { .push(
Message::CreateSpend(CreateSpendMessage::SelectCoin(i)) checkbox("", selected).on_toggle(move |_| {
})) Message::CreateSpend(CreateSpendMessage::SelectCoin(i))
}),
)
.push( .push(
if let Some(label) = coins_labels.get(&coin.outpoint.to_string()) { if let Some(label) = coins_labels.get(&coin.outpoint.to_string()) {
Container::new(p1_regular(label)).width(Length::Fill) Container::new(p1_regular(label)).width(Length::Fill)

View File

@ -7,7 +7,6 @@ use liana_ui::{
color, color,
component::{amount::*, badge, button, card, form, text::*}, component::{amount::*, badge, button, card, form, text::*},
icon, theme, icon, theme,
util::Collection,
widget::*, widget::*,
}; };

View File

@ -1626,7 +1626,7 @@ impl From<BackupDescriptor> for Box<dyn Step> {
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use super::*; use super::*;
use iced_native::command::Action; use iced_runtime::command::Action;
use std::sync::{Arc, Mutex}; use std::sync::{Arc, Mutex};
pub struct Sandbox<S: Step> { pub struct Sandbox<S: Step> {

View File

@ -18,7 +18,6 @@ use liana_ui::{
tooltip, tooltip,
}, },
icon, image, theme, icon, image, theme,
util::Collection,
widget::*, widget::*,
}; };
@ -319,7 +318,9 @@ pub fn define_descriptor<'a>(
) )
.padding(5), .padding(5),
) )
.horizontal_scroll(Properties::new().width(3).scroller_width(3)), .direction(scrollable::Direction::Horizontal(
Properties::new().width(3).scroller_width(3),
)),
), ),
)) ))
.spacing(10); .spacing(10);
@ -436,7 +437,9 @@ pub fn recovery_path_view(
) )
.padding(5), .padding(5),
) )
.horizontal_scroll(Properties::new().width(3).scroller_width(3)), .direction(scrollable::Direction::Horizontal(
Properties::new().width(3).scroller_width(3),
)),
), ),
), ),
) )
@ -568,9 +571,9 @@ pub fn signer_xpubs(xpubs: &[String]) -> Element<Message> {
.push( .push(
Container::new( Container::new(
scrollable(Container::new(text(xpub).small()).padding(10)) scrollable(Container::new(text(xpub).small()).padding(10))
.horizontal_scroll( .direction(scrollable::Direction::Horizontal(
Properties::new().width(5).scroller_width(5), Properties::new().width(5).scroller_width(5),
), )),
) )
.width(Length::Fill), .width(Length::Fill),
) )
@ -641,9 +644,9 @@ pub fn hardware_wallet_xpubs<'a>(
.push( .push(
Container::new( Container::new(
scrollable(Container::new(text(xpub).small()).padding(10)) scrollable(Container::new(text(xpub).small()).padding(10))
.horizontal_scroll( .direction(scrollable::Direction::Horizontal(
Properties::new().width(5).scroller_width(5), Properties::new().width(5).scroller_width(5),
), )),
) )
.width(Length::Fill), .width(Length::Fill),
) )
@ -716,11 +719,10 @@ pub fn participate_xpub<'a>(
.push(signer) .push(signer)
.width(Length::Fill), .width(Length::Fill),
) )
.push(checkbox( .push(
"I have shared my extended public key", checkbox("I have shared my extended public key", shared)
shared, .on_toggle(Message::UserActionDone),
Message::UserActionDone, )
))
.push(if shared && network_valid { .push(if shared && network_valid {
button::primary(None, "Next") button::primary(None, "Next")
.width(Length::Fixed(200.0)) .width(Length::Fixed(200.0))
@ -756,7 +758,16 @@ pub fn register_descriptor<'a>(
.push(card::simple( .push(card::simple(
Column::new() Column::new()
.push(text("The descriptor:").small().bold()) .push(text("The descriptor:").small().bold())
.push(text(descriptor.clone()).small()) .push(
scrollable(
Column::new()
.push(text(descriptor.to_owned()).small())
.push(Space::with_height(Length::Fixed(5.0))),
)
.direction(scrollable::Direction::Horizontal(
scrollable::Properties::new().width(5).scroller_width(5),
)),
)
.push( .push(
Row::new().push(Column::new().width(Length::Fill)).push( Row::new().push(Column::new().width(Length::Fill)).push(
button::secondary(Some(icon::clipboard_icon()), "Copy") button::secondary(Some(icon::clipboard_icon()), "Copy")
@ -803,8 +814,7 @@ pub fn register_descriptor<'a>(
.push_maybe(created_desc.then_some(checkbox( .push_maybe(created_desc.then_some(checkbox(
"I have registered the descriptor on my device(s)", "I have registered the descriptor on my device(s)",
done, done,
Message::UserActionDone, ).on_toggle(Message::UserActionDone)))
)))
.push(if !created_desc || (done && !processing) { .push(if !created_desc || (done && !processing) {
button::primary(None, "Next") button::primary(None, "Next")
.on_press(Message::Next) .on_press(Message::Next)
@ -858,7 +868,16 @@ pub fn backup_descriptor<'a>(
.push(card::simple( .push(card::simple(
Column::new() Column::new()
.push(text("The descriptor:").small().bold()) .push(text("The descriptor:").small().bold())
.push(text(descriptor.clone()).small()) .push(
scrollable(
Column::new()
.push(text(descriptor.to_owned()).small())
.push(Space::with_height(Length::Fixed(5.0))),
)
.direction(scrollable::Direction::Horizontal(
scrollable::Properties::new().width(5).scroller_width(5),
)),
)
.push( .push(
Row::new().push(Column::new().width(Length::Fill)).push( Row::new().push(Column::new().width(Length::Fill)).push(
button::secondary(Some(icon::clipboard_icon()), "Copy") button::secondary(Some(icon::clipboard_icon()), "Copy")
@ -868,11 +887,9 @@ pub fn backup_descriptor<'a>(
.spacing(10) .spacing(10)
.max_width(1000), .max_width(1000),
)) ))
.push(checkbox( .push(
"I have backed up my descriptor", checkbox("I have backed up my descriptor", done).on_toggle(Message::UserActionDone),
done, )
Message::UserActionDone,
))
.push(if done { .push(if done {
button::primary(None, "Next") button::primary(None, "Next")
.on_press(Message::Next) .on_press(Message::Next)
@ -1401,7 +1418,9 @@ pub fn defined_descriptor_key<'a>(
.push(text(name).bold()) .push(text(name).bold())
.push(Space::with_height(Length::Fixed(5.0))), .push(Space::with_height(Length::Fixed(5.0))),
) )
.horizontal_scroll(Properties::new().width(5).scroller_width(5)), .direction(scrollable::Direction::Horizontal(
Properties::new().width(5).scroller_width(5),
)),
) )
.push(image::success_mark_icon().width(Length::Fixed(50.0))) .push(image::success_mark_icon().width(Length::Fixed(50.0)))
.push(Space::with_width(Length::Fixed(1.0))), .push(Space::with_width(Length::Fixed(1.0))),
@ -1672,7 +1691,7 @@ pub fn edit_sequence_modal<'a>(sequence: &form::Value<String>) -> Element<'a, Me
message::SequenceModal::SequenceEdited(v.to_string()), message::SequenceModal::SequenceEdited(v.to_string()),
)) ))
}) })
.step(144), // 144 blocks per day .step(144_u16), // 144 blocks per day
) )
.width(Length::Fixed(500.0)), .width(Length::Fixed(500.0)),
); );
@ -1815,11 +1834,7 @@ pub fn backup_mnemonic<'a>(
) )
}), }),
) )
.push(checkbox( .push(checkbox("I have backed up my mnemonic", done).on_toggle(Message::UserActionDone))
"I have backed up my mnemonic",
done,
Message::UserActionDone,
))
.push(if done { .push(if done {
button::primary(None, "Next") button::primary(None, "Next")
.on_press(Message::Next) .on_press(Message::Next)
@ -1997,8 +2012,8 @@ fn layout<'a>(
mod threshsold_input { mod threshsold_input {
use iced::alignment::{self, Alignment}; use iced::alignment::{self, Alignment};
use iced::widget::{component, Component};
use iced::Length; use iced::Length;
use iced_lazy::{self, Component};
use liana_ui::{component::text::*, icon, theme, widget::*}; use liana_ui::{component::text::*, icon, theme, widget::*};
pub struct ThresholdInput<Message> { pub struct ThresholdInput<Message> {
@ -2035,7 +2050,7 @@ mod threshsold_input {
} }
} }
impl<Message> Component<Message, iced::Renderer<theme::Theme>> for ThresholdInput<Message> { impl<Message> Component<Message, theme::Theme> for ThresholdInput<Message> {
type State = (); type State = ();
type Event = Event; type Event = Event;
@ -2085,7 +2100,7 @@ mod threshsold_input {
Message: 'a, Message: 'a,
{ {
fn from(numeric_input: ThresholdInput<Message>) -> Self { fn from(numeric_input: ThresholdInput<Message>) -> Self {
iced_lazy::component(numeric_input) component(numeric_input)
} }
} }
} }

View File

@ -1,6 +1,7 @@
use std::path::PathBuf; use std::path::PathBuf;
use iced::{ use iced::{
alignment::Horizontal,
widget::{tooltip, Space}, widget::{tooltip, Space},
Alignment, Command, Length, Subscription, Alignment, Command, Length, Subscription,
}; };
@ -10,7 +11,6 @@ use liana_ui::{
color, color,
component::{badge, button, card, modal::Modal, notification, text::*}, component::{badge, button, card, modal::Modal, notification, text::*},
icon, image, theme, icon, image, theme,
util::*,
widget::*, widget::*,
}; };
@ -330,7 +330,7 @@ impl DeleteWalletModal {
.push(icon::circle_check_icon().style(color::GREEN)) .push(icon::circle_check_icon().style(color::GREEN))
.push(text("Wallet successfully deleted").style(color::GREEN)) .push(text("Wallet successfully deleted").style(color::GREEN))
}) })
.align_x(iced_native::alignment::Horizontal::Center) .align_x(Horizontal::Center)
.width(Length::Fill), .width(Length::Fill),
), ),
) )

View File

@ -19,7 +19,6 @@ use liana_ui::{
color, color,
component::{button, notification, text::*}, component::{button, notification, text::*},
icon, icon,
util::Collection,
widget::*, widget::*,
}; };

View File

@ -64,6 +64,7 @@ impl Logger {
&& !metadata.target().starts_with("iced_glow") && !metadata.target().starts_with("iced_glow")
&& !metadata.target().starts_with("glow_glyph") && !metadata.target().starts_with("glow_glyph")
&& !metadata.target().starts_with("naga") && !metadata.target().starts_with("naga")
&& !metadata.target().starts_with("winit")
&& !metadata.target().starts_with("mio") && !metadata.target().starts_with("mio")
&& !metadata.target().starts_with("ledger_transport_hid") && !metadata.target().starts_with("ledger_transport_hid")
})), })),

View File

@ -5,9 +5,9 @@ use std::{error::Error, io::Write, path::PathBuf, process, str::FromStr};
use iced::{ use iced::{
event::{self, Event}, event::{self, Event},
executor, executor,
keyboard::{self, KeyCode}, keyboard::{self},
subscription,
widget::{focus_next, focus_previous}, widget::{focus_next, focus_previous},
window::settings::PlatformSpecific,
Application, Command, Settings, Subscription, Application, Command, Settings, Subscription,
}; };
use tracing::{error, info}; use tracing::{error, info};
@ -90,12 +90,19 @@ pub enum Key {
#[derive(Debug)] #[derive(Debug)]
pub enum Message { pub enum Message {
CtrlC, CtrlC,
FontLoaded(Result<(), iced::font::Error>),
Launch(Box<launcher::Message>), Launch(Box<launcher::Message>),
Install(Box<installer::Message>), Install(Box<installer::Message>),
Load(Box<loader::Message>), Load(Box<loader::Message>),
Run(Box<app::Message>), Run(Box<app::Message>),
Event(iced_native::Event),
KeyPressed(Key), KeyPressed(Key),
Event(iced::Event),
}
impl From<Result<(), iced::font::Error>> for Message {
fn from(value: Result<(), iced::font::Error>) -> Self {
Self::FontLoaded(value)
}
} }
async fn ctrl_c() -> Result<(), ()> { async fn ctrl_c() -> Result<(), ()> {
@ -121,17 +128,12 @@ impl Application for GUI {
fn new((config, log_level): (Config, Option<LevelFilter>)) -> (GUI, Command<Self::Message>) { fn new((config, log_level): (Config, Option<LevelFilter>)) -> (GUI, Command<Self::Message>) {
let logger = Logger::setup(log_level.unwrap_or(LevelFilter::INFO)); let logger = Logger::setup(log_level.unwrap_or(LevelFilter::INFO));
match config { let mut cmds = font::loads();
cmds.push(Command::perform(ctrl_c(), |_| Message::CtrlC));
let state = match config {
Config::Launcher(datadir_path) => { Config::Launcher(datadir_path) => {
let launcher = Launcher::new(datadir_path); let launcher = Launcher::new(datadir_path);
( State::Launcher(Box::new(launcher))
Self {
state: State::Launcher(Box::new(launcher)),
logger,
log_level,
},
Command::perform(ctrl_c(), |_| Message::CtrlC),
)
} }
Config::Install(datadir_path, network) => { Config::Install(datadir_path, network) => {
if !datadir_path.exists() { if !datadir_path.exists() {
@ -151,17 +153,8 @@ impl Application for GUI {
log_level.unwrap_or(LevelFilter::INFO), log_level.unwrap_or(LevelFilter::INFO),
); );
let (install, command) = Installer::new(datadir_path, network); let (install, command) = Installer::new(datadir_path, network);
( cmds.push(command.map(|msg| Message::Install(Box::new(msg))));
Self { State::Installer(Box::new(install))
state: State::Installer(Box::new(install)),
logger,
log_level,
},
Command::batch(vec![
command.map(|msg| Message::Install(Box::new(msg))),
Command::perform(ctrl_c(), |_| Message::CtrlC),
]),
)
} }
Config::Run(datadir_path, cfg, network) => { Config::Run(datadir_path, cfg, network) => {
logger.set_running_mode( logger.set_running_mode(
@ -170,37 +163,31 @@ impl Application for GUI {
log_level.unwrap_or_else(|| cfg.log_level().unwrap_or(LevelFilter::INFO)), log_level.unwrap_or_else(|| cfg.log_level().unwrap_or(LevelFilter::INFO)),
); );
let (loader, command) = Loader::new(datadir_path, cfg, network, None); let (loader, command) = Loader::new(datadir_path, cfg, network, None);
( cmds.push(command.map(|msg| Message::Load(Box::new(msg))));
Self { State::Loader(Box::new(loader))
state: State::Loader(Box::new(loader)),
logger,
log_level,
},
Command::batch(vec![
command.map(|msg| Message::Load(Box::new(msg))),
Command::perform(ctrl_c(), |_| Message::CtrlC),
]),
)
} }
} };
(
Self {
state,
logger,
log_level,
},
Command::batch(cmds),
)
} }
fn update(&mut self, message: Self::Message) -> Command<Self::Message> { fn update(&mut self, message: Self::Message) -> Command<Self::Message> {
match (&mut self.state, message) { match (&mut self.state, message) {
(_, Message::CtrlC) (_, Message::CtrlC)
| ( | (_, Message::Event(iced::Event::Window(_, iced::window::Event::CloseRequested))) => {
_,
Message::Event(iced_native::Event::Window(
iced_native::window::Event::CloseRequested,
)),
) => {
match &mut self.state { match &mut self.state {
State::Loader(s) => s.stop(), State::Loader(s) => s.stop(),
State::Launcher(s) => s.stop(), State::Launcher(s) => s.stop(),
State::Installer(s) => s.stop(), State::Installer(s) => s.stop(),
State::App(s) => s.stop(), State::App(s) => s.stop(),
}; };
iced::window::close() iced::window::close(iced::window::Id::MAIN)
} }
(_, Message::KeyPressed(Key::Tab(shift))) => { (_, Message::KeyPressed(Key::Tab(shift))) => {
log::debug!("Tab pressed!"); log::debug!("Tab pressed!");
@ -299,26 +286,26 @@ impl Application for GUI {
State::App(v) => v.subscription().map(|msg| Message::Run(Box::new(msg))), State::App(v) => v.subscription().map(|msg| Message::Run(Box::new(msg))),
State::Launcher(v) => v.subscription().map(|msg| Message::Launch(Box::new(msg))), State::Launcher(v) => v.subscription().map(|msg| Message::Launch(Box::new(msg))),
}, },
subscription::events_with(|event, status| match (&event, status) { iced::event::listen_with(|event, status| match (&event, status) {
( (
Event::Keyboard(keyboard::Event::KeyPressed { Event::Keyboard(keyboard::Event::KeyPressed {
key_code: KeyCode::Tab, key: iced::keyboard::Key::Named(iced::keyboard::key::Named::Tab),
modifiers, modifiers,
.. ..
}), }),
event::Status::Ignored, event::Status::Ignored,
) => Some(Message::KeyPressed(Key::Tab(modifiers.shift()))), ) => Some(Message::KeyPressed(Key::Tab(modifiers.shift()))),
( (
iced::Event::Window(iced_native::window::Event::CloseRequested), iced::Event::Window(_, iced::window::Event::CloseRequested),
event::Status::Ignored, event::Status::Ignored,
) => Some(Message::Event(event)), ) => Some(Message::Event(event)),
_ => None, _ => None,
}), }),
]) ])
.with_filter(|(event, _status)| { .with_filter(|event| {
matches!( matches!(
event, event,
iced::Event::Window(iced_native::window::Event::CloseRequested) iced::Event::Window(_, iced::window::Event::CloseRequested)
| iced::Event::Keyboard(_) | iced::Event::Keyboard(_)
) )
}) })
@ -453,11 +440,19 @@ fn main() -> Result<(), Box<dyn Error>> {
setup_panic_hook(); setup_panic_hook();
let mut settings = Settings::with_flags((config, log_level)); let mut settings = Settings::with_flags((config, log_level));
settings.id = Some("liana-gui".to_string());
settings.window.icon = Some(image::liana_app_icon()); settings.window.icon = Some(image::liana_app_icon());
settings.default_text_size = text::P1_SIZE.into(); settings.default_text_size = text::P1_SIZE.into();
settings.default_font = Some(font::REGULAR_BYTES); settings.default_font = liana_ui::font::REGULAR;
settings.exit_on_close_request = false; settings.window.exit_on_close_request = false;
settings.id = Some("Liana".to_string());
#[cfg(target_os = "linux")]
{
settings.window.platform_specific = PlatformSpecific {
application_id: "Liana".to_string(),
};
}
if let Err(e) = GUI::run(settings) { if let Err(e) = GUI::run(settings) {
return Err(format!("Failed to launch UI: {}", e).into()); return Err(format!("Failed to launch UI: {}", e).into());

View File

@ -1,6 +1,6 @@
use std::sync::Arc; use std::sync::Arc;
use iced_native::command::Action; use iced_runtime::command::Action;
use crate::{ use crate::{
app::{cache::Cache, message::Message, state::State, wallet::Wallet}, app::{cache::Cache, message::Message, state::State, wallet::Wallet},

View File

@ -6,8 +6,6 @@ edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies] [dependencies]
iced = { version = "0.9", default_features = false, features = ["svg", "image", "glow"] } iced = { version = "0.12.1", default-features = false, features = ["svg", "image", "lazy", "qr_code", "advanced", "webgl"] }
iced_native = "0.10"
iced_lazy = { version = "0.6"}
bitcoin = "0.31" bitcoin = "0.31"
chrono = "0.4" chrono = "0.4"

View File

@ -6,8 +6,7 @@ edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies] [dependencies]
iced = "0.9" iced = "0.10"
iced_native = "0.10"
web-sys = "0.3.61" web-sys = "0.3.61"
chrono = "0.4" chrono = "0.4"
liana_ui = { path = "../.." } liana_ui = { path = "../.." }

View File

@ -2,11 +2,12 @@ mod section;
use iced::widget::{button, column, container, row, scrollable, text, Space}; use iced::widget::{button, column, container, row, scrollable, text, Space};
use iced::{executor, Application, Command, Length, Settings, Subscription}; use iced::{executor, Application, Command, Length, Settings, Subscription};
use liana_ui::{component::text::*, image, theme, widget::*}; use liana_ui::{component::text::*, font, image, theme, widget::*};
pub fn main() -> iced::Result { pub fn main() -> iced::Result {
let mut settings = Settings::with_flags(Config {}); let mut settings = Settings::with_flags(Config {});
settings.default_text_size = P1_SIZE.into(); settings.default_text_size = P1_SIZE.into();
settings.default_font = font::REGULAR;
DesignSystem::run(settings) DesignSystem::run(settings)
} }
@ -21,11 +22,18 @@ struct DesignSystem {
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub enum Message { pub enum Message {
Event(iced_native::Event), FontLoaded(Result<(), iced::font::Error>),
Event(iced::Event),
Section(usize), Section(usize),
Ignore, Ignore,
} }
impl From<Result<(), iced::font::Error>> for Message {
fn from(res: Result<(), iced::font::Error>) -> Message {
Message::FontLoaded(res)
}
}
impl Application for DesignSystem { impl Application for DesignSystem {
type Message = Message; type Message = Message;
type Theme = theme::Theme; type Theme = theme::Theme;
@ -49,6 +57,9 @@ impl Application for DesignSystem {
], ],
current: 0, current: 0,
}; };
#[allow(unused_mut)]
let mut cmds: Vec<Command<Self::Message>> = font::loads();
#[cfg(target_arch = "wasm32")] #[cfg(target_arch = "wasm32")]
{ {
use iced_native::{command, window}; use iced_native::{command, window};
@ -57,16 +68,12 @@ impl Application for DesignSystem {
(window.inner_width().unwrap().as_f64().unwrap()) as u32, (window.inner_width().unwrap().as_f64().unwrap()) as u32,
(window.inner_height().unwrap().as_f64().unwrap()) as u32, (window.inner_height().unwrap().as_f64().unwrap()) as u32,
); );
( cmds.push(Command::single(command::Action::Window(
app, window::Action::Resize { width, height },
Command::single(command::Action::Window(window::Action::Resize { )));
width,
height,
})),
)
} }
#[cfg(not(target_arch = "wasm32"))]
(app, Command::none()) (app, Command::batch(cmds))
} }
fn update(&mut self, message: Message) -> Command<Self::Message> { fn update(&mut self, message: Message) -> Command<Self::Message> {
@ -76,10 +83,7 @@ impl Application for DesignSystem {
self.current = i; self.current = i;
} }
} }
Message::Event(iced::Event::Window(iced_native::window::Event::Resized { Message::Event(iced::Event::Window(iced::window::Event::Resized { width, height })) => {
width,
height,
})) => {
#[cfg(target_arch = "wasm32")] #[cfg(target_arch = "wasm32")]
{ {
use iced_native::{command, window}; use iced_native::{command, window};
@ -95,7 +99,7 @@ impl Application for DesignSystem {
} }
fn subscription(&self) -> Subscription<Self::Message> { fn subscription(&self) -> Subscription<Self::Message> {
iced_native::subscription::events().map(Self::Message::Event) iced::subscription::events().map(Self::Message::Event)
} }
fn view(&self) -> Element<Message> { fn view(&self) -> Element<Message> {

View File

@ -1,6 +1,6 @@
pub use bitcoin::Amount; pub use bitcoin::Amount;
use crate::{color, component::text::*, util::Collection, widget::*}; use crate::{color, component::text::*, widget::*};
pub fn amount<'a, T: 'a>(a: &Amount) -> Row<'a, T> { pub fn amount<'a, T: 'a>(a: &Amount) -> Row<'a, T> {
amount_with_size(a, P1_SIZE) amount_with_size(a, P1_SIZE)

View File

@ -108,7 +108,6 @@ pub fn badge_pill<'a, T: 'a>(label: &'a str, tooltip: &'a str) -> Container<'a,
tooltip::Tooltip::new( tooltip::Tooltip::new(
Container::new(text::p2_regular(label)) Container::new(text::p2_regular(label))
.padding(10) .padding(10)
.width(Length::Fill)
.center_x() .center_x()
.style(theme::Container::Pill(theme::Pill::Simple)), .style(theme::Container::Pill(theme::Pill::Simple)),
tooltip, tooltip,

View File

@ -1,19 +1,28 @@
use crate::{color, theme, widget::*}; use crate::{color, theme, widget::*};
use iced::widget::{button, container, row}; use iced::widget::{button, container, row};
use iced::{Alignment, Length}; use iced::Alignment;
use super::text::text; use super::text::text;
pub fn menu<'a, T: 'a>(icon: Option<Text<'a>>, t: &'static str) -> Button<'a, T> { pub fn menu<'a, T: 'a>(icon: Option<Text<'a>>, t: &'static str) -> Button<'a, T> {
button::Button::new(content(icon.map(|i| i.style(color::GREY_3)), t).padding(10)) button::Button::new(content_menu(icon.map(|i| i.style(color::GREY_3)), t).padding(10))
.style(theme::Button::Menu(false)) .style(theme::Button::Menu(false))
} }
pub fn menu_active<'a, T: 'a>(icon: Option<Text<'a>>, t: &'static str) -> Button<'a, T> { pub fn menu_active<'a, T: 'a>(icon: Option<Text<'a>>, t: &'static str) -> Button<'a, T> {
button::Button::new(content(icon.map(|i| i.style(color::GREY_3)), t).padding(10)) button::Button::new(content_menu(icon.map(|i| i.style(color::GREY_3)), t).padding(10))
.style(theme::Button::Menu(true)) .style(theme::Button::Menu(true))
} }
fn content_menu<'a, T: 'a>(icon: Option<Text<'a>>, t: &'static str) -> Container<'a, T> {
match icon {
None => container(text(t)).padding(5),
Some(i) => {
container(row![i, text(t)].spacing(10).align_items(Alignment::Center)).padding(5)
}
}
}
pub fn alert<'a, T: 'a>(icon: Option<Text<'a>>, t: &'static str) -> Button<'a, T> { pub fn alert<'a, T: 'a>(icon: Option<Text<'a>>, t: &'static str) -> Button<'a, T> {
button::Button::new(content(icon, t)).style(theme::Button::Destructive) button::Button::new(content(icon, t)).style(theme::Button::Destructive)
} }
@ -40,15 +49,9 @@ pub fn transparent_border<'a, T: 'a>(icon: Option<Text<'a>>, t: &'static str) ->
fn content<'a, T: 'a>(icon: Option<Text<'a>>, t: &'static str) -> Container<'a, T> { fn content<'a, T: 'a>(icon: Option<Text<'a>>, t: &'static str) -> Container<'a, T> {
match icon { match icon {
None => container(text(t)).width(Length::Fill).center_x().padding(5), None => container(text(t)).center_x().padding(5),
Some(i) => container( Some(i) => container(row![i, text(t)].spacing(10).align_items(Alignment::Center))
row![i, text(t)] .center_x()
.spacing(10) .padding(5),
.width(iced::Length::Fill)
.align_items(Alignment::Center),
)
.width(iced::Length::Fill)
.center_x()
.padding(5),
} }
} }

View File

@ -34,7 +34,7 @@ pub fn error<'a, T: 'a>(message: &'static str, error: String) -> Container<'a, T
.align_items(iced::Alignment::Center) .align_items(iced::Alignment::Center)
.push(icon::warning_icon().style(color::RED)) .push(icon::warning_icon().style(color::RED))
.push(text(message).style(color::RED)), .push(text(message).style(color::RED)),
error, Text::new(error),
iced::widget::tooltip::Position::Bottom, iced::widget::tooltip::Position::Bottom,
) )
.style(theme::Container::Card(theme::Card::Error)), .style(theme::Container::Card(theme::Card::Error)),

View File

@ -1,6 +1,8 @@
use crate::widget::*; use iced::{
use iced::widget::column; advanced,
use iced_lazy::{self, Component}; widget::{column, component, Button, Component},
Element,
};
use std::marker::PhantomData; use std::marker::PhantomData;
pub struct Collapse<'a, M, H, F, C> { pub struct Collapse<'a, M, H, F, C> {
@ -10,13 +12,15 @@ pub struct Collapse<'a, M, H, F, C> {
phantom: PhantomData<&'a M>, phantom: PhantomData<&'a M>,
} }
impl<'a, Message, T, H, F, C> Collapse<'a, Message, H, F, C> impl<'a, Message, T, H, F, C, Theme, Renderer> Collapse<'a, Message, H, F, C>
where where
Renderer: advanced::Renderer,
Theme: iced::widget::button::StyleSheet,
Message: 'a, Message: 'a,
T: Into<Message> + Clone + 'a, T: Into<Message> + Clone + 'a,
H: Fn() -> Button<'a, Event<T>> + 'a, H: Fn() -> Button<'a, Event<T>, Theme, Renderer> + 'a,
F: Fn() -> Button<'a, Event<T>> + 'a, F: Fn() -> Button<'a, Event<T>, Theme, Renderer> + 'a,
C: Fn() -> Element<'a, T> + 'a, C: Fn() -> Element<'a, T, Theme, Renderer> + 'a,
{ {
pub fn new(before: H, after: F, content: C) -> Self { pub fn new(before: H, after: F, content: C) -> Self {
Collapse { Collapse {
@ -34,13 +38,15 @@ pub enum Event<T> {
Collapse(bool), Collapse(bool),
} }
impl<'a, Message, T, H, F, C> Component<Message, iced::Renderer<crate::theme::Theme>> impl<'a, Message, T, H, F, C, Theme, Renderer> Component<Message, Theme, Renderer>
for Collapse<'a, Message, H, F, C> for Collapse<'a, Message, H, F, C>
where where
T: Into<Message> + Clone + 'a, T: Into<Message> + Clone + 'a,
H: Fn() -> Button<'a, Event<T>>, H: Fn() -> Button<'a, Event<T>, Theme, Renderer>,
F: Fn() -> Button<'a, Event<T>>, F: Fn() -> Button<'a, Event<T>, Theme, Renderer>,
C: Fn() -> Element<'a, T>, C: Fn() -> Element<'a, T, Theme, Renderer>,
Renderer: 'a + advanced::Renderer,
Theme: 'a + iced::widget::button::StyleSheet,
{ {
type State = bool; type State = bool;
type Event = Event<T>; type Event = Event<T>;
@ -55,7 +61,7 @@ where
} }
} }
fn view(&self, state: &Self::State) -> Element<Self::Event> { fn view(&self, state: &Self::State) -> Element<Self::Event, Theme, Renderer> {
if *state { if *state {
column![ column![
(self.after)().on_press(Event::Collapse(false)), (self.after)().on_press(Event::Collapse(false)),
@ -68,16 +74,18 @@ where
} }
} }
impl<'a, Message, T, H: 'a, F: 'a, C: 'a> From<Collapse<'a, Message, H, F, C>> impl<'a, Message, T, H: 'a, F: 'a, C: 'a, Theme, Renderer> From<Collapse<'a, Message, H, F, C>>
for Element<'a, Message> for Element<'a, Message, Theme, Renderer>
where where
Message: 'a, Message: 'a,
Renderer: 'a + advanced::Renderer,
Theme: 'a + iced::widget::button::StyleSheet,
T: Into<Message> + Clone + 'a, T: Into<Message> + Clone + 'a,
H: Fn() -> Button<'a, Event<T>>, H: Fn() -> Button<'a, Event<T>, Theme, Renderer>,
F: Fn() -> Button<'a, Event<T>>, F: Fn() -> Button<'a, Event<T>, Theme, Renderer>,
C: Fn() -> Element<'a, T>, C: Fn() -> Element<'a, T, Theme, Renderer>,
{ {
fn from(c: Collapse<'a, Message, H, F, C>) -> Self { fn from(c: Collapse<'a, Message, H, F, C>) -> Self {
iced_lazy::component(c) component(c)
} }
} }

View File

@ -2,7 +2,6 @@ use crate::{
color, color,
component::{amount, badge, text}, component::{amount, badge, text},
theme, theme,
util::Collection,
widget::*, widget::*,
}; };
use bitcoin::Amount; use bitcoin::Amount;
@ -12,7 +11,7 @@ use iced::{
}; };
pub fn unconfirmed_outgoing_event<'a, T: Clone + 'a>( pub fn unconfirmed_outgoing_event<'a, T: Clone + 'a>(
label: Option<iced::widget::Text<'a, iced::Renderer<theme::Theme>>>, label: Option<Text<'a>>,
amount: &Amount, amount: &Amount,
msg: T, msg: T,
) -> Container<'a, T> { ) -> Container<'a, T> {
@ -39,7 +38,7 @@ pub fn unconfirmed_outgoing_event<'a, T: Clone + 'a>(
} }
pub fn confirmed_outgoing_event<'a, T: Clone + 'a>( pub fn confirmed_outgoing_event<'a, T: Clone + 'a>(
label: Option<iced::widget::Text<'a, iced::Renderer<theme::Theme>>>, label: Option<Text<'a>>,
date: chrono::NaiveDateTime, date: chrono::NaiveDateTime,
amount: &Amount, amount: &Amount,
msg: T, msg: T,
@ -72,7 +71,7 @@ pub fn confirmed_outgoing_event<'a, T: Clone + 'a>(
} }
pub fn unconfirmed_incoming_event<'a, T: Clone + 'a>( pub fn unconfirmed_incoming_event<'a, T: Clone + 'a>(
label: Option<iced::widget::Text<'a, iced::Renderer<theme::Theme>>>, label: Option<Text<'a>>,
amount: &Amount, amount: &Amount,
msg: T, msg: T,
) -> Container<'a, T> { ) -> Container<'a, T> {
@ -99,7 +98,7 @@ pub fn unconfirmed_incoming_event<'a, T: Clone + 'a>(
} }
pub fn confirmed_incoming_event<'a, T: Clone + 'a>( pub fn confirmed_incoming_event<'a, T: Clone + 'a>(
label: Option<iced::widget::Text<'a, iced::Renderer<theme::Theme>>>, label: Option<Text<'a>>,
date: chrono::NaiveDateTime, date: chrono::NaiveDateTime,
amount: &Amount, amount: &Amount,
msg: T, msg: T,

View File

@ -1,7 +1,7 @@
use bitcoin::Denomination; use bitcoin::Denomination;
use iced::{widget::text_input, Length}; use iced::{widget::text_input, Length};
use crate::{color, component::text, theme, util::Collection, widget::*}; use crate::{color, component::text, theme, widget::*};
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub struct Value<T> { pub struct Value<T> {
@ -19,7 +19,7 @@ impl std::default::Default for Value<String> {
} }
pub struct Form<'a, Message> { pub struct Form<'a, Message> {
input: text_input::TextInput<'a, Message, iced::Renderer<theme::Theme>>, input: TextInput<'a, Message>,
warning: Option<&'a str>, warning: Option<&'a str>,
valid: bool, valid: bool,
} }

View File

@ -1,4 +1,4 @@
use crate::{color, component::text, icon, image, theme, util::*, widget::*}; use crate::{color, component::text, icon, image, theme, widget::*};
use iced::{ use iced::{
widget::{column, container, row, tooltip}, widget::{column, container, row, tooltip},
Alignment, Length, Alignment, Length,

View File

@ -1,23 +1,26 @@
/// modal widget from https://github.com/iced-rs/iced/blob/master/examples/modal/ /// modal widget from https://github.com/iced-rs/iced/blob/master/examples/modal/
use iced_native::alignment::Alignment; use iced::advanced::layout::{self, Layout};
use iced_native::widget::{self, Tree}; use iced::advanced::overlay;
use iced_native::{ use iced::advanced::renderer;
event, layout, mouse, overlay, renderer, Clipboard, Color, Element, Event, Layout, Length, use iced::advanced::widget::{self, Tree, Widget};
Point, Rectangle, Shell, Size, Widget, use iced::advanced::{self, Clipboard, Shell};
}; use iced::alignment::Alignment;
use iced::event;
use iced::mouse;
use iced::{Color, Element, Event, Length, Point, Rectangle, Size, Vector};
/// A widget that centers a modal element over some base element /// A widget that centers a modal element over some base element
pub struct Modal<'a, Message, Renderer> { pub struct Modal<'a, Message, Theme, Renderer> {
base: Element<'a, Message, Renderer>, base: Element<'a, Message, Theme, Renderer>,
modal: Element<'a, Message, Renderer>, modal: Element<'a, Message, Theme, Renderer>,
on_blur: Option<Message>, on_blur: Option<Message>,
} }
impl<'a, Message, Renderer> Modal<'a, Message, Renderer> { impl<'a, Message, Theme, Renderer> Modal<'a, Message, Theme, Renderer> {
/// Returns a new [`Modal`] /// Returns a new [`Modal`]
pub fn new( pub fn new(
base: impl Into<Element<'a, Message, Renderer>>, base: impl Into<Element<'a, Message, Theme, Renderer>>,
modal: impl Into<Element<'a, Message, Renderer>>, modal: impl Into<Element<'a, Message, Theme, Renderer>>,
) -> Self { ) -> Self {
Self { Self {
base: base.into(), base: base.into(),
@ -33,9 +36,10 @@ impl<'a, Message, Renderer> Modal<'a, Message, Renderer> {
} }
} }
impl<'a, Message, Renderer> Widget<Message, Renderer> for Modal<'a, Message, Renderer> impl<'a, Message, Theme, Renderer> Widget<Message, Theme, Renderer>
for Modal<'a, Message, Theme, Renderer>
where where
Renderer: iced_native::Renderer, Renderer: advanced::Renderer,
Message: Clone, Message: Clone,
{ {
fn children(&self) -> Vec<Tree> { fn children(&self) -> Vec<Tree> {
@ -46,47 +50,52 @@ where
tree.diff_children(&[&self.base, &self.modal]); tree.diff_children(&[&self.base, &self.modal]);
} }
fn width(&self) -> Length { fn size(&self) -> Size<Length> {
self.base.as_widget().width() self.base.as_widget().size()
} }
fn height(&self) -> Length { fn layout(
self.base.as_widget().height() &self,
} tree: &mut widget::Tree,
renderer: &Renderer,
fn layout(&self, renderer: &Renderer, limits: &layout::Limits) -> layout::Node { limits: &layout::Limits,
self.base.as_widget().layout(renderer, limits) ) -> layout::Node {
self.base
.as_widget()
.layout(&mut tree.children[0], renderer, limits)
} }
fn on_event( fn on_event(
&mut self, &mut self,
state: &mut Tree, state: &mut widget::Tree,
event: Event, event: Event,
layout: Layout<'_>, layout: Layout<'_>,
cursor_position: Point, cursor: mouse::Cursor,
renderer: &Renderer, renderer: &Renderer,
clipboard: &mut dyn Clipboard, clipboard: &mut dyn Clipboard,
shell: &mut Shell<'_, Message>, shell: &mut Shell<'_, Message>,
viewport: &Rectangle,
) -> event::Status { ) -> event::Status {
self.base.as_widget_mut().on_event( self.base.as_widget_mut().on_event(
&mut state.children[0], &mut state.children[0],
event, event,
layout, layout,
cursor_position, cursor,
renderer, renderer,
clipboard, clipboard,
shell, shell,
viewport,
) )
} }
fn draw( fn draw(
&self, &self,
state: &Tree, state: &widget::Tree,
renderer: &mut Renderer, renderer: &mut Renderer,
theme: &<Renderer as iced_native::Renderer>::Theme, theme: &Theme,
style: &renderer::Style, style: &renderer::Style,
layout: Layout<'_>, layout: Layout<'_>,
cursor_position: Point, cursor: mouse::Cursor,
viewport: &Rectangle, viewport: &Rectangle,
) { ) {
self.base.as_widget().draw( self.base.as_widget().draw(
@ -95,7 +104,7 @@ where
theme, theme,
style, style,
layout, layout,
cursor_position, cursor,
viewport, viewport,
); );
} }
@ -105,30 +114,29 @@ where
state: &'b mut Tree, state: &'b mut Tree,
layout: Layout<'_>, layout: Layout<'_>,
_renderer: &Renderer, _renderer: &Renderer,
) -> Option<overlay::Element<'b, Message, Renderer>> { translation: Vector,
Some(overlay::Element::new( ) -> Option<overlay::Element<'b, Message, Theme, Renderer>> {
layout.position(), Some(overlay::Element::new(Box::new(Overlay {
Box::new(Overlay { position: layout.position() + translation,
content: &mut self.modal, content: &mut self.modal,
tree: &mut state.children[1], tree: &mut state.children[1],
size: layout.bounds().size(), size: layout.bounds().size(),
on_blur: self.on_blur.clone(), on_blur: self.on_blur.clone(),
}), })))
))
} }
fn mouse_interaction( fn mouse_interaction(
&self, &self,
state: &Tree, state: &widget::Tree,
layout: Layout<'_>, layout: Layout<'_>,
cursor_position: Point, cursor: mouse::Cursor,
viewport: &Rectangle, viewport: &Rectangle,
renderer: &Renderer, renderer: &Renderer,
) -> mouse::Interaction { ) -> mouse::Interaction {
self.base.as_widget().mouse_interaction( self.base.as_widget().mouse_interaction(
&state.children[0], &state.children[0],
layout, layout,
cursor_position, cursor,
viewport, viewport,
renderer, renderer,
) )
@ -147,38 +155,39 @@ where
} }
} }
struct Overlay<'a, 'b, Message, Renderer> { struct Overlay<'a, 'b, Message, Theme, Renderer> {
content: &'b mut Element<'a, Message, Renderer>, position: Point,
content: &'b mut Element<'a, Message, Theme, Renderer>,
tree: &'b mut Tree, tree: &'b mut Tree,
size: Size, size: Size,
on_blur: Option<Message>, on_blur: Option<Message>,
} }
impl<'a, 'b, Message, Renderer> overlay::Overlay<Message, Renderer> impl<'a, 'b, Message, Theme, Renderer> overlay::Overlay<Message, Theme, Renderer>
for Overlay<'a, 'b, Message, Renderer> for Overlay<'a, 'b, Message, Theme, Renderer>
where where
Renderer: iced_native::Renderer, Renderer: advanced::Renderer,
Message: Clone, Message: Clone,
{ {
fn layout(&self, renderer: &Renderer, _bounds: Size, position: Point) -> layout::Node { fn layout(&mut self, renderer: &Renderer, _bounds: Size) -> layout::Node {
let limits = layout::Limits::new(Size::ZERO, self.size) let limits = layout::Limits::new(Size::ZERO, self.size)
.width(Length::Fill) .width(Length::Fill)
.height(Length::Fill); .height(Length::Fill);
let mut child = self.content.as_widget().layout(renderer, &limits); let child = self
child.align(Alignment::Center, Alignment::Center, limits.max()); .content
.as_widget()
.layout(self.tree, renderer, &limits)
.align(Alignment::Center, Alignment::Center, limits.max());
let mut node = layout::Node::with_children(self.size, vec![child]); layout::Node::with_children(self.size, vec![child]).move_to(self.position)
node.move_to(position);
node
} }
fn on_event( fn on_event(
&mut self, &mut self,
event: Event, event: Event,
layout: Layout<'_>, layout: Layout<'_>,
cursor_position: Point, cursor: mouse::Cursor,
renderer: &Renderer, renderer: &Renderer,
clipboard: &mut dyn Clipboard, clipboard: &mut dyn Clipboard,
shell: &mut Shell<'_, Message>, shell: &mut Shell<'_, Message>,
@ -187,7 +196,7 @@ where
if let Some(message) = self.on_blur.as_ref() { if let Some(message) = self.on_blur.as_ref() {
if let Event::Mouse(mouse::Event::ButtonPressed(mouse::Button::Left)) = &event { if let Event::Mouse(mouse::Event::ButtonPressed(mouse::Button::Left)) = &event {
if !content_bounds.contains(cursor_position) { if !cursor.is_over(content_bounds) {
shell.publish(message.clone()); shell.publish(message.clone());
return event::Status::Captured; return event::Status::Captured;
} }
@ -198,27 +207,26 @@ where
self.tree, self.tree,
event, event,
layout.children().next().unwrap(), layout.children().next().unwrap(),
cursor_position, cursor,
renderer, renderer,
clipboard, clipboard,
shell, shell,
&layout.bounds(),
) )
} }
fn draw( fn draw(
&self, &self,
renderer: &mut Renderer, renderer: &mut Renderer,
theme: &Renderer::Theme, theme: &Theme,
style: &renderer::Style, style: &renderer::Style,
layout: Layout<'_>, layout: Layout<'_>,
cursor_position: Point, cursor: mouse::Cursor,
) { ) {
renderer.fill_quad( renderer.fill_quad(
renderer::Quad { renderer::Quad {
bounds: layout.bounds(), bounds: layout.bounds(),
border_radius: renderer::BorderRadius::from(0.0), ..renderer::Quad::default()
border_width: 0.0,
border_color: Color::TRANSPARENT,
}, },
Color { Color {
a: 0.80, a: 0.80,
@ -232,7 +240,7 @@ where
theme, theme,
style, style,
layout.children().next().unwrap(), layout.children().next().unwrap(),
cursor_position, cursor,
&layout.bounds(), &layout.bounds(),
); );
} }
@ -254,26 +262,41 @@ where
fn mouse_interaction( fn mouse_interaction(
&self, &self,
layout: Layout<'_>, layout: Layout<'_>,
cursor_position: Point, cursor: mouse::Cursor,
viewport: &Rectangle, viewport: &Rectangle,
renderer: &Renderer, renderer: &Renderer,
) -> mouse::Interaction { ) -> mouse::Interaction {
self.content.as_widget().mouse_interaction( self.content.as_widget().mouse_interaction(
self.tree, self.tree,
layout.children().next().unwrap(), layout.children().next().unwrap(),
cursor_position, cursor,
viewport, viewport,
renderer, renderer,
) )
} }
fn overlay<'c>(
&'c mut self,
layout: Layout<'_>,
renderer: &Renderer,
) -> Option<overlay::Element<'c, Message, Theme, Renderer>> {
self.content.as_widget_mut().overlay(
self.tree,
layout.children().next().unwrap(),
renderer,
Vector::ZERO,
)
}
} }
impl<'a, Message, Renderer> From<Modal<'a, Message, Renderer>> for Element<'a, Message, Renderer> impl<'a, Message, Theme, Renderer> From<Modal<'a, Message, Theme, Renderer>>
for Element<'a, Message, Theme, Renderer>
where where
Renderer: 'a + iced_native::Renderer, Renderer: 'a + advanced::Renderer,
Message: 'a + Clone, Message: 'a + Clone,
Theme: 'a,
{ {
fn from(modal: Modal<'a, Message, Renderer>) -> Self { fn from(modal: Modal<'a, Message, Theme, Renderer>) -> Self {
Element::new(modal) Element::new(modal)
} }
} }

View File

@ -5,7 +5,6 @@ use crate::{
color, color,
component::{collapse, text}, component::{collapse, text},
icon, theme, icon, theme,
util::*,
widget::*, widget::*,
}; };
use iced::{ use iced::{

View File

@ -1,122 +1,94 @@
use crate::{font, theme::Theme}; use crate::{font, theme::Theme};
use std::borrow::Cow; use std::borrow::Cow;
// 40 * 1.2 pub const H1_SIZE: u16 = 40;
pub const H1_SIZE: u16 = 48; pub const H2_SIZE: u16 = 29;
// 29 * 1.2 pub const H3_SIZE: u16 = 24;
pub const H2_SIZE: u16 = 35; pub const H4_SIZE: u16 = 20;
// 24 * 1.2 pub const H5_SIZE: u16 = 18;
pub const H3_SIZE: u16 = 29; pub const P1_SIZE: u16 = 16;
// 20 * 1.2 pub const P2_SIZE: u16 = 14;
pub const H4_SIZE: u16 = 24; pub const CAPTION_SIZE: u16 = 12;
// 18 * 1.2
pub const H5_SIZE: u16 = 22;
// 16 * 1.2
pub const P1_SIZE: u16 = 20;
// 14 * 1.2
pub const P2_SIZE: u16 = 17;
// 12 * 1.2
pub const CAPTION_SIZE: u16 = 15;
pub fn h1<'a>(content: impl Into<Cow<'a, str>>) -> iced::widget::Text<'a, iced::Renderer<Theme>> { pub fn h1<'a>(content: impl Into<Cow<'a, str>>) -> iced::widget::Text<'a, Theme> {
iced::widget::Text::new(content) iced::widget::Text::new(content)
.font(font::BOLD) .font(font::BOLD)
.size(H1_SIZE) .size(H1_SIZE)
} }
pub fn h2<'a>(content: impl Into<Cow<'a, str>>) -> iced::widget::Text<'a, iced::Renderer<Theme>> { pub fn h2<'a>(content: impl Into<Cow<'a, str>>) -> iced::widget::Text<'a, Theme> {
iced::widget::Text::new(content) iced::widget::Text::new(content)
.font(font::BOLD) .font(font::BOLD)
.size(H2_SIZE) .size(H2_SIZE)
} }
pub fn h3<'a>(content: impl Into<Cow<'a, str>>) -> iced::widget::Text<'a, iced::Renderer<Theme>> { pub fn h3<'a>(content: impl Into<Cow<'a, str>>) -> iced::widget::Text<'a, Theme> {
iced::widget::Text::new(content) iced::widget::Text::new(content)
.font(font::BOLD) .font(font::BOLD)
.size(H3_SIZE) .size(H3_SIZE)
} }
pub fn h4_bold<'a>( pub fn h4_bold<'a>(content: impl Into<Cow<'a, str>>) -> iced::widget::Text<'a, Theme> {
content: impl Into<Cow<'a, str>>,
) -> iced::widget::Text<'a, iced::Renderer<Theme>> {
iced::widget::Text::new(content) iced::widget::Text::new(content)
.font(font::BOLD) .font(font::BOLD)
.size(H4_SIZE) .size(H4_SIZE)
} }
pub fn h4_regular<'a>( pub fn h4_regular<'a>(content: impl Into<Cow<'a, str>>) -> iced::widget::Text<'a, Theme> {
content: impl Into<Cow<'a, str>>,
) -> iced::widget::Text<'a, iced::Renderer<Theme>> {
iced::widget::Text::new(content) iced::widget::Text::new(content)
.font(font::REGULAR) .font(font::REGULAR)
.size(H4_SIZE) .size(H4_SIZE)
} }
pub fn h5_medium<'a>( pub fn h5_medium<'a>(content: impl Into<Cow<'a, str>>) -> iced::widget::Text<'a, Theme> {
content: impl Into<Cow<'a, str>>,
) -> iced::widget::Text<'a, iced::Renderer<Theme>> {
iced::widget::Text::new(content) iced::widget::Text::new(content)
.font(font::MEDIUM) .font(font::MEDIUM)
.size(H5_SIZE) .size(H5_SIZE)
} }
pub fn h5_regular<'a>( pub fn h5_regular<'a>(content: impl Into<Cow<'a, str>>) -> iced::widget::Text<'a, Theme> {
content: impl Into<Cow<'a, str>>,
) -> iced::widget::Text<'a, iced::Renderer<Theme>> {
iced::widget::Text::new(content) iced::widget::Text::new(content)
.font(font::REGULAR) .font(font::REGULAR)
.size(H5_SIZE) .size(H5_SIZE)
} }
pub fn p1_bold<'a>( pub fn p1_bold<'a>(content: impl Into<Cow<'a, str>>) -> iced::widget::Text<'a, Theme> {
content: impl Into<Cow<'a, str>>,
) -> iced::widget::Text<'a, iced::Renderer<Theme>> {
iced::widget::Text::new(content) iced::widget::Text::new(content)
.font(font::BOLD) .font(font::BOLD)
.size(P1_SIZE) .size(P1_SIZE)
} }
pub fn p1_medium<'a>( pub fn p1_medium<'a>(content: impl Into<Cow<'a, str>>) -> iced::widget::Text<'a, Theme> {
content: impl Into<Cow<'a, str>>,
) -> iced::widget::Text<'a, iced::Renderer<Theme>> {
iced::widget::Text::new(content) iced::widget::Text::new(content)
.font(font::MEDIUM) .font(font::MEDIUM)
.size(P1_SIZE) .size(P1_SIZE)
} }
pub fn p1_regular<'a>( pub fn p1_regular<'a>(content: impl Into<Cow<'a, str>>) -> iced::widget::Text<'a, Theme> {
content: impl Into<Cow<'a, str>>,
) -> iced::widget::Text<'a, iced::Renderer<Theme>> {
iced::widget::Text::new(content) iced::widget::Text::new(content)
.font(font::REGULAR) .font(font::REGULAR)
.size(P1_SIZE) .size(P1_SIZE)
} }
pub fn p2_medium<'a>( pub fn p2_medium<'a>(content: impl Into<Cow<'a, str>>) -> iced::widget::Text<'a, Theme> {
content: impl Into<Cow<'a, str>>,
) -> iced::widget::Text<'a, iced::Renderer<Theme>> {
iced::widget::Text::new(content) iced::widget::Text::new(content)
.font(font::MEDIUM) .font(font::MEDIUM)
.size(P2_SIZE) .size(P2_SIZE)
} }
pub fn p2_regular<'a>( pub fn p2_regular<'a>(content: impl Into<Cow<'a, str>>) -> iced::widget::Text<'a, Theme> {
content: impl Into<Cow<'a, str>>,
) -> iced::widget::Text<'a, iced::Renderer<Theme>> {
iced::widget::Text::new(content) iced::widget::Text::new(content)
.font(font::REGULAR) .font(font::REGULAR)
.size(P2_SIZE) .size(P2_SIZE)
} }
pub fn caption<'a>( pub fn caption<'a>(content: impl Into<Cow<'a, str>>) -> iced::widget::Text<'a, Theme> {
content: impl Into<Cow<'a, str>>,
) -> iced::widget::Text<'a, iced::Renderer<Theme>> {
iced::widget::Text::new(content) iced::widget::Text::new(content)
.font(font::REGULAR) .font(font::REGULAR)
.size(CAPTION_SIZE) .size(CAPTION_SIZE)
} }
pub fn text<'a>(content: impl Into<Cow<'a, str>>) -> iced::widget::Text<'a, iced::Renderer<Theme>> { pub fn text<'a>(content: impl Into<Cow<'a, str>>) -> iced::widget::Text<'a, Theme> {
p1_regular(content) p1_regular(content)
} }
@ -125,7 +97,7 @@ pub trait Text {
fn small(self) -> Self; fn small(self) -> Self;
} }
impl Text for iced::widget::Text<'_, iced::Renderer<Theme>> { impl Text for iced::widget::Text<'_, Theme> {
fn bold(self) -> Self { fn bold(self) -> Self {
self.font(font::BOLD) self.font(font::BOLD)
} }

View File

@ -1,29 +1,28 @@
use std::time::Instant; use std::time::Instant;
use super::theme::Theme; use iced::advanced::widget::{Operation, Tree};
use iced::advanced::{layout, mouse, overlay, renderer};
use iced::advanced::{Clipboard, Layout, Shell, Widget};
use iced::event::{self, Event};
use iced::{Alignment, Element, Length, Point, Rectangle, Size, Vector}; use iced::{Alignment, Element, Length, Point, Rectangle, Size, Vector};
use iced_native::widget::{Operation, Tree};
use iced_native::{event, layout, mouse, overlay, renderer};
use iced_native::{Clipboard, Event, Layout, Shell, Widget};
pub trait Toast { pub trait Toast {
fn title(&self) -> &str; fn title(&self) -> &str;
fn body(&self) -> &str; fn body(&self) -> &str;
} }
pub struct Manager<'a, Message, Renderer> { pub struct Manager<'a, Message, Theme, Renderer> {
content: Element<'a, Message, Renderer>, content: Element<'a, Message, Theme, Renderer>,
toasts: Vec<Element<'a, Message, Renderer>>, toasts: Vec<Element<'a, Message, Theme, Renderer>>,
} }
impl<'a, Message> Manager<'a, Message, iced::Renderer<Theme>> impl<'a, Message, Theme> Manager<'a, Message, Theme, iced::Renderer>
where where
Message: 'a + Clone, Message: 'a + Clone,
{ {
pub fn new( pub fn new(
content: impl Into<Element<'a, Message, iced::Renderer<Theme>>>, content: impl Into<Element<'a, Message, Theme, iced::Renderer>>,
toasts: Vec<Element<'a, Message, iced::Renderer<Theme>>>, toasts: Vec<Element<'a, Message, Theme, iced::Renderer>>,
) -> Self { ) -> Self {
Self { Self {
content: content.into(), content: content.into(),
@ -32,29 +31,33 @@ where
} }
} }
impl<'a, Message, Renderer> Widget<Message, Renderer> for Manager<'a, Message, Renderer> impl<'a, Message, Theme, Renderer> Widget<Message, Theme, Renderer>
for Manager<'a, Message, Theme, Renderer>
where where
Renderer: iced_native::Renderer, Renderer: iced::advanced::Renderer,
{ {
fn width(&self) -> Length { fn size(&self) -> Size<Length> {
self.content.as_widget().width() self.content.as_widget().size()
} }
fn height(&self) -> Length { fn layout(
self.content.as_widget().height() &self,
tree: &mut Tree,
renderer: &Renderer,
limits: &layout::Limits,
) -> layout::Node {
self.content
.as_widget()
.layout(&mut tree.children[0], renderer, limits)
} }
fn layout(&self, renderer: &Renderer, limits: &layout::Limits) -> layout::Node { fn tag(&self) -> iced::advanced::widget::tree::Tag {
self.content.as_widget().layout(renderer, limits)
}
fn tag(&self) -> iced_native::widget::tree::Tag {
struct Marker(Vec<Instant>); struct Marker(Vec<Instant>);
iced_native::widget::tree::Tag::of::<Marker>() iced::advanced::widget::tree::Tag::of::<Marker>()
} }
fn state(&self) -> iced_native::widget::tree::State { fn state(&self) -> iced::advanced::widget::tree::State {
iced_native::widget::tree::State::new(Vec::<Option<Instant>>::new()) iced::advanced::widget::tree::State::new(Vec::<Option<Instant>>::new())
} }
fn children(&self) -> Vec<Tree> { fn children(&self) -> Vec<Tree> {
@ -95,7 +98,7 @@ where
renderer: &Renderer, renderer: &Renderer,
operation: &mut dyn Operation<Message>, operation: &mut dyn Operation<Message>,
) { ) {
operation.container(None, &mut |operation| { operation.container(None, layout.bounds(), &mut |operation| {
self.content self.content
.as_widget() .as_widget()
.operate(&mut state.children[0], layout, renderer, operation); .operate(&mut state.children[0], layout, renderer, operation);
@ -107,10 +110,11 @@ where
state: &mut Tree, state: &mut Tree,
event: Event, event: Event,
layout: Layout<'_>, layout: Layout<'_>,
cursor_position: Point, cursor_position: iced::mouse::Cursor,
renderer: &Renderer, renderer: &Renderer,
clipboard: &mut dyn Clipboard, clipboard: &mut dyn Clipboard,
shell: &mut Shell<'_, Message>, shell: &mut Shell<'_, Message>,
viewport: &Rectangle,
) -> event::Status { ) -> event::Status {
self.content.as_widget_mut().on_event( self.content.as_widget_mut().on_event(
&mut state.children[0], &mut state.children[0],
@ -120,6 +124,7 @@ where
renderer, renderer,
clipboard, clipboard,
shell, shell,
viewport,
) )
} }
@ -127,10 +132,10 @@ where
&self, &self,
state: &Tree, state: &Tree,
renderer: &mut Renderer, renderer: &mut Renderer,
theme: &<Renderer as iced_native::Renderer>::Theme, theme: &Theme,
style: &renderer::Style, style: &renderer::Style,
layout: Layout<'_>, layout: Layout<'_>,
cursor_position: Point, cursor_position: iced::mouse::Cursor,
viewport: &Rectangle, viewport: &Rectangle,
) { ) {
self.content.as_widget().draw( self.content.as_widget().draw(
@ -148,7 +153,7 @@ where
&self, &self,
state: &Tree, state: &Tree,
layout: Layout<'_>, layout: Layout<'_>,
cursor_position: Point, cursor_position: iced::mouse::Cursor,
viewport: &Rectangle, viewport: &Rectangle,
renderer: &Renderer, renderer: &Renderer,
) -> mouse::Interaction { ) -> mouse::Interaction {
@ -166,25 +171,26 @@ where
state: &'b mut Tree, state: &'b mut Tree,
layout: Layout<'_>, layout: Layout<'_>,
renderer: &Renderer, renderer: &Renderer,
) -> Option<overlay::Element<'b, Message, Renderer>> { translation: Vector,
) -> Option<overlay::Element<'b, Message, Theme, Renderer>> {
let instants = state.state.downcast_mut::<Vec<Option<Instant>>>(); let instants = state.state.downcast_mut::<Vec<Option<Instant>>>();
let (content_state, toasts_state) = state.children.split_at_mut(1); let (content_state, toasts_state) = state.children.split_at_mut(1);
let content = self let content = self.content.as_widget_mut().overlay(
.content &mut content_state[0],
.as_widget_mut() layout,
.overlay(&mut content_state[0], layout, renderer); renderer,
translation,
);
let toasts = (!self.toasts.is_empty()).then(|| { let toasts = (!self.toasts.is_empty()).then(|| {
overlay::Element::new( overlay::Element::new(Box::new(Overlay {
layout.bounds().position(), position: layout.bounds().position() + translation,
Box::new(Overlay { toasts: &mut self.toasts,
toasts: &mut self.toasts, state: toasts_state,
state: toasts_state, instants,
instants, }))
}),
)
}); });
let overlays = content.into_iter().chain(toasts).collect::<Vec<_>>(); let overlays = content.into_iter().chain(toasts).collect::<Vec<_>>();
@ -192,18 +198,19 @@ where
} }
} }
struct Overlay<'a, 'b, Message, Renderer> { struct Overlay<'a, 'b, Message, Theme, Renderer> {
toasts: &'b mut [Element<'a, Message, Renderer>], position: Point,
toasts: &'b mut [Element<'a, Message, Theme, Renderer>],
state: &'b mut [Tree], state: &'b mut [Tree],
instants: &'b mut [Option<Instant>], instants: &'b mut [Option<Instant>],
} }
impl<'a, 'b, Message, Renderer> overlay::Overlay<Message, Renderer> impl<'a, 'b, Message, Theme, Renderer> overlay::Overlay<Message, Theme, Renderer>
for Overlay<'a, 'b, Message, Renderer> for Overlay<'a, 'b, Message, Theme, Renderer>
where where
Renderer: iced_native::Renderer, Renderer: iced::advanced::Renderer,
{ {
fn layout(&self, renderer: &Renderer, bounds: Size, position: Point) -> layout::Node { fn layout(&mut self, renderer: &Renderer, bounds: Size) -> layout::Node {
let limits = layout::Limits::new(Size::ZERO, bounds) let limits = layout::Limits::new(Size::ZERO, bounds)
.width(Length::Fill) .width(Length::Fill)
.height(Length::Fill); .height(Length::Fill);
@ -212,23 +219,27 @@ where
layout::flex::Axis::Vertical, layout::flex::Axis::Vertical,
renderer, renderer,
&limits, &limits,
Length::Fill,
Length::Fill,
10.into(), 10.into(),
10.0, 10.0,
Alignment::End, Alignment::End,
self.toasts, self.toasts,
self.state,
) )
.translate(Vector::new(position.x, position.y)) .translate(Vector::new(self.position.x, self.position.y))
} }
fn on_event( fn on_event(
&mut self, &mut self,
event: Event, event: Event,
layout: Layout<'_>, layout: Layout<'_>,
cursor_position: Point, cursor_position: iced::mouse::Cursor,
renderer: &Renderer, renderer: &Renderer,
clipboard: &mut dyn Clipboard, clipboard: &mut dyn Clipboard,
shell: &mut Shell<'_, Message>, shell: &mut Shell<'_, Message>,
) -> event::Status { ) -> event::Status {
let viewport = layout.bounds();
self.toasts self.toasts
.iter_mut() .iter_mut()
.zip(self.state.iter_mut()) .zip(self.state.iter_mut())
@ -246,6 +257,7 @@ where
renderer, renderer,
clipboard, clipboard,
&mut local_shell, &mut local_shell,
&viewport,
); );
if !local_shell.is_empty() { if !local_shell.is_empty() {
@ -262,10 +274,10 @@ where
fn draw( fn draw(
&self, &self,
renderer: &mut Renderer, renderer: &mut Renderer,
theme: &<Renderer as iced_native::Renderer>::Theme, theme: &Theme,
style: &renderer::Style, style: &renderer::Style,
layout: Layout<'_>, layout: Layout<'_>,
cursor_position: Point, cursor_position: iced::mouse::Cursor,
) { ) {
let viewport = layout.bounds(); let viewport = layout.bounds();
@ -291,9 +303,9 @@ where
&mut self, &mut self,
layout: Layout<'_>, layout: Layout<'_>,
renderer: &Renderer, renderer: &Renderer,
operation: &mut dyn iced_native::widget::Operation<Message>, operation: &mut dyn iced::advanced::widget::Operation<Message>,
) { ) {
operation.container(None, &mut |operation| { operation.container(None, layout.bounds(), &mut |operation| {
self.toasts self.toasts
.iter() .iter()
.zip(self.state.iter_mut()) .zip(self.state.iter_mut())
@ -309,7 +321,7 @@ where
fn mouse_interaction( fn mouse_interaction(
&self, &self,
layout: Layout<'_>, layout: Layout<'_>,
cursor_position: Point, cursor_position: iced::mouse::Cursor,
viewport: &Rectangle, viewport: &Rectangle,
renderer: &Renderer, renderer: &Renderer,
) -> mouse::Interaction { ) -> mouse::Interaction {
@ -330,19 +342,21 @@ where
.unwrap_or_default() .unwrap_or_default()
} }
fn is_over(&self, layout: Layout<'_>, cursor_position: Point) -> bool { fn is_over(&self, layout: Layout<'_>, _renderer: &Renderer, cursor_position: Point) -> bool {
layout layout
.children() .children()
.any(|layout| layout.bounds().contains(cursor_position)) .any(|layout| layout.bounds().contains(cursor_position))
} }
} }
impl<'a, Message, Renderer> From<Manager<'a, Message, Renderer>> for Element<'a, Message, Renderer> impl<'a, Message, Theme, Renderer> From<Manager<'a, Message, Theme, Renderer>>
for Element<'a, Message, Theme, Renderer>
where where
Renderer: 'a + iced_native::Renderer, Renderer: 'a + iced::advanced::Renderer,
Message: 'a, Message: 'a,
Theme: 'a,
{ {
fn from(manager: Manager<'a, Message, Renderer>) -> Self { fn from(manager: Manager<'a, Message, Theme, Renderer>) -> Self {
Element::new(manager) Element::new(manager)
} }
} }

View File

@ -1,18 +1,37 @@
use iced::Font; use iced::{
font::{Family, Stretch, Weight},
pub const BOLD: Font = Font::External { Command, Font,
name: "Bold",
bytes: include_bytes!("../static/fonts/IBMPlexSans-Bold.ttf"),
}; };
pub const MEDIUM: Font = Font::External { pub const BOLD: Font = Font {
name: "Regular", family: Family::Name("IBM Plex Sans"),
bytes: include_bytes!("../static/fonts/IBMPlexSans-Medium.ttf"), weight: Weight::Bold,
style: iced::font::Style::Normal,
stretch: Stretch::Normal,
}; };
pub const MEDIUM: Font = Font {
family: Family::Name("IBM Plex Sans"),
weight: Weight::Medium,
style: iced::font::Style::Normal,
stretch: Stretch::Normal,
};
pub const REGULAR: Font = Font::with_name("IBM Plex Sans");
pub const BOLD_BYTES: &[u8] = include_bytes!("../static/fonts/IBMPlexSans-Bold.ttf");
pub const MEDIUM_BYTES: &[u8] = include_bytes!("../static/fonts/IBMPlexSans-Medium.ttf");
pub const REGULAR_BYTES: &[u8] = include_bytes!("../static/fonts/IBMPlexSans-Regular.ttf"); pub const REGULAR_BYTES: &[u8] = include_bytes!("../static/fonts/IBMPlexSans-Regular.ttf");
pub const REGULAR: Font = Font::External { pub const ICONEX_ICONS_BYTES: &[u8] = include_bytes!("../static/icons/iconex/iconex-icons.ttf");
name: "Regular", pub const BOOTSTRAP_ICONS_BYTE: &[u8] = include_bytes!("../static/icons/bootstrap-icons.ttf");
bytes: REGULAR_BYTES,
}; pub fn loads<T: From<Result<(), iced::font::Error>> + 'static>() -> Vec<Command<T>> {
vec![
iced::font::load(BOLD_BYTES).map(T::from),
iced::font::load(MEDIUM_BYTES).map(T::from),
iced::font::load(REGULAR_BYTES).map(T::from),
iced::font::load(ICONEX_ICONS_BYTES).map(T::from),
iced::font::load(BOOTSTRAP_ICONS_BYTE).map(T::from),
]
}

View File

@ -1,10 +1,7 @@
use crate::{component::text::P1_SIZE, widget::*}; use crate::{component::text::P1_SIZE, widget::*};
use iced::{alignment, Font, Length}; use iced::{alignment, Font, Length};
const BOOTSTRAP_ICONS: Font = Font::External { const BOOTSTRAP_ICONS: Font = Font::with_name("bootstrap-icons");
name: "Bootstrap icons",
bytes: include_bytes!("../static/icons/bootstrap-icons.ttf"),
};
fn bootstrap_icon(unicode: char) -> Text<'static> { fn bootstrap_icon(unicode: char) -> Text<'static> {
Text::new(unicode.to_string()) Text::new(unicode.to_string())
@ -118,10 +115,7 @@ pub fn previous_icon() -> Text<'static> {
bootstrap_icon('\u{F284}') bootstrap_icon('\u{F284}')
} }
const ICONEX_ICONS: Font = Font::External { const ICONEX_ICONS: Font = Font::with_name("Untitled1");
name: "Iconex icons",
bytes: include_bytes!("../static/icons/iconex/iconex-icons.ttf"),
};
fn iconex_icon(unicode: char) -> Text<'static> { fn iconex_icon(unicode: char) -> Text<'static> {
Text::new(unicode.to_string()) Text::new(unicode.to_string())

View File

@ -4,22 +4,24 @@ pub mod font;
pub mod icon; pub mod icon;
pub mod image; pub mod image;
pub mod theme; pub mod theme;
pub mod util;
pub mod widget { pub mod widget {
#![allow(dead_code)] #![allow(dead_code)]
use crate::theme::Theme; use crate::theme::Theme;
pub type Renderer = iced::Renderer<Theme>; pub type Renderer = iced::Renderer;
pub type Element<'a, Message> = iced::Element<'a, Message, Renderer>; pub type Element<'a, Message> = iced::Element<'a, Message, Theme, Renderer>;
pub type Container<'a, Message> = iced::widget::Container<'a, Message, Renderer>; pub type Container<'a, Message> = iced::widget::Container<'a, Message, Theme, Renderer>;
pub type Column<'a, Message> = iced::widget::Column<'a, Message, Renderer>; pub type Column<'a, Message> = iced::widget::Column<'a, Message, Theme, Renderer>;
pub type Row<'a, Message> = iced::widget::Row<'a, Message, Renderer>; pub type Row<'a, Message> = iced::widget::Row<'a, Message, Theme, Renderer>;
pub type Button<'a, Message> = iced::widget::Button<'a, Message, Renderer>; pub type Button<'a, Message> = iced::widget::Button<'a, Message, Theme, Renderer>;
pub type Text<'a> = iced::widget::Text<'a, Renderer>; pub type CheckBox<'a, Message> = iced::widget::Checkbox<'a, Message, Theme, Renderer>;
pub type Tooltip<'a> = iced::widget::Tooltip<'a, Renderer>; pub type Text<'a> = iced::widget::Text<'a, Theme, Renderer>;
pub type ProgressBar = iced::widget::ProgressBar<Renderer>; pub type TextInput<'a, Message> = iced::widget::TextInput<'a, Message, Theme, Renderer>;
pub type PickList<'a, Message> = iced::widget::PickList<'a, Message, Renderer>; pub type Tooltip<'a> = iced::widget::Tooltip<'a, Theme, Renderer>;
pub type Scrollable<'a, Message> = iced::widget::Scrollable<'a, Message, Renderer>; pub type ProgressBar = iced::widget::ProgressBar<Theme>;
pub type Svg = iced::widget::Svg<Renderer>; pub type PickList<'a, T, L, V, Message> =
iced::widget::PickList<'a, T, L, V, Message, Theme, Renderer>;
pub type Scrollable<'a, Message> = iced::widget::Scrollable<'a, Message, Theme, Renderer>;
pub type Svg = iced::widget::Svg<Theme>;
} }

View File

@ -1,8 +1,8 @@
use iced::{ use iced::{
application, application,
widget::{ widget::{
button, checkbox, container, pick_list, progress_bar, radio, scrollable, slider, svg, text, button, checkbox, container, pick_list, progress_bar, qr_code, radio, scrollable, slider,
text_input, svg, text, text_input,
}, },
}; };
@ -44,9 +44,11 @@ impl iced::overlay::menu::StyleSheet for Theme {
iced::overlay::menu::Appearance { iced::overlay::menu::Appearance {
text_color: color::GREY_2, text_color: color::GREY_2,
background: color::GREY_6.into(), background: color::GREY_6.into(),
border_width: 0.0, border: iced::Border {
border_radius: 25.0, color: color::GREY_2,
border_color: color::GREY_2, width: 0.0,
radius: 25.0.into(),
},
selected_text_color: color::LIGHT_BLACK, selected_text_color: color::LIGHT_BLACK,
selected_background: color::GREEN.into(), selected_background: color::GREEN.into(),
} }
@ -103,21 +105,24 @@ impl container::StyleSheet for Theme {
match self { match self {
Theme::Light => match style { Theme::Light => match style {
Container::Transparent => container::Appearance { Container::Transparent => container::Appearance {
background: iced::Color::TRANSPARENT.into(), background: Some(iced::Color::TRANSPARENT.into()),
..container::Appearance::default() ..container::Appearance::default()
}, },
Container::Background => container::Appearance { Container::Background => container::Appearance {
background: color::GREY_2.into(), background: Some(color::GREY_2.into()),
..container::Appearance::default() ..container::Appearance::default()
}, },
Container::Foreground => container::Appearance { Container::Foreground => container::Appearance {
background: color::GREY_2.into(), background: Some(color::GREY_2.into()),
..container::Appearance::default() ..container::Appearance::default()
}, },
Container::Border => container::Appearance { Container::Border => container::Appearance {
background: iced::Color::TRANSPARENT.into(), background: Some(iced::Color::TRANSPARENT.into()),
border_width: 1.0, border: iced::Border {
border_color: color::LIGHT_BLACK, color: color::LIGHT_BLACK,
width: 1.0,
radius: 0.0.into(),
},
..container::Appearance::default() ..container::Appearance::default()
}, },
Container::Card(c) => c.appearance(self), Container::Card(c) => c.appearance(self),
@ -125,32 +130,39 @@ impl container::StyleSheet for Theme {
Container::Pill(c) => c.appearance(self), Container::Pill(c) => c.appearance(self),
Container::Notification(c) => c.appearance(self), Container::Notification(c) => c.appearance(self),
Container::Custom(c) => container::Appearance { Container::Custom(c) => container::Appearance {
background: (*c).into(), background: Some((*c).into()),
..container::Appearance::default() ..container::Appearance::default()
}, },
Container::QrCode => container::Appearance { Container::QrCode => container::Appearance {
background: color::WHITE.into(), background: Some(color::WHITE.into()),
border_radius: 25.0, border: iced::Border {
color: color::TRANSPARENT,
width: 0.0,
radius: 25.0.into(),
},
..container::Appearance::default() ..container::Appearance::default()
}, },
}, },
Theme::Dark => match style { Theme::Dark => match style {
Container::Transparent => container::Appearance { Container::Transparent => container::Appearance {
background: iced::Color::TRANSPARENT.into(), background: Some(iced::Color::TRANSPARENT.into()),
..container::Appearance::default() ..container::Appearance::default()
}, },
Container::Background => container::Appearance { Container::Background => container::Appearance {
background: color::LIGHT_BLACK.into(), background: Some(color::LIGHT_BLACK.into()),
..container::Appearance::default() ..container::Appearance::default()
}, },
Container::Foreground => container::Appearance { Container::Foreground => container::Appearance {
background: color::BLACK.into(), background: Some(color::BLACK.into()),
..container::Appearance::default() ..container::Appearance::default()
}, },
Container::Border => container::Appearance { Container::Border => container::Appearance {
background: iced::Color::TRANSPARENT.into(), background: Some(iced::Color::TRANSPARENT.into()),
border_width: 1.0, border: iced::Border {
border_color: color::GREY_3, color: color::GREY_3,
width: 1.0,
radius: 0.0.into(),
},
..container::Appearance::default() ..container::Appearance::default()
}, },
Container::Card(c) => c.appearance(self), Container::Card(c) => c.appearance(self),
@ -158,12 +170,16 @@ impl container::StyleSheet for Theme {
Container::Pill(c) => c.appearance(self), Container::Pill(c) => c.appearance(self),
Container::Notification(c) => c.appearance(self), Container::Notification(c) => c.appearance(self),
Container::Custom(c) => container::Appearance { Container::Custom(c) => container::Appearance {
background: (*c).into(), background: Some((*c).into()),
..container::Appearance::default() ..container::Appearance::default()
}, },
Container::QrCode => container::Appearance { Container::QrCode => container::Appearance {
background: color::WHITE.into(), background: Some(color::WHITE.into()),
border_radius: 25.0, border: iced::Border {
color: color::TRANSPARENT,
width: 0.0,
radius: 25.0.into(),
},
..container::Appearance::default() ..container::Appearance::default()
}, },
}, },
@ -201,34 +217,46 @@ impl Notification {
match theme { match theme {
Theme::Light => match self { Theme::Light => match self {
Self::Pending => container::Appearance { Self::Pending => container::Appearance {
background: color::GREEN.into(), background: Some(iced::Background::Color(color::GREEN)),
text_color: color::LIGHT_BLACK.into(), text_color: color::LIGHT_BLACK.into(),
border_width: 1.0, border: iced::Border {
border_color: color::GREEN, color: color::GREEN,
border_radius: 25.0, width: 1.0,
radius: 25.0.into(),
},
..container::Appearance::default()
}, },
Self::Error => container::Appearance { Self::Error => container::Appearance {
background: color::ORANGE.into(), background: Some(iced::Background::Color(color::ORANGE)),
text_color: color::LIGHT_BLACK.into(), text_color: color::LIGHT_BLACK.into(),
border_width: 1.0, border: iced::Border {
border_color: color::ORANGE, color: color::ORANGE,
border_radius: 25.0, width: 1.0,
radius: 25.0.into(),
},
..container::Appearance::default()
}, },
}, },
Theme::Dark => match self { Theme::Dark => match self {
Self::Pending => container::Appearance { Self::Pending => container::Appearance {
background: color::GREEN.into(), background: Some(iced::Background::Color(color::GREEN)),
text_color: color::LIGHT_BLACK.into(), text_color: color::LIGHT_BLACK.into(),
border_width: 1.0, border: iced::Border {
border_color: color::GREEN, color: color::GREEN,
border_radius: 25.0, width: 1.0,
radius: 25.0.into(),
},
..container::Appearance::default()
}, },
Self::Error => container::Appearance { Self::Error => container::Appearance {
background: color::ORANGE.into(), background: Some(iced::Background::Color(color::ORANGE)),
text_color: color::LIGHT_BLACK.into(), text_color: color::LIGHT_BLACK.into(),
border_width: 1.0, border: iced::Border {
border_color: color::ORANGE, color: color::ORANGE,
border_radius: 25.0, width: 1.0,
radius: 25.0.into(),
},
..container::Appearance::default()
}, },
}, },
} }
@ -250,65 +278,85 @@ impl Card {
match theme { match theme {
Theme::Light => match self { Theme::Light => match self {
Card::Simple => container::Appearance { Card::Simple => container::Appearance {
background: color::GREY_2.into(), background: Some(color::GREY_2.into()),
..container::Appearance::default() ..container::Appearance::default()
}, },
Card::Border => container::Appearance { Card::Border => container::Appearance {
background: iced::Color::TRANSPARENT.into(), background: Some(iced::Color::TRANSPARENT.into()),
border_radius: 10.0, border: iced::Border {
border_color: color::GREY_2, color: color::GREY_2,
border_width: 1.0, width: 1.0,
radius: 10.0.into(),
},
..container::Appearance::default() ..container::Appearance::default()
}, },
Card::Invalid => container::Appearance { Card::Invalid => container::Appearance {
background: color::GREY_2.into(), background: Some(color::GREY_2.into()),
text_color: color::BLACK.into(), text_color: color::BLACK.into(),
border_width: 1.0, border: iced::Border {
border_color: color::RED, color: color::RED,
width: 1.0,
radius: 0.0.into(),
},
..container::Appearance::default() ..container::Appearance::default()
}, },
Card::Error => container::Appearance { Card::Error => container::Appearance {
background: color::GREY_2.into(), background: Some(color::GREY_2.into()),
text_color: color::RED.into(), text_color: color::RED.into(),
border_width: 1.0, border: iced::Border {
border_color: color::RED, color: color::RED,
width: 1.0,
radius: 0.0.into(),
},
..container::Appearance::default() ..container::Appearance::default()
}, },
Card::Warning => container::Appearance { Card::Warning => container::Appearance {
background: color::ORANGE.into(), background: Some(color::ORANGE.into()),
text_color: color::GREY_2.into(), text_color: color::GREY_2.into(),
..container::Appearance::default() ..container::Appearance::default()
}, },
}, },
Theme::Dark => match self { Theme::Dark => match self {
Card::Simple => container::Appearance { Card::Simple => container::Appearance {
background: color::GREY_6.into(), background: Some(color::GREY_6.into()),
border_radius: 25.0, border: iced::Border {
color: color::TRANSPARENT,
width: 0.0,
radius: 25.0.into(),
},
..container::Appearance::default() ..container::Appearance::default()
}, },
Card::Border => container::Appearance { Card::Border => container::Appearance {
background: iced::Color::TRANSPARENT.into(), background: Some(iced::Color::TRANSPARENT.into()),
border_radius: 25.0, border: iced::Border {
border_color: color::GREY_5, color: color::GREY_5,
border_width: 1.0, width: 1.0,
radius: 25.0.into(),
},
..container::Appearance::default() ..container::Appearance::default()
}, },
Card::Invalid => container::Appearance { Card::Invalid => container::Appearance {
background: color::LIGHT_BLACK.into(), background: Some(color::LIGHT_BLACK.into()),
text_color: color::RED.into(), text_color: color::RED.into(),
border_width: 1.0, border: iced::Border {
border_radius: 25.0, color: color::RED,
border_color: color::RED, width: 1.0,
radius: 25.0.into(),
},
..container::Appearance::default()
}, },
Card::Error => container::Appearance { Card::Error => container::Appearance {
background: color::LIGHT_BLACK.into(), background: Some(color::LIGHT_BLACK.into()),
text_color: color::RED.into(), text_color: color::RED.into(),
border_width: 1.0, border: iced::Border {
border_color: color::RED, color: color::RED,
width: 1.0,
radius: 25.0.into(),
},
..container::Appearance::default() ..container::Appearance::default()
}, },
Card::Warning => container::Appearance { Card::Warning => container::Appearance {
background: color::ORANGE.into(), background: Some(color::ORANGE.into()),
text_color: color::LIGHT_BLACK.into(), text_color: color::LIGHT_BLACK.into(),
..container::Appearance::default() ..container::Appearance::default()
}, },
@ -328,13 +376,21 @@ impl Badge {
fn appearance(&self, _theme: &Theme) -> iced::widget::container::Appearance { fn appearance(&self, _theme: &Theme) -> iced::widget::container::Appearance {
match self { match self {
Self::Standard => container::Appearance { Self::Standard => container::Appearance {
border_radius: 40.0, border: iced::Border {
background: color::GREY_4.into(), color: color::TRANSPARENT,
width: 0.0,
radius: 40.0.into(),
},
background: Some(color::GREY_4.into()),
..container::Appearance::default() ..container::Appearance::default()
}, },
Self::Bitcoin => container::Appearance { Self::Bitcoin => container::Appearance {
border_radius: 40.0, border: iced::Border {
background: color::ORANGE.into(), color: color::TRANSPARENT,
width: 0.0,
radius: 40.0.into(),
},
background: Some(color::ORANGE.into()),
text_color: iced::Color::WHITE.into(), text_color: iced::Color::WHITE.into(),
..container::Appearance::default() ..container::Appearance::default()
}, },
@ -355,30 +411,44 @@ impl Pill {
fn appearance(&self, _theme: &Theme) -> iced::widget::container::Appearance { fn appearance(&self, _theme: &Theme) -> iced::widget::container::Appearance {
match self { match self {
Self::Primary => container::Appearance { Self::Primary => container::Appearance {
background: color::GREEN.into(), background: Some(color::GREEN.into()),
border_radius: 25.0, border: iced::Border {
color: color::TRANSPARENT,
width: 0.0,
radius: 25.0.into(),
},
text_color: color::LIGHT_BLACK.into(), text_color: color::LIGHT_BLACK.into(),
..container::Appearance::default() ..container::Appearance::default()
}, },
Self::Success => container::Appearance { Self::Success => container::Appearance {
background: color::GREEN.into(), background: Some(color::GREEN.into()),
border_radius: 25.0, border: iced::Border {
color: color::TRANSPARENT,
width: 0.0,
radius: 25.0.into(),
},
text_color: color::LIGHT_BLACK.into(), text_color: color::LIGHT_BLACK.into(),
..container::Appearance::default() ..container::Appearance::default()
}, },
Self::Simple => container::Appearance { Self::Simple => container::Appearance {
background: iced::Color::TRANSPARENT.into(), background: Some(iced::Color::TRANSPARENT.into()),
border_radius: 25.0, border: iced::Border {
border_width: 1.0, color: color::GREY_3,
border_color: color::GREY_3, width: 1.0,
radius: 25.0.into(),
},
text_color: color::GREY_3.into(), text_color: color::GREY_3.into(),
..container::Appearance::default()
}, },
Self::Warning => container::Appearance { Self::Warning => container::Appearance {
background: iced::Color::TRANSPARENT.into(), background: Some(iced::Color::TRANSPARENT.into()),
border_radius: 25.0, border: iced::Border {
border_width: 1.0, color: color::RED,
border_color: color::RED, width: 1.0,
radius: 25.0.into(),
},
text_color: color::RED.into(), text_color: color::RED.into(),
..container::Appearance::default()
}, },
} }
} }
@ -415,24 +485,32 @@ pub struct Scrollable {}
impl scrollable::StyleSheet for Theme { impl scrollable::StyleSheet for Theme {
type Style = Scrollable; type Style = Scrollable;
fn active(&self, _style: &Self::Style) -> scrollable::Scrollbar { fn active(&self, _style: &Self::Style) -> scrollable::Appearance {
scrollable::Scrollbar { scrollable::Appearance {
background: None, gap: None,
border_width: 0.0, container: container::Appearance::default(),
border_color: color::GREY_7, scrollbar: scrollable::Scrollbar {
border_radius: 10.0, background: None,
scroller: scrollable::Scroller { border: iced::Border {
color: color::GREY_7, color: color::GREY_3,
border_radius: 10.0, width: 0.0,
border_width: 0.0, radius: 10.0.into(),
border_color: iced::Color::TRANSPARENT, },
scroller: scrollable::Scroller {
color: color::GREY_7,
border: iced::Border {
color: color::TRANSPARENT,
width: 0.0,
radius: 10.0.into(),
},
},
}, },
} }
} }
fn hovered(&self, style: &Self::Style, _is_hovered: bool) -> scrollable::Scrollbar { fn hovered(&self, style: &Self::Style, _is_hovered: bool) -> scrollable::Appearance {
let active = self.active(style); let active = self.active(style);
scrollable::Scrollbar { ..active } scrollable::Appearance { ..active }
} }
} }
@ -452,27 +530,33 @@ impl pick_list::StyleSheet for Theme {
placeholder_color: color::GREY_6, placeholder_color: color::GREY_6,
handle_color: color::GREY_6, handle_color: color::GREY_6,
background: color::GREEN.into(), background: color::GREEN.into(),
border_width: 1.0, border: iced::Border {
border_color: color::GREY_7, color: color::GREY_7,
border_radius: 25.0, width: 1.0,
radius: 25.0.into(),
},
text_color: iced::Color::BLACK, text_color: iced::Color::BLACK,
}, },
PickList::Invalid => pick_list::Appearance { PickList::Invalid => pick_list::Appearance {
placeholder_color: color::GREY_6, placeholder_color: color::GREY_6,
handle_color: color::GREY_6, handle_color: color::GREY_6,
background: color::GREY_6.into(), background: color::GREY_6.into(),
border_width: 1.0, border: iced::Border {
border_color: color::RED, color: color::RED,
border_radius: 25.0, width: 1.0,
radius: 25.0.into(),
},
text_color: color::RED, text_color: color::RED,
}, },
PickList::Secondary => pick_list::Appearance { PickList::Secondary => pick_list::Appearance {
placeholder_color: color::GREY_3, placeholder_color: color::GREY_6,
handle_color: color::GREY_3, handle_color: color::GREY_6,
background: color::TRANSPARENT.into(), background: color::GREY_6.into(),
border_width: 1.0, border: iced::Border {
border_color: color::GREY_3, color: color::GREY_7,
border_radius: 25.0, width: 1.0,
radius: 25.0.into(),
},
text_color: color::GREY_2, text_color: color::GREY_2,
}, },
} }
@ -493,20 +577,24 @@ impl checkbox::StyleSheet for Theme {
if is_selected { if is_selected {
checkbox::Appearance { checkbox::Appearance {
background: color::GREEN.into(), background: color::GREEN.into(),
border_width: 0.0,
border_color: iced::Color::TRANSPARENT,
icon_color: color::GREY_4, icon_color: color::GREY_4,
text_color: None, text_color: None,
border_radius: 4.0, border: iced::Border {
color: color::TRANSPARENT,
width: 1.0,
radius: 4.0.into(),
},
} }
} else { } else {
checkbox::Appearance { checkbox::Appearance {
background: color::GREY_4.into(), background: color::GREY_4.into(),
border_width: 0.0,
border_color: iced::Color::TRANSPARENT,
icon_color: color::GREEN, icon_color: color::GREEN,
text_color: None, text_color: None,
border_radius: 4.0, border: iced::Border {
color: color::TRANSPARENT,
width: 0.0,
radius: 4.0.into(),
},
} }
} }
} }
@ -538,64 +626,85 @@ impl button::StyleSheet for Theme {
Theme::Dark => match style { Theme::Dark => match style {
Button::Primary => button::Appearance { Button::Primary => button::Appearance {
shadow_offset: iced::Vector::default(), shadow_offset: iced::Vector::default(),
background: iced::Color::TRANSPARENT.into(), background: Some(iced::Color::TRANSPARENT.into()),
border_radius: 25.0,
border_width: 1.0,
border_color: color::GREY_7,
text_color: color::GREY_2, text_color: color::GREY_2,
border: iced::Border {
color: color::GREY_7,
width: 1.0,
radius: 25.0.into(),
},
..button::Appearance::default()
}, },
Button::Secondary | Button::SecondaryDestructive | Button::Border => { Button::Secondary | Button::SecondaryDestructive | Button::Border => {
button::Appearance { button::Appearance {
shadow_offset: iced::Vector::default(), shadow_offset: iced::Vector::default(),
background: iced::Color::TRANSPARENT.into(), background: Some(iced::Color::TRANSPARENT.into()),
border_radius: 25.0,
border_width: 1.0,
border_color: color::GREY_7,
text_color: color::GREY_2, text_color: color::GREY_2,
border: iced::Border {
color: color::GREY_7,
width: 1.0,
radius: 25.0.into(),
},
..button::Appearance::default()
} }
} }
Button::Destructive => button::Appearance { Button::Destructive => button::Appearance {
shadow_offset: iced::Vector::default(), shadow_offset: iced::Vector::default(),
background: iced::Color::TRANSPARENT.into(), background: Some(iced::Color::TRANSPARENT.into()),
border_radius: 25.0,
border_width: 1.0,
border_color: color::RED,
text_color: color::RED, text_color: color::RED,
border: iced::Border {
color: color::RED,
width: 1.0,
radius: 25.0.into(),
},
..button::Appearance::default()
}, },
Button::Transparent => button::Appearance { Button::Transparent => button::Appearance {
shadow_offset: iced::Vector::default(), shadow_offset: iced::Vector::default(),
background: iced::Color::TRANSPARENT.into(), background: Some(iced::Color::TRANSPARENT.into()),
border_radius: 25.0,
border_width: 0.0,
border_color: iced::Color::TRANSPARENT,
text_color: color::GREY_2, text_color: color::GREY_2,
border: iced::Border {
color: color::TRANSPARENT,
width: 0.0,
radius: 25.0.into(),
},
..button::Appearance::default()
}, },
Button::TransparentBorder => button::Appearance { Button::TransparentBorder => button::Appearance {
shadow_offset: iced::Vector::default(), shadow_offset: iced::Vector::default(),
background: iced::Color::TRANSPARENT.into(), background: Some(iced::Color::TRANSPARENT.into()),
border_radius: 25.0,
border_width: 0.0,
border_color: iced::Color::TRANSPARENT,
text_color: color::WHITE, text_color: color::WHITE,
border: iced::Border {
color: color::TRANSPARENT,
width: 0.0,
radius: 25.0.into(),
},
..button::Appearance::default()
}, },
Button::Menu(active) => { Button::Menu(active) => {
if *active { if *active {
button::Appearance { button::Appearance {
shadow_offset: iced::Vector::default(), shadow_offset: iced::Vector::default(),
background: color::LIGHT_BLACK.into(), background: Some(color::LIGHT_BLACK.into()),
border_radius: 25.0,
border_width: 0.0,
border_color: iced::Color::TRANSPARENT,
text_color: color::WHITE, text_color: color::WHITE,
border: iced::Border {
color: color::TRANSPARENT,
width: 0.0,
radius: 25.0.into(),
},
..button::Appearance::default()
} }
} else { } else {
button::Appearance { button::Appearance {
shadow_offset: iced::Vector::default(), shadow_offset: iced::Vector::default(),
background: iced::Color::TRANSPARENT.into(), background: Some(iced::Color::TRANSPARENT.into()),
border_radius: 25.0,
border_width: 0.0,
border_color: iced::Color::TRANSPARENT,
text_color: color::WHITE, text_color: color::WHITE,
border: iced::Border {
color: color::TRANSPARENT,
width: 0.0,
radius: 25.0.into(),
},
..button::Appearance::default()
} }
} }
} }
@ -609,51 +718,69 @@ impl button::StyleSheet for Theme {
Theme::Dark => match style { Theme::Dark => match style {
Button::Primary => button::Appearance { Button::Primary => button::Appearance {
shadow_offset: iced::Vector::default(), shadow_offset: iced::Vector::default(),
background: color::GREEN.into(), background: Some(color::GREEN.into()),
border_radius: 25.0,
border_width: 0.0,
border_color: iced::Color::TRANSPARENT,
text_color: color::LIGHT_BLACK, text_color: color::LIGHT_BLACK,
border: iced::Border {
color: color::TRANSPARENT,
width: 0.0,
radius: 25.0.into(),
},
..button::Appearance::default()
}, },
Button::Secondary => button::Appearance { Button::Secondary => button::Appearance {
shadow_offset: iced::Vector::default(), shadow_offset: iced::Vector::default(),
background: color::GREEN.into(), background: Some(color::GREEN.into()),
border_radius: 25.0,
border_width: 0.0,
border_color: iced::Color::TRANSPARENT,
text_color: color::LIGHT_BLACK, text_color: color::LIGHT_BLACK,
border: iced::Border {
color: color::TRANSPARENT,
width: 0.0,
radius: 25.0.into(),
},
..button::Appearance::default()
}, },
Button::Destructive | Button::SecondaryDestructive => button::Appearance { Button::Destructive | Button::SecondaryDestructive => button::Appearance {
shadow_offset: iced::Vector::default(), shadow_offset: iced::Vector::default(),
background: color::RED.into(), background: Some(color::RED.into()),
border_radius: 25.0,
border_width: 0.0,
border_color: iced::Color::TRANSPARENT,
text_color: color::LIGHT_BLACK, text_color: color::LIGHT_BLACK,
border: iced::Border {
color: color::TRANSPARENT,
width: 0.0,
radius: 25.0.into(),
},
..button::Appearance::default()
}, },
Button::Transparent => button::Appearance { Button::Transparent => button::Appearance {
shadow_offset: iced::Vector::default(), shadow_offset: iced::Vector::default(),
background: iced::Color::TRANSPARENT.into(), background: Some(iced::Color::TRANSPARENT.into()),
border_radius: 25.0,
border_width: 0.0,
border_color: iced::Color::TRANSPARENT,
text_color: color::GREY_2, text_color: color::GREY_2,
border: iced::Border {
color: color::TRANSPARENT,
width: 0.0,
radius: 25.0.into(),
},
..button::Appearance::default()
}, },
Button::TransparentBorder | Button::Border => button::Appearance { Button::TransparentBorder | Button::Border => button::Appearance {
shadow_offset: iced::Vector::default(), shadow_offset: iced::Vector::default(),
background: iced::Color::TRANSPARENT.into(), background: Some(iced::Color::TRANSPARENT.into()),
border_radius: 25.0,
border_width: 1.0,
border_color: color::GREEN,
text_color: color::WHITE, text_color: color::WHITE,
border: iced::Border {
color: color::GREEN,
width: 1.0,
radius: 25.0.into(),
},
..button::Appearance::default()
}, },
Button::Menu(_) => button::Appearance { Button::Menu(_) => button::Appearance {
shadow_offset: iced::Vector::default(), shadow_offset: iced::Vector::default(),
background: color::LIGHT_BLACK.into(), background: Some(color::LIGHT_BLACK.into()),
border_radius: 25.0,
border_width: 0.0,
border_color: iced::Color::TRANSPARENT,
text_color: color::WHITE, text_color: color::WHITE,
border: iced::Border {
color: color::TRANSPARENT,
width: 0.0,
radius: 25.0.into(),
},
..button::Appearance::default()
}, },
}, },
} }
@ -674,16 +801,20 @@ impl text_input::StyleSheet for Theme {
Form::Simple => text_input::Appearance { Form::Simple => text_input::Appearance {
icon_color: color::GREY_7, icon_color: color::GREY_7,
background: iced::Background::Color(iced::Color::TRANSPARENT), background: iced::Background::Color(iced::Color::TRANSPARENT),
border_radius: 25.0, border: iced::Border {
border_width: 1.0, color: color::GREY_7,
border_color: color::GREY_7, width: 1.0,
radius: 25.0.into(),
},
}, },
Form::Invalid => text_input::Appearance { Form::Invalid => text_input::Appearance {
icon_color: color::GREY_7, icon_color: color::GREY_7,
background: iced::Background::Color(iced::Color::TRANSPARENT), background: iced::Background::Color(iced::Color::TRANSPARENT),
border_radius: 25.0, border: iced::Border {
border_width: 1.0, color: color::RED,
border_color: color::RED, width: 1.0,
radius: 25.0.into(),
},
}, },
} }
} }
@ -729,7 +860,7 @@ impl progress_bar::StyleSheet for Theme {
progress_bar::Appearance { progress_bar::Appearance {
background: color::GREY_6.into(), background: color::GREY_6.into(),
bar: color::GREEN.into(), bar: color::GREEN.into(),
border_radius: 10.0, border_radius: 10.0.into(),
} }
} }
} }
@ -746,7 +877,7 @@ impl slider::StyleSheet for Theme {
let handle = slider::Handle { let handle = slider::Handle {
shape: slider::HandleShape::Rectangle { shape: slider::HandleShape::Rectangle {
width: 8, width: 8,
border_radius: 4.0, border_radius: 4.0.into(),
}, },
color: color::BLACK, color: color::BLACK,
border_color: color::GREEN, border_color: color::GREEN,
@ -755,6 +886,7 @@ impl slider::StyleSheet for Theme {
slider::Appearance { slider::Appearance {
rail: slider::Rail { rail: slider::Rail {
colors: (color::GREEN, iced::Color::TRANSPARENT), colors: (color::GREEN, iced::Color::TRANSPARENT),
border_radius: 4.0.into(),
width: 2.0, width: 2.0,
}, },
handle, handle,
@ -764,7 +896,7 @@ impl slider::StyleSheet for Theme {
let handle = slider::Handle { let handle = slider::Handle {
shape: slider::HandleShape::Rectangle { shape: slider::HandleShape::Rectangle {
width: 8, width: 8,
border_radius: 4.0, border_radius: 4.0.into(),
}, },
color: color::GREEN, color: color::GREEN,
border_color: color::GREEN, border_color: color::GREEN,
@ -773,6 +905,7 @@ impl slider::StyleSheet for Theme {
slider::Appearance { slider::Appearance {
rail: slider::Rail { rail: slider::Rail {
colors: (color::GREEN, iced::Color::TRANSPARENT), colors: (color::GREEN, iced::Color::TRANSPARENT),
border_radius: 4.0.into(),
width: 2.0, width: 2.0,
}, },
handle, handle,
@ -782,7 +915,7 @@ impl slider::StyleSheet for Theme {
let handle = slider::Handle { let handle = slider::Handle {
shape: slider::HandleShape::Rectangle { shape: slider::HandleShape::Rectangle {
width: 8, width: 8,
border_radius: 4.0, border_radius: 4.0.into(),
}, },
color: color::GREEN, color: color::GREEN,
border_color: color::GREEN, border_color: color::GREEN,
@ -791,6 +924,7 @@ impl slider::StyleSheet for Theme {
slider::Appearance { slider::Appearance {
rail: slider::Rail { rail: slider::Rail {
colors: (color::GREEN, iced::Color::TRANSPARENT), colors: (color::GREEN, iced::Color::TRANSPARENT),
border_radius: 4.0.into(),
width: 2.0, width: 2.0,
}, },
handle, handle,
@ -810,3 +944,13 @@ impl svg::StyleSheet for Theme {
svg::Appearance::default() svg::Appearance::default()
} }
} }
impl qr_code::StyleSheet for Theme {
type Style = ();
fn appearance(&self, _style: &Self::Style) -> qr_code::Appearance {
qr_code::Appearance {
cell: color::BLACK,
background: color::WHITE,
}
}
}

View File

@ -1,25 +0,0 @@
/// from hecjr idea on Discord
use crate::widget::*;
pub trait Collection<'a, Message>: Sized {
fn push(self, element: impl Into<Element<'a, Message>>) -> Self;
fn push_maybe(self, element: Option<impl Into<Element<'a, Message>>>) -> Self {
match element {
Some(element) => self.push(element),
None => self,
}
}
}
impl<'a, Message> Collection<'a, Message> for Column<'a, Message> {
fn push(self, element: impl Into<Element<'a, Message>>) -> Self {
Self::push(self, element)
}
}
impl<'a, Message> Collection<'a, Message> for Row<'a, Message> {
fn push(self, element: impl Into<Element<'a, Message>>) -> Self {
Self::push(self, element)
}
}