gui: iced-0.12

This commit is contained in:
edouardparis 2024-03-29 15:27:05 +01:00
parent 64a626d7e8
commit fdcc302367
32 changed files with 1410 additions and 970 deletions

1521
gui/Cargo.lock generated

File diff suppressed because it is too large Load Diff

View File

@ -21,8 +21,8 @@ liana_ui = { path = "ui" }
backtrace = "0.3"
hex = "0.4.3"
iced = { version = "0.10", default-features = false, features = ["tokio", "svg", "qr_code", "image", "lazy", "wgpu"] }
iced_runtime = "0.1.1"
iced = { version = "0.12.1", default-features = false, features = ["tokio", "svg", "qr_code", "image", "lazy", "webgl"] }
iced_runtime = "0.12.1"
tokio = {version = "1.21.0", features = ["signal"]}
serde = { version = "1.0", features = ["derive"] }
@ -47,6 +47,12 @@ bitcoin_hashes = "0.12"
reqwest = { version = "0.11", default-features=false, features = ["rustls-tls"] }
rust-ini = "0.19.0"
[patch.crates-io]
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]
zip = { version = "0.6", default-features=false, features = ["bzip2", "deflate"] }

View File

@ -302,13 +302,13 @@ impl VerifyAddressModal {
}
pub struct ShowQrCodeModal {
qr_code: qr_code::State,
qr_code: qr_code::Data,
address: String,
}
impl ShowQrCodeModal {
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()
.map(|qr_code| Self {
qr_code,

View File

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

View File

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

View File

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

View File

@ -23,7 +23,6 @@ use liana_ui::{
text::{self, *},
},
icon, theme,
util::Collection,
widget::*,
};
@ -426,7 +425,7 @@ pub fn signatures<'a>(
Container::new(text(alias))
.padding(10)
.style(theme::Container::Pill(theme::Pill::Simple)),
value.to_string(),
liana_ui::widget::Text::new(value.to_string()),
tooltip::Position::Bottom,
)
.style(theme::Container::Card(theme::Card::Simple)),
@ -522,7 +521,7 @@ fn container_from_fg(
Container::new(text(alias))
.padding(10)
.style(theme::Container::Pill(theme::Pill::Simple)),
fg.to_string(),
liana_ui::widget::Text::new(fg.to_string()),
tooltip::Position::Bottom,
)
.style(theme::Container::Card(theme::Card::Simple)),

View File

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

View File

@ -21,7 +21,6 @@ use liana_ui::{
text::{self, *},
},
icon, theme,
util::Collection,
widget::*,
};
@ -216,13 +215,13 @@ pub fn verify_address_modal<'a>(
.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()
.push(
Row::new()
.push(Space::with_width(Length::Fill))
.push(
Container::new(QRCode::new(qr).cell_size(8))
Container::new(QRCode::<liana_ui::theme::Theme>::new(qr).cell_size(8))
.padding(10)
.style(theme::Container::QrCode),
)

View File

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

View File

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

View File

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

View File

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

View File

@ -18,7 +18,6 @@ use liana_ui::{
tooltip,
},
icon, image, theme,
util::Collection,
widget::*,
};
@ -720,11 +719,10 @@ pub fn participate_xpub<'a>(
.push(signer)
.width(Length::Fill),
)
.push(checkbox(
"I have shared my extended public key",
shared,
Message::UserActionDone,
))
.push(
checkbox("I have shared my extended public key", shared)
.on_toggle(Message::UserActionDone),
)
.push(if shared && network_valid {
button::primary(None, "Next")
.width(Length::Fixed(200.0))
@ -807,8 +805,7 @@ pub fn register_descriptor<'a>(
.push_maybe(created_desc.then_some(checkbox(
"I have registered the descriptor on my device(s)",
done,
Message::UserActionDone,
)))
).on_toggle(Message::UserActionDone)))
.push(if !created_desc || (done && !processing) {
button::primary(None, "Next")
.on_press(Message::Next)
@ -872,11 +869,9 @@ pub fn backup_descriptor<'a>(
.spacing(10)
.max_width(1000),
))
.push(checkbox(
"I have backed up my descriptor",
done,
Message::UserActionDone,
))
.push(
checkbox("I have backed up my descriptor", done).on_toggle(Message::UserActionDone),
)
.push(if done {
button::primary(None, "Next")
.on_press(Message::Next)
@ -1678,7 +1673,7 @@ pub fn edit_sequence_modal<'a>(sequence: &form::Value<String>) -> Element<'a, Me
message::SequenceModal::SequenceEdited(v.to_string()),
))
})
.step(144), // 144 blocks per day
.step(144_u16), // 144 blocks per day
)
.width(Length::Fixed(500.0)),
);
@ -1821,11 +1816,7 @@ pub fn backup_mnemonic<'a>(
)
}),
)
.push(checkbox(
"I have backed up my mnemonic",
done,
Message::UserActionDone,
))
.push(checkbox("I have backed up my mnemonic", done).on_toggle(Message::UserActionDone))
.push(if done {
button::primary(None, "Next")
.on_press(Message::Next)
@ -2041,7 +2032,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 Event = Event;

View File

@ -11,7 +11,6 @@ use liana_ui::{
color,
component::{badge, button, card, modal::Modal, notification, text::*},
icon, image, theme,
util::*,
widget::*,
};

View File

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

View File

@ -5,9 +5,9 @@ use std::{error::Error, io::Write, path::PathBuf, process, str::FromStr};
use iced::{
event::{self, Event},
executor,
keyboard::{self, KeyCode},
subscription,
keyboard::{self},
widget::{focus_next, focus_previous},
window::settings::PlatformSpecific,
Application, Command, Settings, Subscription,
};
use tracing::{error, info};
@ -180,14 +180,14 @@ impl Application for GUI {
fn update(&mut self, message: Self::Message) -> Command<Self::Message> {
match (&mut self.state, message) {
(_, Message::CtrlC)
| (_, Message::Event(iced::Event::Window(iced::window::Event::CloseRequested))) => {
| (_, Message::Event(iced::Event::Window(_, iced::window::Event::CloseRequested))) => {
match &mut self.state {
State::Loader(s) => s.stop(),
State::Launcher(s) => s.stop(),
State::Installer(s) => s.stop(),
State::App(s) => s.stop(),
};
iced::window::close()
iced::window::close(iced::window::Id::MAIN)
}
(_, Message::KeyPressed(Key::Tab(shift))) => {
log::debug!("Tab pressed!");
@ -286,22 +286,29 @@ impl Application for GUI {
State::App(v) => v.subscription().map(|msg| Message::Run(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 {
key_code: KeyCode::Tab,
key: iced::keyboard::Key::Named(iced::keyboard::key::Named::Tab),
modifiers,
..
}),
event::Status::Ignored,
) => Some(Message::KeyPressed(Key::Tab(modifiers.shift()))),
(
iced::Event::Window(iced::window::Event::CloseRequested),
iced::Event::Window(_, iced::window::Event::CloseRequested),
event::Status::Ignored,
) => Some(Message::Event(event)),
_ => None,
}),
])
.with_filter(|event| {
matches!(
event,
iced::Event::Window(_, iced::window::Event::CloseRequested)
| iced::Event::Keyboard(_)
)
})
}
fn view(&self) -> Element<Self::Message> {
@ -436,13 +443,13 @@ fn main() -> Result<(), Box<dyn Error>> {
settings.window.icon = Some(image::liana_app_icon());
settings.default_text_size = text::P1_SIZE.into();
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 = iced::window::PlatformSpecific {
settings.window.platform_specific = PlatformSpecific {
application_id: "Liana".to_string(),
};
}

View File

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

View File

@ -1,6 +1,6 @@
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> {
amount_with_size(a, P1_SIZE)

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)
.push(icon::warning_icon().style(color::RED))
.push(text(message).style(color::RED)),
error,
Text::new(error),
iced::widget::tooltip::Position::Bottom,
)
.style(theme::Container::Card(theme::Card::Error)),

View File

@ -1,5 +1,8 @@
use crate::widget::*;
use iced::widget::{column, component, Component};
use iced::{
advanced,
widget::{column, component, Button, Component},
Element,
};
use std::marker::PhantomData;
pub struct Collapse<'a, M, H, F, C> {
@ -9,13 +12,15 @@ pub struct Collapse<'a, M, H, F, C> {
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
Renderer: advanced::Renderer,
Theme: iced::widget::button::StyleSheet,
Message: 'a,
T: Into<Message> + Clone + 'a,
H: Fn() -> Button<'a, Event<T>> + 'a,
F: Fn() -> Button<'a, Event<T>> + 'a,
C: Fn() -> Element<'a, T> + 'a,
H: Fn() -> Button<'a, Event<T>, Theme, Renderer> + 'a,
F: Fn() -> Button<'a, Event<T>, Theme, Renderer> + 'a,
C: Fn() -> Element<'a, T, Theme, Renderer> + 'a,
{
pub fn new(before: H, after: F, content: C) -> Self {
Collapse {
@ -33,13 +38,15 @@ pub enum Event<T> {
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>
where
T: Into<Message> + Clone + 'a,
H: Fn() -> Button<'a, Event<T>>,
F: Fn() -> Button<'a, Event<T>>,
C: Fn() -> Element<'a, T>,
H: Fn() -> Button<'a, Event<T>, Theme, Renderer>,
F: Fn() -> Button<'a, Event<T>, Theme, Renderer>,
C: Fn() -> Element<'a, T, Theme, Renderer>,
Renderer: 'a + advanced::Renderer,
Theme: 'a + iced::widget::button::StyleSheet,
{
type State = bool;
type Event = Event<T>;
@ -54,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 {
column![
(self.after)().on_press(Event::Collapse(false)),
@ -67,14 +74,16 @@ where
}
}
impl<'a, Message, T, H: 'a, F: 'a, C: 'a> From<Collapse<'a, Message, H, F, C>>
for Element<'a, Message>
impl<'a, Message, T, H: 'a, F: 'a, C: 'a, Theme, Renderer> From<Collapse<'a, Message, H, F, C>>
for Element<'a, Message, Theme, Renderer>
where
Message: 'a,
Renderer: 'a + advanced::Renderer,
Theme: 'a + iced::widget::button::StyleSheet,
T: Into<Message> + Clone + 'a,
H: Fn() -> Button<'a, Event<T>>,
F: Fn() -> Button<'a, Event<T>>,
C: Fn() -> Element<'a, T>,
H: Fn() -> Button<'a, Event<T>, Theme, Renderer>,
F: Fn() -> Button<'a, Event<T>, Theme, Renderer>,
C: Fn() -> Element<'a, T, Theme, Renderer>,
{
fn from(c: Collapse<'a, Message, H, F, C>) -> Self {
component(c)

View File

@ -2,7 +2,6 @@ use crate::{
color,
component::{amount, badge, text},
theme,
util::Collection,
widget::*,
};
use bitcoin::Amount;
@ -12,7 +11,7 @@ use iced::{
};
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,
msg: 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>(
label: Option<iced::widget::Text<'a, iced::Renderer<theme::Theme>>>,
label: Option<Text<'a>>,
date: chrono::NaiveDateTime,
amount: &Amount,
msg: T,
@ -72,7 +71,7 @@ pub fn confirmed_outgoing_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,
msg: 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>(
label: Option<iced::widget::Text<'a, iced::Renderer<theme::Theme>>>,
label: Option<Text<'a>>,
date: chrono::NaiveDateTime,
amount: &Amount,
msg: T,

View File

@ -1,7 +1,7 @@
use bitcoin::Denomination;
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)]
pub struct Value<T> {
@ -19,7 +19,7 @@ impl std::default::Default for Value<String> {
}
pub struct Form<'a, Message> {
input: text_input::TextInput<'a, Message, iced::Renderer<theme::Theme>>,
input: TextInput<'a, Message>,
warning: Option<&'a str>,
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::{
widget::{column, container, row, tooltip},
Alignment, Length,

View File

@ -7,20 +7,20 @@ 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};
use iced::{Color, Element, Event, Length, Point, Rectangle, Size, Vector};
/// A widget that centers a modal element over some base element
pub struct Modal<'a, Message, Renderer> {
base: Element<'a, Message, Renderer>,
modal: Element<'a, Message, Renderer>,
pub struct Modal<'a, Message, Theme, Renderer> {
base: Element<'a, Message, Theme, Renderer>,
modal: Element<'a, Message, Theme, Renderer>,
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`]
pub fn new(
base: impl Into<Element<'a, Message, Renderer>>,
modal: impl Into<Element<'a, Message, Renderer>>,
base: impl Into<Element<'a, Message, Theme, Renderer>>,
modal: impl Into<Element<'a, Message, Theme, Renderer>>,
) -> Self {
Self {
base: base.into(),
@ -36,7 +36,8 @@ 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
Renderer: advanced::Renderer,
Message: Clone,
@ -49,16 +50,19 @@ where
tree.diff_children(&[&self.base, &self.modal]);
}
fn width(&self) -> Length {
self.base.as_widget().width()
fn size(&self) -> Size<Length> {
self.base.as_widget().size()
}
fn height(&self) -> Length {
self.base.as_widget().height()
}
fn layout(&self, renderer: &Renderer, limits: &layout::Limits) -> layout::Node {
self.base.as_widget().layout(renderer, limits)
fn layout(
&self,
tree: &mut widget::Tree,
renderer: &Renderer,
limits: &layout::Limits,
) -> layout::Node {
self.base
.as_widget()
.layout(&mut tree.children[0], renderer, limits)
}
fn on_event(
@ -88,7 +92,7 @@ where
&self,
state: &widget::Tree,
renderer: &mut Renderer,
theme: &<Renderer as advanced::Renderer>::Theme,
theme: &Theme,
style: &renderer::Style,
layout: Layout<'_>,
cursor: mouse::Cursor,
@ -110,16 +114,15 @@ where
state: &'b mut Tree,
layout: Layout<'_>,
_renderer: &Renderer,
) -> Option<overlay::Element<'b, Message, Renderer>> {
Some(overlay::Element::new(
layout.position(),
Box::new(Overlay {
content: &mut self.modal,
tree: &mut state.children[1],
size: layout.bounds().size(),
on_blur: self.on_blur.clone(),
}),
))
translation: Vector,
) -> Option<overlay::Element<'b, Message, Theme, Renderer>> {
Some(overlay::Element::new(Box::new(Overlay {
position: layout.position() + translation,
content: &mut self.modal,
tree: &mut state.children[1],
size: layout.bounds().size(),
on_blur: self.on_blur.clone(),
})))
}
fn mouse_interaction(
@ -152,31 +155,32 @@ where
}
}
struct Overlay<'a, 'b, Message, Renderer> {
content: &'b mut Element<'a, Message, Renderer>,
struct Overlay<'a, 'b, Message, Theme, Renderer> {
position: Point,
content: &'b mut Element<'a, Message, Theme, Renderer>,
tree: &'b mut Tree,
size: Size,
on_blur: Option<Message>,
}
impl<'a, 'b, Message, Renderer> overlay::Overlay<Message, Renderer>
for Overlay<'a, 'b, Message, Renderer>
impl<'a, 'b, Message, Theme, Renderer> overlay::Overlay<Message, Theme, Renderer>
for Overlay<'a, 'b, Message, Theme, Renderer>
where
Renderer: advanced::Renderer,
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)
.width(Length::Fill)
.height(Length::Fill);
let mut child = self.content.as_widget().layout(renderer, &limits);
child.align(Alignment::Center, Alignment::Center, limits.max());
let child = self
.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]);
node.move_to(position);
node
layout::Node::with_children(self.size, vec![child]).move_to(self.position)
}
fn on_event(
@ -214,7 +218,7 @@ where
fn draw(
&self,
renderer: &mut Renderer,
theme: &Renderer::Theme,
theme: &Theme,
style: &renderer::Style,
layout: Layout<'_>,
cursor: mouse::Cursor,
@ -222,9 +226,7 @@ where
renderer.fill_quad(
renderer::Quad {
bounds: layout.bounds(),
border_radius: Default::default(),
border_width: 0.0,
border_color: Color::TRANSPARENT,
..renderer::Quad::default()
},
Color {
a: 0.80,
@ -277,19 +279,24 @@ where
&'c mut self,
layout: Layout<'_>,
renderer: &Renderer,
) -> Option<overlay::Element<'c, Message, Renderer>> {
self.content
.as_widget_mut()
.overlay(self.tree, layout.children().next().unwrap(), 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
Renderer: 'a + advanced::Renderer,
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)
}
}

View File

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

View File

@ -18,105 +18,85 @@ 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)
.font(font::BOLD)
.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)
.font(font::BOLD)
.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)
.font(font::BOLD)
.size(H3_SIZE)
}
pub fn h4_bold<'a>(
content: impl Into<Cow<'a, str>>,
) -> iced::widget::Text<'a, iced::Renderer<Theme>> {
pub fn h4_bold<'a>(content: impl Into<Cow<'a, str>>) -> iced::widget::Text<'a, Theme> {
iced::widget::Text::new(content)
.font(font::BOLD)
.size(H4_SIZE)
}
pub fn h4_regular<'a>(
content: impl Into<Cow<'a, str>>,
) -> iced::widget::Text<'a, iced::Renderer<Theme>> {
pub fn h4_regular<'a>(content: impl Into<Cow<'a, str>>) -> iced::widget::Text<'a, Theme> {
iced::widget::Text::new(content)
.font(font::REGULAR)
.size(H4_SIZE)
}
pub fn h5_medium<'a>(
content: impl Into<Cow<'a, str>>,
) -> iced::widget::Text<'a, iced::Renderer<Theme>> {
pub fn h5_medium<'a>(content: impl Into<Cow<'a, str>>) -> iced::widget::Text<'a, Theme> {
iced::widget::Text::new(content)
.font(font::MEDIUM)
.size(H5_SIZE)
}
pub fn h5_regular<'a>(
content: impl Into<Cow<'a, str>>,
) -> iced::widget::Text<'a, iced::Renderer<Theme>> {
pub fn h5_regular<'a>(content: impl Into<Cow<'a, str>>) -> iced::widget::Text<'a, Theme> {
iced::widget::Text::new(content)
.font(font::REGULAR)
.size(H5_SIZE)
}
pub fn p1_bold<'a>(
content: impl Into<Cow<'a, str>>,
) -> iced::widget::Text<'a, iced::Renderer<Theme>> {
pub fn p1_bold<'a>(content: impl Into<Cow<'a, str>>) -> iced::widget::Text<'a, Theme> {
iced::widget::Text::new(content)
.font(font::BOLD)
.size(P1_SIZE)
}
pub fn p1_medium<'a>(
content: impl Into<Cow<'a, str>>,
) -> iced::widget::Text<'a, iced::Renderer<Theme>> {
pub fn p1_medium<'a>(content: impl Into<Cow<'a, str>>) -> iced::widget::Text<'a, Theme> {
iced::widget::Text::new(content)
.font(font::MEDIUM)
.size(P1_SIZE)
}
pub fn p1_regular<'a>(
content: impl Into<Cow<'a, str>>,
) -> iced::widget::Text<'a, iced::Renderer<Theme>> {
pub fn p1_regular<'a>(content: impl Into<Cow<'a, str>>) -> iced::widget::Text<'a, Theme> {
iced::widget::Text::new(content)
.font(font::REGULAR)
.size(P1_SIZE)
}
pub fn p2_medium<'a>(
content: impl Into<Cow<'a, str>>,
) -> iced::widget::Text<'a, iced::Renderer<Theme>> {
pub fn p2_medium<'a>(content: impl Into<Cow<'a, str>>) -> iced::widget::Text<'a, Theme> {
iced::widget::Text::new(content)
.font(font::MEDIUM)
.size(P2_SIZE)
}
pub fn p2_regular<'a>(
content: impl Into<Cow<'a, str>>,
) -> iced::widget::Text<'a, iced::Renderer<Theme>> {
pub fn p2_regular<'a>(content: impl Into<Cow<'a, str>>) -> iced::widget::Text<'a, Theme> {
iced::widget::Text::new(content)
.font(font::REGULAR)
.size(P2_SIZE)
}
pub fn caption<'a>(
content: impl Into<Cow<'a, str>>,
) -> iced::widget::Text<'a, iced::Renderer<Theme>> {
pub fn caption<'a>(content: impl Into<Cow<'a, str>>) -> iced::widget::Text<'a, Theme> {
iced::widget::Text::new(content)
.font(font::REGULAR)
.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)
}
@ -125,7 +105,7 @@ pub trait Text {
fn small(self) -> Self;
}
impl Text for iced::widget::Text<'_, iced::Renderer<Theme>> {
impl Text for iced::widget::Text<'_, Theme> {
fn bold(self) -> Self {
self.font(font::BOLD)
}

View File

@ -1,7 +1,5 @@
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};
@ -13,18 +11,18 @@ pub trait Toast {
fn body(&self) -> &str;
}
pub struct Manager<'a, Message, Renderer> {
content: Element<'a, Message, Renderer>,
toasts: Vec<Element<'a, Message, Renderer>>,
pub struct Manager<'a, Message, Theme, Renderer> {
content: Element<'a, Message, Theme, 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
Message: 'a + Clone,
{
pub fn new(
content: impl Into<Element<'a, Message, iced::Renderer<Theme>>>,
toasts: Vec<Element<'a, Message, iced::Renderer<Theme>>>,
content: impl Into<Element<'a, Message, Theme, iced::Renderer>>,
toasts: Vec<Element<'a, Message, Theme, iced::Renderer>>,
) -> Self {
Self {
content: content.into(),
@ -33,20 +31,24 @@ 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
Renderer: iced::advanced::Renderer,
{
fn width(&self) -> Length {
self.content.as_widget().width()
fn size(&self) -> Size<Length> {
self.content.as_widget().size()
}
fn height(&self) -> Length {
self.content.as_widget().height()
}
fn layout(&self, renderer: &Renderer, limits: &layout::Limits) -> layout::Node {
self.content.as_widget().layout(renderer, limits)
fn layout(
&self,
tree: &mut Tree,
renderer: &Renderer,
limits: &layout::Limits,
) -> layout::Node {
self.content
.as_widget()
.layout(&mut tree.children[0], renderer, limits)
}
fn tag(&self) -> iced::advanced::widget::tree::Tag {
@ -130,7 +132,7 @@ where
&self,
state: &Tree,
renderer: &mut Renderer,
theme: &<Renderer as iced::advanced::Renderer>::Theme,
theme: &Theme,
style: &renderer::Style,
layout: Layout<'_>,
cursor_position: iced::mouse::Cursor,
@ -169,25 +171,26 @@ where
state: &'b mut Tree,
layout: Layout<'_>,
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 (content_state, toasts_state) = state.children.split_at_mut(1);
let content = self
.content
.as_widget_mut()
.overlay(&mut content_state[0], layout, renderer);
let content = self.content.as_widget_mut().overlay(
&mut content_state[0],
layout,
renderer,
translation,
);
let toasts = (!self.toasts.is_empty()).then(|| {
overlay::Element::new(
layout.bounds().position(),
Box::new(Overlay {
toasts: &mut self.toasts,
state: toasts_state,
instants,
}),
)
overlay::Element::new(Box::new(Overlay {
position: layout.bounds().position() + translation,
toasts: &mut self.toasts,
state: toasts_state,
instants,
}))
});
let overlays = content.into_iter().chain(toasts).collect::<Vec<_>>();
@ -195,18 +198,19 @@ where
}
}
struct Overlay<'a, 'b, Message, Renderer> {
toasts: &'b mut [Element<'a, Message, Renderer>],
struct Overlay<'a, 'b, Message, Theme, Renderer> {
position: Point,
toasts: &'b mut [Element<'a, Message, Theme, Renderer>],
state: &'b mut [Tree],
instants: &'b mut [Option<Instant>],
}
impl<'a, 'b, Message, Renderer> overlay::Overlay<Message, Renderer>
for Overlay<'a, 'b, Message, Renderer>
impl<'a, 'b, Message, Theme, Renderer> overlay::Overlay<Message, Theme, Renderer>
for Overlay<'a, 'b, Message, Theme, Renderer>
where
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)
.width(Length::Fill)
.height(Length::Fill);
@ -215,12 +219,15 @@ where
layout::flex::Axis::Vertical,
renderer,
&limits,
Length::Fill,
Length::Fill,
10.into(),
10.0,
Alignment::End,
self.toasts,
self.state,
)
.translate(Vector::new(position.x, position.y))
.translate(Vector::new(self.position.x, self.position.y))
}
fn on_event(
@ -267,7 +274,7 @@ where
fn draw(
&self,
renderer: &mut Renderer,
theme: &<Renderer as iced::advanced::Renderer>::Theme,
theme: &Theme,
style: &renderer::Style,
layout: Layout<'_>,
cursor_position: iced::mouse::Cursor,
@ -342,12 +349,14 @@ where
}
}
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
Renderer: 'a + iced::advanced::Renderer,
Message: 'a,
Theme: 'a,
{
fn from(manager: Manager<'a, Message, Renderer>) -> Self {
fn from(manager: Manager<'a, Message, Theme, Renderer>) -> Self {
Element::new(manager)
}
}

View File

@ -6,14 +6,14 @@ use iced::{
pub const BOLD: Font = Font {
family: Family::Name("IBM Plex Sans"),
weight: Weight::Bold,
monospaced: false,
style: iced::font::Style::Normal,
stretch: Stretch::Normal,
};
pub const MEDIUM: Font = Font {
family: Family::Name("IBM Plex Sans"),
weight: Weight::Medium,
monospaced: false,
style: iced::font::Style::Normal,
stretch: Stretch::Normal,
};

View File

@ -4,22 +4,24 @@ pub mod font;
pub mod icon;
pub mod image;
pub mod theme;
pub mod util;
pub mod widget {
#![allow(dead_code)]
use crate::theme::Theme;
pub type Renderer = iced::Renderer<Theme>;
pub type Element<'a, Message> = iced::Element<'a, Message, Renderer>;
pub type Container<'a, Message> = iced::widget::Container<'a, Message, Renderer>;
pub type Column<'a, Message> = iced::widget::Column<'a, Message, Renderer>;
pub type Row<'a, Message> = iced::widget::Row<'a, Message, Renderer>;
pub type Button<'a, Message> = iced::widget::Button<'a, Message, Renderer>;
pub type Text<'a> = iced::widget::Text<'a, Renderer>;
pub type Tooltip<'a> = iced::widget::Tooltip<'a, Renderer>;
pub type ProgressBar = iced::widget::ProgressBar<Renderer>;
pub type PickList<'a, Message> = iced::widget::PickList<'a, Message, Renderer>;
pub type Scrollable<'a, Message> = iced::widget::Scrollable<'a, Message, Renderer>;
pub type Svg = iced::widget::Svg<Renderer>;
pub type Renderer = iced::Renderer;
pub type Element<'a, Message> = iced::Element<'a, Message, Theme, Renderer>;
pub type Container<'a, Message> = iced::widget::Container<'a, Message, Theme, Renderer>;
pub type Column<'a, Message> = iced::widget::Column<'a, Message, Theme, Renderer>;
pub type Row<'a, Message> = iced::widget::Row<'a, Message, Theme, Renderer>;
pub type Button<'a, Message> = iced::widget::Button<'a, Message, Theme, Renderer>;
pub type CheckBox<'a, Message> = iced::widget::Checkbox<'a, Message, Theme, Renderer>;
pub type Text<'a> = iced::widget::Text<'a, Theme, Renderer>;
pub type TextInput<'a, Message> = iced::widget::TextInput<'a, Message, Theme, Renderer>;
pub type Tooltip<'a> = iced::widget::Tooltip<'a, Theme, Renderer>;
pub type ProgressBar = iced::widget::ProgressBar<Theme>;
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::{
application,
widget::{
button, checkbox, container, pick_list, progress_bar, radio, scrollable, slider, svg, text,
text_input,
button, checkbox, container, pick_list, progress_bar, qr_code, radio, scrollable, slider,
svg, text, text_input,
},
};
@ -44,9 +44,11 @@ impl iced::overlay::menu::StyleSheet for Theme {
iced::overlay::menu::Appearance {
text_color: color::GREY_2,
background: color::GREY_6.into(),
border_width: 0.0,
border_radius: 25.0.into(),
border_color: color::GREY_2,
border: iced::Border {
color: color::GREY_2,
width: 0.0,
radius: 25.0.into(),
},
selected_text_color: color::LIGHT_BLACK,
selected_background: color::GREEN.into(),
}
@ -116,8 +118,11 @@ impl container::StyleSheet for Theme {
},
Container::Border => container::Appearance {
background: Some(iced::Color::TRANSPARENT.into()),
border_width: 1.0,
border_color: color::LIGHT_BLACK,
border: iced::Border {
color: color::LIGHT_BLACK,
width: 1.0,
radius: 0.0.into(),
},
..container::Appearance::default()
},
Container::Card(c) => c.appearance(self),
@ -130,7 +135,11 @@ impl container::StyleSheet for Theme {
},
Container::QrCode => container::Appearance {
background: Some(color::WHITE.into()),
border_radius: 25.0.into(),
border: iced::Border {
color: color::TRANSPARENT,
width: 0.0,
radius: 25.0.into(),
},
..container::Appearance::default()
},
},
@ -149,8 +158,11 @@ impl container::StyleSheet for Theme {
},
Container::Border => container::Appearance {
background: Some(iced::Color::TRANSPARENT.into()),
border_width: 1.0,
border_color: color::GREY_3,
border: iced::Border {
color: color::GREY_3,
width: 1.0,
radius: 0.0.into(),
},
..container::Appearance::default()
},
Container::Card(c) => c.appearance(self),
@ -163,7 +175,11 @@ impl container::StyleSheet for Theme {
},
Container::QrCode => container::Appearance {
background: Some(color::WHITE.into()),
border_radius: 25.0.into(),
border: iced::Border {
color: color::TRANSPARENT,
width: 0.0,
radius: 25.0.into(),
},
..container::Appearance::default()
},
},
@ -203,32 +219,44 @@ impl Notification {
Self::Pending => container::Appearance {
background: Some(iced::Background::Color(color::GREEN)),
text_color: color::LIGHT_BLACK.into(),
border_width: 1.0,
border_color: color::GREEN,
border_radius: 25.0.into(),
border: iced::Border {
color: color::GREEN,
width: 1.0,
radius: 25.0.into(),
},
..container::Appearance::default()
},
Self::Error => container::Appearance {
background: Some(iced::Background::Color(color::ORANGE)),
text_color: color::LIGHT_BLACK.into(),
border_width: 1.0,
border_color: color::ORANGE,
border_radius: 25.0.into(),
border: iced::Border {
color: color::ORANGE,
width: 1.0,
radius: 25.0.into(),
},
..container::Appearance::default()
},
},
Theme::Dark => match self {
Self::Pending => container::Appearance {
background: Some(iced::Background::Color(color::GREEN)),
text_color: color::LIGHT_BLACK.into(),
border_width: 1.0,
border_color: color::GREEN,
border_radius: 25.0.into(),
border: iced::Border {
color: color::GREEN,
width: 1.0,
radius: 25.0.into(),
},
..container::Appearance::default()
},
Self::Error => container::Appearance {
background: Some(iced::Background::Color(color::ORANGE)),
text_color: color::LIGHT_BLACK.into(),
border_width: 1.0,
border_color: color::ORANGE,
border_radius: 25.0.into(),
border: iced::Border {
color: color::ORANGE,
width: 1.0,
radius: 25.0.into(),
},
..container::Appearance::default()
},
},
}
@ -255,23 +283,31 @@ impl Card {
},
Card::Border => container::Appearance {
background: Some(iced::Color::TRANSPARENT.into()),
border_radius: 10.0.into(),
border_color: color::GREY_2,
border_width: 1.0,
border: iced::Border {
color: color::GREY_2,
width: 1.0,
radius: 10.0.into(),
},
..container::Appearance::default()
},
Card::Invalid => container::Appearance {
background: Some(color::GREY_2.into()),
text_color: color::BLACK.into(),
border_width: 1.0,
border_color: color::RED,
border: iced::Border {
color: color::RED,
width: 1.0,
radius: 0.0.into(),
},
..container::Appearance::default()
},
Card::Error => container::Appearance {
background: Some(color::GREY_2.into()),
text_color: color::RED.into(),
border_width: 1.0,
border_color: color::RED,
border: iced::Border {
color: color::RED,
width: 1.0,
radius: 0.0.into(),
},
..container::Appearance::default()
},
Card::Warning => container::Appearance {
@ -283,28 +319,40 @@ impl Card {
Theme::Dark => match self {
Card::Simple => container::Appearance {
background: Some(color::GREY_6.into()),
border_radius: 25.0.into(),
border: iced::Border {
color: color::TRANSPARENT,
width: 0.0,
radius: 25.0.into(),
},
..container::Appearance::default()
},
Card::Border => container::Appearance {
background: Some(iced::Color::TRANSPARENT.into()),
border_radius: 25.0.into(),
border_color: color::GREY_5,
border_width: 1.0,
border: iced::Border {
color: color::GREY_5,
width: 1.0,
radius: 25.0.into(),
},
..container::Appearance::default()
},
Card::Invalid => container::Appearance {
background: Some(color::LIGHT_BLACK.into()),
text_color: color::RED.into(),
border_width: 1.0,
border_radius: 25.0.into(),
border_color: color::RED,
border: iced::Border {
color: color::RED,
width: 1.0,
radius: 25.0.into(),
},
..container::Appearance::default()
},
Card::Error => container::Appearance {
background: Some(color::LIGHT_BLACK.into()),
text_color: color::RED.into(),
border_width: 1.0,
border_color: color::RED,
border: iced::Border {
color: color::RED,
width: 1.0,
radius: 25.0.into(),
},
..container::Appearance::default()
},
Card::Warning => container::Appearance {
@ -328,12 +376,20 @@ impl Badge {
fn appearance(&self, _theme: &Theme) -> iced::widget::container::Appearance {
match self {
Self::Standard => container::Appearance {
border_radius: 40.0.into(),
border: iced::Border {
color: color::TRANSPARENT,
width: 0.0,
radius: 40.0.into(),
},
background: Some(color::GREY_4.into()),
..container::Appearance::default()
},
Self::Bitcoin => container::Appearance {
border_radius: 40.0.into(),
border: iced::Border {
color: color::TRANSPARENT,
width: 0.0,
radius: 40.0.into(),
},
background: Some(color::ORANGE.into()),
text_color: iced::Color::WHITE.into(),
..container::Appearance::default()
@ -356,29 +412,43 @@ impl Pill {
match self {
Self::Primary => container::Appearance {
background: Some(color::GREEN.into()),
border_radius: 25.0.into(),
border: iced::Border {
color: color::TRANSPARENT,
width: 0.0,
radius: 25.0.into(),
},
text_color: color::LIGHT_BLACK.into(),
..container::Appearance::default()
},
Self::Success => container::Appearance {
background: Some(color::GREEN.into()),
border_radius: 25.0.into(),
border: iced::Border {
color: color::TRANSPARENT,
width: 0.0,
radius: 25.0.into(),
},
text_color: color::LIGHT_BLACK.into(),
..container::Appearance::default()
},
Self::Simple => container::Appearance {
background: Some(iced::Color::TRANSPARENT.into()),
border_radius: 25.0.into(),
border_width: 1.0,
border_color: color::GREY_3,
border: iced::Border {
color: color::GREY_3,
width: 1.0,
radius: 25.0.into(),
},
text_color: color::GREY_3.into(),
..container::Appearance::default()
},
Self::Warning => container::Appearance {
background: Some(iced::Color::TRANSPARENT.into()),
border_radius: 25.0.into(),
border_width: 1.0,
border_color: color::RED,
border: iced::Border {
color: color::RED,
width: 1.0,
radius: 25.0.into(),
},
text_color: color::RED.into(),
..container::Appearance::default()
},
}
}
@ -415,24 +485,32 @@ pub struct Scrollable {}
impl scrollable::StyleSheet for Theme {
type Style = Scrollable;
fn active(&self, _style: &Self::Style) -> scrollable::Scrollbar {
scrollable::Scrollbar {
background: None,
border_width: 0.0,
border_color: color::GREY_7,
border_radius: 10.0.into(),
scroller: scrollable::Scroller {
color: color::GREY_7,
border_radius: 10.0.into(),
border_width: 0.0,
border_color: iced::Color::TRANSPARENT,
fn active(&self, _style: &Self::Style) -> scrollable::Appearance {
scrollable::Appearance {
gap: None,
container: container::Appearance::default(),
scrollbar: scrollable::Scrollbar {
background: None,
border: iced::Border {
color: color::GREY_3,
width: 0.0,
radius: 10.0.into(),
},
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);
scrollable::Scrollbar { ..active }
scrollable::Appearance { ..active }
}
}
@ -452,27 +530,33 @@ impl pick_list::StyleSheet for Theme {
placeholder_color: color::GREY_6,
handle_color: color::GREY_6,
background: color::GREEN.into(),
border_width: 1.0,
border_color: color::GREY_7,
border_radius: 25.0.into(),
border: iced::Border {
color: color::GREY_7,
width: 1.0,
radius: 25.0.into(),
},
text_color: iced::Color::BLACK,
},
PickList::Invalid => pick_list::Appearance {
placeholder_color: color::GREY_6,
handle_color: color::GREY_6,
background: color::GREY_6.into(),
border_width: 1.0,
border_color: color::RED,
border_radius: 25.0.into(),
border: iced::Border {
color: color::RED,
width: 1.0,
radius: 25.0.into(),
},
text_color: color::RED,
},
PickList::Secondary => pick_list::Appearance {
placeholder_color: color::GREY_3,
handle_color: color::GREY_3,
background: color::TRANSPARENT.into(),
border_width: 1.0,
border_color: color::GREY_3,
border_radius: 25.0.into(),
border: iced::Border {
color: color::GREY_3,
width: 1.0,
radius: 25.0.into(),
},
text_color: color::GREY_2,
},
}
@ -493,20 +577,24 @@ impl checkbox::StyleSheet for Theme {
if is_selected {
checkbox::Appearance {
background: color::GREEN.into(),
border_width: 0.0,
border_color: iced::Color::TRANSPARENT,
icon_color: color::GREY_4,
text_color: None,
border_radius: 4.0.into(),
border: iced::Border {
color: color::TRANSPARENT,
width: 1.0,
radius: 4.0.into(),
},
}
} else {
checkbox::Appearance {
background: color::GREY_4.into(),
border_width: 0.0,
border_color: iced::Color::TRANSPARENT,
icon_color: color::GREEN,
text_color: None,
border_radius: 4.0.into(),
border: iced::Border {
color: color::TRANSPARENT,
width: 0.0,
radius: 4.0.into(),
},
}
}
}
@ -539,63 +627,84 @@ impl button::StyleSheet for Theme {
Button::Primary => button::Appearance {
shadow_offset: iced::Vector::default(),
background: Some(iced::Color::TRANSPARENT.into()),
border_radius: 25.0.into(),
border_width: 1.0,
border_color: color::GREY_7,
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::Appearance {
shadow_offset: iced::Vector::default(),
background: Some(iced::Color::TRANSPARENT.into()),
border_radius: 25.0.into(),
border_width: 1.0,
border_color: color::GREY_7,
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 {
shadow_offset: iced::Vector::default(),
background: Some(iced::Color::TRANSPARENT.into()),
border_radius: 25.0.into(),
border_width: 1.0,
border_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 {
shadow_offset: iced::Vector::default(),
background: Some(iced::Color::TRANSPARENT.into()),
border_radius: 25.0.into(),
border_width: 0.0,
border_color: iced::Color::TRANSPARENT,
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 {
shadow_offset: iced::Vector::default(),
background: Some(iced::Color::TRANSPARENT.into()),
border_radius: 25.0.into(),
border_width: 0.0,
border_color: iced::Color::TRANSPARENT,
text_color: color::WHITE,
border: iced::Border {
color: color::TRANSPARENT,
width: 0.0,
radius: 25.0.into(),
},
..button::Appearance::default()
},
Button::Menu(active) => {
if *active {
button::Appearance {
shadow_offset: iced::Vector::default(),
background: Some(color::LIGHT_BLACK.into()),
border_radius: 25.0.into(),
border_width: 0.0,
border_color: iced::Color::TRANSPARENT,
text_color: color::WHITE,
border: iced::Border {
color: color::TRANSPARENT,
width: 0.0,
radius: 25.0.into(),
},
..button::Appearance::default()
}
} else {
button::Appearance {
shadow_offset: iced::Vector::default(),
background: Some(iced::Color::TRANSPARENT.into()),
border_radius: 25.0.into(),
border_width: 0.0,
border_color: iced::Color::TRANSPARENT,
text_color: color::WHITE,
border: iced::Border {
color: color::TRANSPARENT,
width: 0.0,
radius: 25.0.into(),
},
..button::Appearance::default()
}
}
}
@ -610,50 +719,68 @@ impl button::StyleSheet for Theme {
Button::Primary => button::Appearance {
shadow_offset: iced::Vector::default(),
background: Some(color::GREEN.into()),
border_radius: 25.0.into(),
border_width: 0.0,
border_color: iced::Color::TRANSPARENT,
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 {
shadow_offset: iced::Vector::default(),
background: Some(color::GREEN.into()),
border_radius: 25.0.into(),
border_width: 0.0,
border_color: iced::Color::TRANSPARENT,
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 {
shadow_offset: iced::Vector::default(),
background: Some(color::RED.into()),
border_radius: 25.0.into(),
border_width: 0.0,
border_color: iced::Color::TRANSPARENT,
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 {
shadow_offset: iced::Vector::default(),
background: Some(iced::Color::TRANSPARENT.into()),
border_radius: 25.0.into(),
border_width: 0.0,
border_color: iced::Color::TRANSPARENT,
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 {
shadow_offset: iced::Vector::default(),
background: Some(iced::Color::TRANSPARENT.into()),
border_radius: 25.0.into(),
border_width: 1.0,
border_color: color::GREEN,
text_color: color::WHITE,
border: iced::Border {
color: color::GREEN,
width: 1.0,
radius: 25.0.into(),
},
..button::Appearance::default()
},
Button::Menu(_) => button::Appearance {
shadow_offset: iced::Vector::default(),
background: Some(color::LIGHT_BLACK.into()),
border_radius: 25.0.into(),
border_width: 0.0,
border_color: iced::Color::TRANSPARENT,
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 {
icon_color: color::GREY_7,
background: iced::Background::Color(iced::Color::TRANSPARENT),
border_radius: 25.0.into(),
border_width: 1.0,
border_color: color::GREY_7,
border: iced::Border {
color: color::GREY_7,
width: 1.0,
radius: 25.0.into(),
},
},
Form::Invalid => text_input::Appearance {
icon_color: color::GREY_7,
background: iced::Background::Color(iced::Color::TRANSPARENT),
border_radius: 25.0.into(),
border_width: 1.0,
border_color: color::RED,
border: iced::Border {
color: color::RED,
width: 1.0,
radius: 25.0.into(),
},
},
}
}
@ -813,3 +944,13 @@ impl svg::StyleSheet for Theme {
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)
}
}