ui: add support for legacy
This commit is contained in:
parent
f3bf6fa589
commit
8d337eb7e1
@ -1,8 +1,8 @@
|
||||
mod section;
|
||||
|
||||
use iced::widget::{button, column, container, radio, row, text, Column, Space};
|
||||
use iced::widget::{button, column, container, radio, row, text, Space};
|
||||
use iced::{executor, Application, Command, Length, Settings, Subscription};
|
||||
use liana_ui::{theme, widget::Element};
|
||||
use liana_ui::{theme, widget::*};
|
||||
|
||||
pub fn main() -> iced::Result {
|
||||
DesignSystem::run(Settings::with_flags(Config {}))
|
||||
@ -21,6 +21,7 @@ struct DesignSystem {
|
||||
pub enum ThemeType {
|
||||
Light,
|
||||
Dark,
|
||||
Legacy,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
@ -77,6 +78,7 @@ impl Application for DesignSystem {
|
||||
self.theme = match theme {
|
||||
ThemeType::Light => theme::Theme::Light,
|
||||
ThemeType::Dark => theme::Theme::Dark,
|
||||
ThemeType::Legacy => theme::Theme::Legacy,
|
||||
}
|
||||
}
|
||||
Message::Section(i) => {
|
||||
@ -118,6 +120,7 @@ impl Application for DesignSystem {
|
||||
Some(match self.theme {
|
||||
theme::Theme::Light => ThemeType::Light,
|
||||
theme::Theme::Dark => ThemeType::Dark,
|
||||
theme::Theme::Legacy => ThemeType::Legacy,
|
||||
}),
|
||||
Message::ThemeChanged,
|
||||
))
|
||||
|
||||
@ -3,7 +3,7 @@ use iced::{
|
||||
widget::{button, column, container, row, Space},
|
||||
Alignment, Length,
|
||||
};
|
||||
use liana_ui::{color, text::*, theme, widget::Element};
|
||||
use liana_ui::{color, component::text::*, theme, widget::Element};
|
||||
|
||||
use super::{Message, Section};
|
||||
|
||||
|
||||
@ -40,3 +40,87 @@ pub const RED: Color = Color::from_rgb(
|
||||
|
||||
pub const ORANGE: Color =
|
||||
Color::from_rgb(0xFF as f32 / 255.0, 0xa7 as f32 / 255.0, 0x0 as f32 / 255.0);
|
||||
|
||||
pub mod legacy {
|
||||
use iced::Color;
|
||||
|
||||
pub const BACKGROUND: Color = Color::from_rgb(
|
||||
0xF6 as f32 / 255.0,
|
||||
0xF7 as f32 / 255.0,
|
||||
0xF8 as f32 / 255.0,
|
||||
);
|
||||
|
||||
pub const BORDER_GREY: Color = Color::from_rgb(
|
||||
0xd0 as f32 / 255.0,
|
||||
0xd7 as f32 / 255.0,
|
||||
0xde as f32 / 255.0,
|
||||
);
|
||||
|
||||
pub const FOREGROUND: Color = Color::WHITE;
|
||||
|
||||
pub const PRIMARY: Color = Color::BLACK;
|
||||
|
||||
pub const SECONDARY: Color = DARK_GREY;
|
||||
|
||||
pub const SUCCESS: Color = Color::from_rgb(
|
||||
0x29 as f32 / 255.0,
|
||||
0xBC as f32 / 255.0,
|
||||
0x97 as f32 / 255.0,
|
||||
);
|
||||
|
||||
#[allow(dead_code)]
|
||||
pub const SUCCESS_LIGHT: Color = Color::from_rgba(
|
||||
0x29 as f32 / 255.0,
|
||||
0xBC as f32 / 255.0,
|
||||
0x97 as f32 / 255.0,
|
||||
0.5f32,
|
||||
);
|
||||
|
||||
pub const ALERT: Color = Color::from_rgb(
|
||||
0xF0 as f32 / 255.0,
|
||||
0x43 as f32 / 255.0,
|
||||
0x59 as f32 / 255.0,
|
||||
);
|
||||
|
||||
pub const ALERT_LIGHT: Color = Color::from_rgba(
|
||||
0xF0 as f32 / 255.0,
|
||||
0x43 as f32 / 255.0,
|
||||
0x59 as f32 / 255.0,
|
||||
0.5f32,
|
||||
);
|
||||
|
||||
pub const WARNING: Color =
|
||||
Color::from_rgb(0xFF as f32 / 255.0, 0xa7 as f32 / 255.0, 0x0 as f32 / 255.0);
|
||||
|
||||
pub const WARNING_LIGHT: Color = Color::from_rgba(
|
||||
0xFF as f32 / 255.0,
|
||||
0xa7 as f32 / 255.0,
|
||||
0x0 as f32 / 255.0,
|
||||
0.5f32,
|
||||
);
|
||||
|
||||
pub const CANCEL: Color = Color::from_rgb(
|
||||
0x34 as f32 / 255.0,
|
||||
0x37 as f32 / 255.0,
|
||||
0x3D as f32 / 255.0,
|
||||
);
|
||||
|
||||
pub const INFO: Color = Color::from_rgb(
|
||||
0x2A as f32 / 255.0,
|
||||
0x98 as f32 / 255.0,
|
||||
0xBD as f32 / 255.0,
|
||||
);
|
||||
|
||||
pub const INFO_LIGHT: Color = Color::from_rgba(
|
||||
0x2A as f32 / 255.0,
|
||||
0x98 as f32 / 255.0,
|
||||
0xBD as f32 / 255.0,
|
||||
0.5f32,
|
||||
);
|
||||
|
||||
pub const DARK_GREY: Color = Color::from_rgb(
|
||||
0x8c as f32 / 255.0,
|
||||
0x97 as f32 / 255.0,
|
||||
0xa6 as f32 / 255.0,
|
||||
);
|
||||
}
|
||||
|
||||
101
gui/ui/src/component/badge.rs
Normal file
101
gui/ui/src/component/badge.rs
Normal file
@ -0,0 +1,101 @@
|
||||
use iced::{widget::tooltip, Length};
|
||||
|
||||
use crate::{component::text::*, icon, theme, widget::*};
|
||||
|
||||
pub struct Badge {
|
||||
icon: crate::widget::Text<'static>,
|
||||
style: theme::Badge,
|
||||
}
|
||||
|
||||
impl Badge {
|
||||
pub fn new(icon: crate::widget::Text<'static>) -> Self {
|
||||
Self {
|
||||
icon,
|
||||
style: theme::Badge::Standard,
|
||||
}
|
||||
}
|
||||
pub fn style(self, style: theme::Badge) -> Self {
|
||||
Self {
|
||||
icon: self.icon,
|
||||
style,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, Message: 'a> From<Badge> for Element<'a, Message> {
|
||||
fn from(badge: Badge) -> Element<'a, Message> {
|
||||
Container::new(badge.icon.width(Length::Units(20)))
|
||||
.width(Length::Units(40))
|
||||
.height(Length::Units(40))
|
||||
.style(theme::Container::Badge(badge.style))
|
||||
.center_x()
|
||||
.center_y()
|
||||
.into()
|
||||
}
|
||||
}
|
||||
|
||||
pub fn receive<T>() -> Container<'static, T> {
|
||||
Container::new(icon::receive_icon().width(Length::Units(20)))
|
||||
.width(Length::Units(40))
|
||||
.height(Length::Units(40))
|
||||
.style(theme::Container::Badge(theme::Badge::Standard))
|
||||
.center_x()
|
||||
.center_y()
|
||||
}
|
||||
|
||||
pub fn spend<T>() -> Container<'static, T> {
|
||||
Container::new(icon::send_icon().width(Length::Units(20)))
|
||||
.width(Length::Units(40))
|
||||
.height(Length::Units(40))
|
||||
.style(theme::Container::Badge(theme::Badge::Standard))
|
||||
.center_x()
|
||||
.center_y()
|
||||
}
|
||||
|
||||
pub fn coin<T>() -> Container<'static, T> {
|
||||
Container::new(icon::coin_icon().width(Length::Units(20)))
|
||||
.width(Length::Units(40))
|
||||
.height(Length::Units(40))
|
||||
.style(theme::Container::Badge(theme::Badge::Standard))
|
||||
.center_x()
|
||||
.center_y()
|
||||
}
|
||||
|
||||
pub fn unconfirmed<'a, T: 'a>() -> Container<'a, T> {
|
||||
Container::new(
|
||||
tooltip::Tooltip::new(
|
||||
Container::new(text(" Unconfirmed ").small())
|
||||
.padding(3)
|
||||
.style(theme::Container::Pill(theme::Pill::Simple)),
|
||||
"Do not treat this as a payment until it is confirmed",
|
||||
tooltip::Position::Top,
|
||||
)
|
||||
.style(theme::Container::Card(theme::Card::Simple)),
|
||||
)
|
||||
}
|
||||
|
||||
pub fn deprecated<'a, T: 'a>() -> Container<'a, T> {
|
||||
Container::new(
|
||||
tooltip::Tooltip::new(
|
||||
Container::new(text(" Deprecated ").small())
|
||||
.padding(3)
|
||||
.style(theme::Container::Pill(theme::Pill::Simple)),
|
||||
"This spend cannot be included anymore in the blockchain",
|
||||
tooltip::Position::Top,
|
||||
)
|
||||
.style(theme::Container::Card(theme::Card::Simple)),
|
||||
)
|
||||
}
|
||||
|
||||
pub fn spent<'a, T: 'a>() -> Container<'a, T> {
|
||||
Container::new(
|
||||
tooltip::Tooltip::new(
|
||||
Container::new(text(" Spent ").small())
|
||||
.padding(3)
|
||||
.style(theme::Container::Pill(theme::Pill::Simple)),
|
||||
"The spend transaction was included in the blockchain",
|
||||
tooltip::Position::Top,
|
||||
)
|
||||
.style(theme::Container::Card(theme::Card::Simple)),
|
||||
)
|
||||
}
|
||||
40
gui/ui/src/component/button.rs
Normal file
40
gui/ui/src/component/button.rs
Normal file
@ -0,0 +1,40 @@
|
||||
use crate::{theme, widget::*};
|
||||
use iced::widget::{button, container, row};
|
||||
use iced::{Alignment, Length};
|
||||
|
||||
use super::text::text;
|
||||
|
||||
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.into())
|
||||
}
|
||||
|
||||
pub fn primary<'a, T: 'a>(icon: Option<Text<'a>>, t: &'static str) -> Button<'a, T> {
|
||||
button::Button::new(content(icon, t)).style(theme::Button::Primary.into())
|
||||
}
|
||||
|
||||
pub fn transparent<'a, T: 'a>(icon: Option<Text<'a>>, t: &'static str) -> Button<'a, T> {
|
||||
button::Button::new(content(icon, t)).style(theme::Button::Transparent.into())
|
||||
}
|
||||
|
||||
pub fn border<'a, T: 'a>(icon: Option<Text<'a>>, t: &'static str) -> Button<'a, T> {
|
||||
button::Button::new(content(icon, t)).style(theme::Button::Secondary.into())
|
||||
}
|
||||
|
||||
pub fn transparent_border<'a, T: 'a>(icon: Option<Text<'a>>, t: &'static str) -> Button<'a, T> {
|
||||
button(content(icon, t)).style(theme::Button::TransparentBorder.into())
|
||||
}
|
||||
|
||||
fn content<'a, T: 'a>(icon: Option<Text<'a>>, t: &'static str) -> Container<'a, T> {
|
||||
match icon {
|
||||
None => container(text(t)).width(Length::Fill).center_x().padding(5),
|
||||
Some(i) => container(
|
||||
row![i, text(t)]
|
||||
.spacing(10)
|
||||
.width(iced::Length::Fill)
|
||||
.align_items(Alignment::Center),
|
||||
)
|
||||
.width(iced::Length::Fill)
|
||||
.center_x()
|
||||
.padding(5),
|
||||
}
|
||||
}
|
||||
44
gui/ui/src/component/card.rs
Normal file
44
gui/ui/src/component/card.rs
Normal file
@ -0,0 +1,44 @@
|
||||
use crate::{color, component::text::text, icon, theme, widget::*};
|
||||
|
||||
pub fn simple<'a, T: 'a, C: Into<Element<'a, T>>>(content: C) -> Container<'a, T> {
|
||||
Container::new(content)
|
||||
.padding(15)
|
||||
.style(theme::Container::Card(theme::Card::Simple))
|
||||
}
|
||||
|
||||
pub fn invalid<'a, T: 'a, C: Into<Element<'a, T>>>(content: C) -> Container<'a, T> {
|
||||
Container::new(content)
|
||||
.padding(15)
|
||||
.style(theme::Container::Card(theme::Card::Invalid))
|
||||
}
|
||||
|
||||
/// display an error card with the message and the error in a tooltip.
|
||||
pub fn warning<'a, T: 'a>(message: String) -> Container<'a, T> {
|
||||
Container::new(
|
||||
Row::new()
|
||||
.spacing(20)
|
||||
.align_items(iced::Alignment::Center)
|
||||
.push(icon::warning_octagon_icon().style(color::legacy::WARNING))
|
||||
.push(text(message).style(color::legacy::WARNING)),
|
||||
)
|
||||
.padding(15)
|
||||
.style(theme::Container::Card(theme::Card::Warning))
|
||||
}
|
||||
|
||||
/// display an error card with the message and the error in a tooltip.
|
||||
pub fn error<'a, T: 'a>(message: &'static str, error: String) -> Container<'a, T> {
|
||||
Container::new(
|
||||
iced::widget::tooltip::Tooltip::new(
|
||||
Row::new()
|
||||
.spacing(20)
|
||||
.align_items(iced::Alignment::Center)
|
||||
.push(icon::warning_icon().style(color::legacy::ALERT))
|
||||
.push(text(message).style(color::legacy::ALERT)),
|
||||
error,
|
||||
iced::widget::tooltip::Position::Bottom,
|
||||
)
|
||||
.style(theme::Container::Card(theme::Card::Error)),
|
||||
)
|
||||
.padding(15)
|
||||
.style(theme::Container::Card(theme::Card::Error))
|
||||
}
|
||||
83
gui/ui/src/component/collapse.rs
Normal file
83
gui/ui/src/component/collapse.rs
Normal file
@ -0,0 +1,83 @@
|
||||
use crate::widget::*;
|
||||
use iced::widget::column;
|
||||
use iced_lazy::{self, Component};
|
||||
use std::marker::PhantomData;
|
||||
|
||||
pub struct Collapse<'a, M, H, F, C> {
|
||||
before: H,
|
||||
after: F,
|
||||
content: C,
|
||||
phantom: PhantomData<&'a M>,
|
||||
}
|
||||
|
||||
impl<'a, Message, T, H, F, C> Collapse<'a, Message, H, F, C>
|
||||
where
|
||||
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,
|
||||
{
|
||||
pub fn new(before: H, after: F, content: C) -> Self {
|
||||
Collapse {
|
||||
before,
|
||||
after,
|
||||
content,
|
||||
phantom: PhantomData,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
pub enum Event<T> {
|
||||
Internal(T),
|
||||
Collapse(bool),
|
||||
}
|
||||
|
||||
impl<'a, Message, T, H, F, C> Component<Message, iced::Renderer<crate::theme::Theme>>
|
||||
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>,
|
||||
{
|
||||
type State = bool;
|
||||
type Event = Event<T>;
|
||||
|
||||
fn update(&mut self, state: &mut Self::State, event: Event<T>) -> Option<Message> {
|
||||
match event {
|
||||
Event::Internal(e) => Some(e.into()),
|
||||
Event::Collapse(s) => {
|
||||
*state = s;
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn view(&self, state: &Self::State) -> Element<Self::Event> {
|
||||
if *state {
|
||||
column![
|
||||
(self.after)().on_press(Event::Collapse(false)),
|
||||
(self.content)().map(Event::Internal)
|
||||
]
|
||||
.into()
|
||||
} else {
|
||||
column![(self.before)().on_press(Event::Collapse(true))].into()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, Message, T, H: 'a, F: 'a, C: 'a> From<Collapse<'a, Message, H, F, C>>
|
||||
for Element<'a, Message>
|
||||
where
|
||||
Message: 'a,
|
||||
T: Into<Message> + Clone + 'a,
|
||||
H: Fn() -> Button<'a, Event<T>>,
|
||||
F: Fn() -> Button<'a, Event<T>>,
|
||||
C: Fn() -> Element<'a, T>,
|
||||
{
|
||||
fn from(c: Collapse<'a, Message, H, F, C>) -> Self {
|
||||
iced_lazy::component(c)
|
||||
}
|
||||
}
|
||||
87
gui/ui/src/component/form.rs
Normal file
87
gui/ui/src/component/form.rs
Normal file
@ -0,0 +1,87 @@
|
||||
use iced::{widget::text_input, Length};
|
||||
|
||||
use crate::{color, component::text::*, theme, util::Collection, widget::*};
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct Value<T> {
|
||||
pub value: T,
|
||||
pub valid: bool,
|
||||
}
|
||||
|
||||
impl std::default::Default for Value<String> {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
value: "".to_string(),
|
||||
valid: true,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct Form<'a, Message> {
|
||||
input: text_input::TextInput<'a, Message, iced::Renderer<theme::Theme>>,
|
||||
warning: Option<&'a str>,
|
||||
valid: bool,
|
||||
}
|
||||
|
||||
impl<'a, Message: 'a> Form<'a, Message>
|
||||
where
|
||||
Message: Clone,
|
||||
{
|
||||
/// Creates a new [`Form`].
|
||||
///
|
||||
/// It expects:
|
||||
/// - a placeholder
|
||||
/// - the current value
|
||||
/// - a function that produces a message when the [`Form`] changes
|
||||
pub fn new<F>(placeholder: &str, value: &Value<String>, on_change: F) -> Self
|
||||
where
|
||||
F: 'static + Fn(String) -> Message,
|
||||
{
|
||||
Self {
|
||||
input: text_input::TextInput::new(placeholder, &value.value, on_change),
|
||||
warning: None,
|
||||
valid: value.valid,
|
||||
}
|
||||
}
|
||||
|
||||
/// Sets the [`Form`] with a warning message
|
||||
pub fn warning(mut self, warning: &'a str) -> Self {
|
||||
self.warning = Some(warning);
|
||||
self
|
||||
}
|
||||
|
||||
/// Sets the padding of the [`Form`].
|
||||
pub fn padding(mut self, units: u16) -> Self {
|
||||
self.input = self.input.padding(units);
|
||||
self
|
||||
}
|
||||
|
||||
/// Sets the [`Form`] with a text size
|
||||
pub fn size(mut self, size: u16) -> Self {
|
||||
self.input = self.input.size(size);
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, Message: 'a + Clone> From<Form<'a, Message>> for Element<'a, Message> {
|
||||
fn from(form: Form<'a, Message>) -> Element<'a, Message> {
|
||||
Container::new(
|
||||
Column::new()
|
||||
.push(if !form.valid {
|
||||
form.input.style(theme::Form::Invalid)
|
||||
} else {
|
||||
form.input
|
||||
})
|
||||
.push_maybe(if !form.valid {
|
||||
form.warning
|
||||
.map(|message| text(message).style(color::legacy::ALERT).small())
|
||||
} else {
|
||||
None
|
||||
})
|
||||
.width(Length::Fill)
|
||||
.spacing(5),
|
||||
)
|
||||
.width(Length::Fill)
|
||||
.into()
|
||||
}
|
||||
}
|
||||
21
gui/ui/src/component/mod.rs
Normal file
21
gui/ui/src/component/mod.rs
Normal file
@ -0,0 +1,21 @@
|
||||
pub mod badge;
|
||||
pub mod button;
|
||||
pub mod card;
|
||||
pub mod collapse;
|
||||
pub mod form;
|
||||
pub mod modal;
|
||||
pub mod notification;
|
||||
pub mod text;
|
||||
pub mod tooltip;
|
||||
|
||||
pub use tooltip::tooltip;
|
||||
|
||||
use iced::Length;
|
||||
|
||||
use crate::{theme, widget::*};
|
||||
|
||||
pub fn separation<'a, T: 'a>() -> Container<'a, T> {
|
||||
Container::new(Column::new().push(Text::new(" ")))
|
||||
.style(theme::Container::Border)
|
||||
.height(Length::Units(1))
|
||||
}
|
||||
279
gui/ui/src/component/modal.rs
Normal file
279
gui/ui/src/component/modal.rs
Normal file
@ -0,0 +1,279 @@
|
||||
/// modal widget from https://github.com/iced-rs/iced/blob/master/examples/modal/
|
||||
use iced_native::alignment::Alignment;
|
||||
use iced_native::widget::{self, Tree};
|
||||
use iced_native::{
|
||||
event, layout, mouse, overlay, renderer, Clipboard, Color, Element, Event, Layout, Length,
|
||||
Point, Rectangle, Shell, Size, Widget,
|
||||
};
|
||||
|
||||
/// 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>,
|
||||
on_blur: Option<Message>,
|
||||
}
|
||||
|
||||
impl<'a, Message, Renderer> Modal<'a, Message, Renderer> {
|
||||
/// Returns a new [`Modal`]
|
||||
pub fn new(
|
||||
base: impl Into<Element<'a, Message, Renderer>>,
|
||||
modal: impl Into<Element<'a, Message, Renderer>>,
|
||||
) -> Self {
|
||||
Self {
|
||||
base: base.into(),
|
||||
modal: modal.into(),
|
||||
on_blur: None,
|
||||
}
|
||||
}
|
||||
|
||||
/// Sets the message that will be produces when the background
|
||||
/// of the [`Modal`] is pressed
|
||||
pub fn on_blur(self, on_blur: Option<Message>) -> Self {
|
||||
Self { on_blur, ..self }
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, Message, Renderer> Widget<Message, Renderer> for Modal<'a, Message, Renderer>
|
||||
where
|
||||
Renderer: iced_native::Renderer,
|
||||
Message: Clone,
|
||||
{
|
||||
fn children(&self) -> Vec<Tree> {
|
||||
vec![Tree::new(&self.base), Tree::new(&self.modal)]
|
||||
}
|
||||
|
||||
fn diff(&self, tree: &mut Tree) {
|
||||
tree.diff_children(&[&self.base, &self.modal]);
|
||||
}
|
||||
|
||||
fn width(&self) -> Length {
|
||||
self.base.as_widget().width()
|
||||
}
|
||||
|
||||
fn height(&self) -> Length {
|
||||
self.base.as_widget().height()
|
||||
}
|
||||
|
||||
fn layout(&self, renderer: &Renderer, limits: &layout::Limits) -> layout::Node {
|
||||
self.base.as_widget().layout(renderer, limits)
|
||||
}
|
||||
|
||||
fn on_event(
|
||||
&mut self,
|
||||
state: &mut Tree,
|
||||
event: Event,
|
||||
layout: Layout<'_>,
|
||||
cursor_position: Point,
|
||||
renderer: &Renderer,
|
||||
clipboard: &mut dyn Clipboard,
|
||||
shell: &mut Shell<'_, Message>,
|
||||
) -> event::Status {
|
||||
self.base.as_widget_mut().on_event(
|
||||
&mut state.children[0],
|
||||
event,
|
||||
layout,
|
||||
cursor_position,
|
||||
renderer,
|
||||
clipboard,
|
||||
shell,
|
||||
)
|
||||
}
|
||||
|
||||
fn draw(
|
||||
&self,
|
||||
state: &Tree,
|
||||
renderer: &mut Renderer,
|
||||
theme: &<Renderer as iced_native::Renderer>::Theme,
|
||||
style: &renderer::Style,
|
||||
layout: Layout<'_>,
|
||||
cursor_position: Point,
|
||||
viewport: &Rectangle,
|
||||
) {
|
||||
self.base.as_widget().draw(
|
||||
&state.children[0],
|
||||
renderer,
|
||||
theme,
|
||||
style,
|
||||
layout,
|
||||
cursor_position,
|
||||
viewport,
|
||||
);
|
||||
}
|
||||
|
||||
fn overlay<'b>(
|
||||
&'b mut self,
|
||||
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(),
|
||||
}),
|
||||
))
|
||||
}
|
||||
|
||||
fn mouse_interaction(
|
||||
&self,
|
||||
state: &Tree,
|
||||
layout: Layout<'_>,
|
||||
cursor_position: Point,
|
||||
viewport: &Rectangle,
|
||||
renderer: &Renderer,
|
||||
) -> mouse::Interaction {
|
||||
self.base.as_widget().mouse_interaction(
|
||||
&state.children[0],
|
||||
layout,
|
||||
cursor_position,
|
||||
viewport,
|
||||
renderer,
|
||||
)
|
||||
}
|
||||
|
||||
fn operate(
|
||||
&self,
|
||||
state: &mut Tree,
|
||||
layout: Layout<'_>,
|
||||
renderer: &Renderer,
|
||||
operation: &mut dyn widget::Operation<Message>,
|
||||
) {
|
||||
self.base
|
||||
.as_widget()
|
||||
.operate(&mut state.children[0], layout, renderer, operation);
|
||||
}
|
||||
}
|
||||
|
||||
struct Overlay<'a, 'b, Message, Renderer> {
|
||||
content: &'b mut Element<'a, Message, 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>
|
||||
where
|
||||
Renderer: iced_native::Renderer,
|
||||
Message: Clone,
|
||||
{
|
||||
fn layout(&self, renderer: &Renderer, _bounds: Size, position: Point) -> 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 mut node = layout::Node::with_children(self.size, vec![child]);
|
||||
node.move_to(position);
|
||||
|
||||
node
|
||||
}
|
||||
|
||||
fn on_event(
|
||||
&mut self,
|
||||
event: Event,
|
||||
layout: Layout<'_>,
|
||||
cursor_position: Point,
|
||||
renderer: &Renderer,
|
||||
clipboard: &mut dyn Clipboard,
|
||||
shell: &mut Shell<'_, Message>,
|
||||
) -> event::Status {
|
||||
let content_bounds = layout.children().next().unwrap().bounds();
|
||||
|
||||
if let Some(message) = self.on_blur.as_ref() {
|
||||
if let Event::Mouse(mouse::Event::ButtonPressed(mouse::Button::Left)) = &event {
|
||||
if !content_bounds.contains(cursor_position) {
|
||||
shell.publish(message.clone());
|
||||
return event::Status::Captured;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
self.content.as_widget_mut().on_event(
|
||||
self.tree,
|
||||
event,
|
||||
layout.children().next().unwrap(),
|
||||
cursor_position,
|
||||
renderer,
|
||||
clipboard,
|
||||
shell,
|
||||
)
|
||||
}
|
||||
|
||||
fn draw(
|
||||
&self,
|
||||
renderer: &mut Renderer,
|
||||
theme: &Renderer::Theme,
|
||||
style: &renderer::Style,
|
||||
layout: Layout<'_>,
|
||||
cursor_position: Point,
|
||||
) {
|
||||
renderer.fill_quad(
|
||||
renderer::Quad {
|
||||
bounds: layout.bounds(),
|
||||
border_radius: renderer::BorderRadius::from(0.0),
|
||||
border_width: 0.0,
|
||||
border_color: Color::TRANSPARENT,
|
||||
},
|
||||
Color {
|
||||
a: 0.80,
|
||||
..Color::BLACK
|
||||
},
|
||||
);
|
||||
|
||||
self.content.as_widget().draw(
|
||||
self.tree,
|
||||
renderer,
|
||||
theme,
|
||||
style,
|
||||
layout.children().next().unwrap(),
|
||||
cursor_position,
|
||||
&layout.bounds(),
|
||||
);
|
||||
}
|
||||
|
||||
fn operate(
|
||||
&mut self,
|
||||
layout: Layout<'_>,
|
||||
renderer: &Renderer,
|
||||
operation: &mut dyn widget::Operation<Message>,
|
||||
) {
|
||||
self.content.as_widget().operate(
|
||||
self.tree,
|
||||
layout.children().next().unwrap(),
|
||||
renderer,
|
||||
operation,
|
||||
);
|
||||
}
|
||||
|
||||
fn mouse_interaction(
|
||||
&self,
|
||||
layout: Layout<'_>,
|
||||
cursor_position: Point,
|
||||
viewport: &Rectangle,
|
||||
renderer: &Renderer,
|
||||
) -> mouse::Interaction {
|
||||
self.content.as_widget().mouse_interaction(
|
||||
self.tree,
|
||||
layout.children().next().unwrap(),
|
||||
cursor_position,
|
||||
viewport,
|
||||
renderer,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, Message, Renderer> From<Modal<'a, Message, Renderer>> for Element<'a, Message, Renderer>
|
||||
where
|
||||
Renderer: 'a + iced_native::Renderer,
|
||||
Message: 'a + Clone,
|
||||
{
|
||||
fn from(modal: Modal<'a, Message, Renderer>) -> Self {
|
||||
Element::new(modal)
|
||||
}
|
||||
}
|
||||
49
gui/ui/src/component/notification.rs
Normal file
49
gui/ui/src/component/notification.rs
Normal file
@ -0,0 +1,49 @@
|
||||
use crate::{
|
||||
component::{collapse, text::*},
|
||||
icon, theme,
|
||||
widget::*,
|
||||
};
|
||||
use iced::{Alignment, Length};
|
||||
|
||||
pub fn warning<'a, T: 'a + Clone>(message: String, error: String) -> Container<'a, T> {
|
||||
let message_clone = message.clone();
|
||||
Container::new(Container::new(collapse::Collapse::new(
|
||||
move || {
|
||||
Button::new(
|
||||
Row::new()
|
||||
.push(
|
||||
Container::new(text(message_clone.to_string()).small().bold())
|
||||
.width(Length::Fill),
|
||||
)
|
||||
.push(
|
||||
Row::new()
|
||||
.align_items(Alignment::Center)
|
||||
.spacing(10)
|
||||
.push(text("Learn more").small().bold())
|
||||
.push(icon::collapse_icon()),
|
||||
),
|
||||
)
|
||||
.style(theme::Button::Transparent)
|
||||
},
|
||||
move || {
|
||||
Button::new(
|
||||
Row::new()
|
||||
.push(
|
||||
Container::new(text(message.to_owned()).small().bold()).width(Length::Fill),
|
||||
)
|
||||
.push(
|
||||
Row::new()
|
||||
.align_items(Alignment::Center)
|
||||
.spacing(10)
|
||||
.push(text("Learn more").small().bold())
|
||||
.push(icon::collapsed_icon()),
|
||||
),
|
||||
)
|
||||
.style(theme::Button::Transparent)
|
||||
},
|
||||
move || Element::<'a, T>::from(text(error.to_owned()).small()),
|
||||
)))
|
||||
.padding(15)
|
||||
.style(theme::Container::Card(theme::Card::Warning))
|
||||
.width(Length::Fill)
|
||||
}
|
||||
12
gui/ui/src/component/tooltip.rs
Normal file
12
gui/ui/src/component/tooltip.rs
Normal file
@ -0,0 +1,12 @@
|
||||
use crate::{icon, theme, widget::*};
|
||||
|
||||
pub fn tooltip<'a, T: 'a>(help: &'static str) -> Container<'a, T> {
|
||||
Container::new(
|
||||
iced::widget::tooltip::Tooltip::new(
|
||||
icon::tooltip_icon(),
|
||||
help,
|
||||
iced::widget::tooltip::Position::Right,
|
||||
)
|
||||
.style(theme::Container::Card(theme::Card::Simple)),
|
||||
)
|
||||
}
|
||||
233
gui/ui/src/icon.rs
Normal file
233
gui/ui/src/icon.rs
Normal file
@ -0,0 +1,233 @@
|
||||
use crate::widget::*;
|
||||
use iced::{alignment, Font, Length};
|
||||
|
||||
const ICONS: Font = Font::External {
|
||||
name: "Icons",
|
||||
bytes: include_bytes!("../../static/icons/bootstrap-icons.ttf"),
|
||||
};
|
||||
|
||||
fn icon(unicode: char) -> Text<'static> {
|
||||
Text::new(unicode.to_string())
|
||||
.font(ICONS)
|
||||
.width(Length::Units(20))
|
||||
.horizontal_alignment(alignment::Horizontal::Center)
|
||||
.size(20)
|
||||
}
|
||||
|
||||
pub fn arrow_down() -> Text<'static> {
|
||||
icon('\u{F128}')
|
||||
}
|
||||
|
||||
pub fn chevron_right() -> Text<'static> {
|
||||
icon('\u{F285}')
|
||||
}
|
||||
|
||||
pub fn recovery_icon() -> Text<'static> {
|
||||
icon('\u{F467}')
|
||||
}
|
||||
|
||||
pub fn plug_icon() -> Text<'static> {
|
||||
icon('\u{F4F6}')
|
||||
}
|
||||
|
||||
pub fn reload_icon() -> Text<'static> {
|
||||
icon('\u{F130}')
|
||||
}
|
||||
|
||||
pub fn import_icon() -> Text<'static> {
|
||||
icon('\u{F30A}')
|
||||
}
|
||||
|
||||
pub fn wallet_icon() -> Text<'static> {
|
||||
icon('\u{F615}')
|
||||
}
|
||||
|
||||
pub fn hourglass_icon() -> Text<'static> {
|
||||
icon('\u{F41F}')
|
||||
}
|
||||
|
||||
pub fn hourglass_done_icon() -> Text<'static> {
|
||||
icon('\u{F41E}')
|
||||
}
|
||||
|
||||
pub fn vault_icon() -> Text<'static> {
|
||||
icon('\u{F65A}')
|
||||
}
|
||||
|
||||
pub fn bitcoin_icon() -> Text<'static> {
|
||||
icon('\u{F635}')
|
||||
}
|
||||
|
||||
pub fn history_icon() -> Text<'static> {
|
||||
icon('\u{F292}')
|
||||
}
|
||||
|
||||
pub fn home_icon() -> Text<'static> {
|
||||
icon('\u{F3FC}')
|
||||
}
|
||||
|
||||
pub fn unlock_icon() -> Text<'static> {
|
||||
icon('\u{F600}')
|
||||
}
|
||||
|
||||
pub fn warning_octagon_icon() -> Text<'static> {
|
||||
icon('\u{F337}')
|
||||
}
|
||||
|
||||
pub fn send_icon() -> Text<'static> {
|
||||
icon('\u{F144}')
|
||||
}
|
||||
|
||||
pub fn connect_device_icon() -> Text<'static> {
|
||||
icon('\u{F348}')
|
||||
}
|
||||
|
||||
pub fn connected_device_icon() -> Text<'static> {
|
||||
icon('\u{F350}')
|
||||
}
|
||||
|
||||
pub fn receive_icon() -> Text<'static> {
|
||||
icon('\u{F123}')
|
||||
}
|
||||
|
||||
pub fn calendar_icon() -> Text<'static> {
|
||||
icon('\u{F1E8}')
|
||||
}
|
||||
|
||||
pub fn turnback_icon() -> Text<'static> {
|
||||
icon('\u{F131}')
|
||||
}
|
||||
|
||||
pub fn vaults_icon() -> Text<'static> {
|
||||
icon('\u{F1C7}')
|
||||
}
|
||||
|
||||
pub fn coin_icon() -> Text<'static> {
|
||||
icon('\u{F567}')
|
||||
}
|
||||
|
||||
pub fn settings_icon() -> Text<'static> {
|
||||
icon('\u{F3E5}')
|
||||
}
|
||||
|
||||
pub fn block_icon() -> Text<'static> {
|
||||
icon('\u{F1C8}')
|
||||
}
|
||||
|
||||
pub fn square_icon() -> Text<'static> {
|
||||
icon('\u{F584}')
|
||||
}
|
||||
|
||||
pub fn square_check_icon() -> Text<'static> {
|
||||
icon('\u{F26D}')
|
||||
}
|
||||
|
||||
pub fn circle_check_icon() -> Text<'static> {
|
||||
icon('\u{F26B}')
|
||||
}
|
||||
|
||||
pub fn circle_cross_icon() -> Text<'static> {
|
||||
icon('\u{F623}')
|
||||
}
|
||||
|
||||
pub fn network_icon() -> Text<'static> {
|
||||
icon('\u{F40D}')
|
||||
}
|
||||
|
||||
pub fn dot_icon() -> Text<'static> {
|
||||
icon('\u{F287}')
|
||||
}
|
||||
|
||||
pub fn clipboard_icon() -> Text<'static> {
|
||||
icon('\u{F3C2}')
|
||||
}
|
||||
|
||||
pub fn shield_icon() -> Text<'static> {
|
||||
icon('\u{F53F}')
|
||||
}
|
||||
|
||||
pub fn shield_notif_icon() -> Text<'static> {
|
||||
icon('\u{F530}')
|
||||
}
|
||||
|
||||
pub fn shield_check_icon() -> Text<'static> {
|
||||
icon('\u{F52F}')
|
||||
}
|
||||
|
||||
pub fn person_check_icon() -> Text<'static> {
|
||||
icon('\u{F4D6}')
|
||||
}
|
||||
|
||||
pub fn person_icon() -> Text<'static> {
|
||||
icon('\u{F4DA}')
|
||||
}
|
||||
|
||||
pub fn tooltip_icon() -> Text<'static> {
|
||||
icon('\u{F431}')
|
||||
}
|
||||
|
||||
pub fn plus_icon() -> Text<'static> {
|
||||
icon('\u{F4FE}')
|
||||
}
|
||||
|
||||
pub fn warning_icon() -> Text<'static> {
|
||||
icon('\u{F33B}')
|
||||
}
|
||||
|
||||
pub fn chip_icon() -> Text<'static> {
|
||||
icon('\u{F2D6}')
|
||||
}
|
||||
|
||||
pub fn trash_icon() -> Text<'static> {
|
||||
icon('\u{F5DE}')
|
||||
}
|
||||
|
||||
pub fn key_icon() -> Text<'static> {
|
||||
icon('\u{F44F}')
|
||||
}
|
||||
|
||||
pub fn cross_icon() -> Text<'static> {
|
||||
icon('\u{F62A}')
|
||||
}
|
||||
|
||||
pub fn pencil_icon() -> Text<'static> {
|
||||
icon('\u{F4CB}')
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
pub fn stakeholder_icon() -> Text<'static> {
|
||||
icon('\u{F4AE}')
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
pub fn manager_icon() -> Text<'static> {
|
||||
icon('\u{F4B4}')
|
||||
}
|
||||
|
||||
pub fn done_icon() -> Text<'static> {
|
||||
icon('\u{F26B}')
|
||||
}
|
||||
|
||||
pub fn todo_icon() -> Text<'static> {
|
||||
icon('\u{F28A}')
|
||||
}
|
||||
|
||||
pub fn collapse_icon() -> Text<'static> {
|
||||
icon('\u{F284}')
|
||||
}
|
||||
|
||||
pub fn collapsed_icon() -> Text<'static> {
|
||||
icon('\u{F282}')
|
||||
}
|
||||
|
||||
pub fn down_icon() -> Text<'static> {
|
||||
icon('\u{F279}')
|
||||
}
|
||||
|
||||
pub fn up_icon() -> Text<'static> {
|
||||
icon('\u{F27C}')
|
||||
}
|
||||
|
||||
pub fn people_icon() -> Text<'static> {
|
||||
icon('\u{F4CF}')
|
||||
}
|
||||
@ -1,7 +1,9 @@
|
||||
pub mod color;
|
||||
pub mod component;
|
||||
pub mod font;
|
||||
pub mod text;
|
||||
pub mod icon;
|
||||
pub mod theme;
|
||||
pub mod util;
|
||||
|
||||
pub mod widget {
|
||||
#![allow(dead_code)]
|
||||
@ -10,5 +12,9 @@ pub mod widget {
|
||||
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>;
|
||||
}
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
use iced::{
|
||||
application,
|
||||
widget::{button, container, radio, text},
|
||||
widget::{button, container, radio, text, text_input},
|
||||
};
|
||||
|
||||
use super::color;
|
||||
@ -10,6 +10,7 @@ pub enum Theme {
|
||||
#[default]
|
||||
Dark,
|
||||
Light,
|
||||
Legacy,
|
||||
}
|
||||
|
||||
impl application::StyleSheet for Theme {
|
||||
@ -25,6 +26,10 @@ impl application::StyleSheet for Theme {
|
||||
background_color: color::LIGHT_BLACK,
|
||||
text_color: color::LIGHT_GREY,
|
||||
},
|
||||
Theme::Legacy => application::Appearance {
|
||||
background_color: color::legacy::BACKGROUND,
|
||||
text_color: color::BLACK,
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -60,6 +65,9 @@ pub enum Container {
|
||||
Background,
|
||||
Foreground,
|
||||
Border,
|
||||
Card(Card),
|
||||
Badge(Badge),
|
||||
Pill(Pill),
|
||||
Custom(iced::Color),
|
||||
}
|
||||
|
||||
@ -86,6 +94,9 @@ impl container::StyleSheet for Theme {
|
||||
border_color: color::LIGHT_BLACK.into(),
|
||||
..container::Appearance::default()
|
||||
},
|
||||
Container::Card(c) => c.appearance(self),
|
||||
Container::Badge(c) => c.appearance(self),
|
||||
Container::Pill(c) => c.appearance(self),
|
||||
Container::Custom(c) => container::Appearance {
|
||||
background: (*c).into(),
|
||||
..container::Appearance::default()
|
||||
@ -110,11 +121,204 @@ impl container::StyleSheet for Theme {
|
||||
border_color: color::LIGHT_GREY.into(),
|
||||
..container::Appearance::default()
|
||||
},
|
||||
Container::Card(c) => c.appearance(self),
|
||||
Container::Badge(c) => c.appearance(self),
|
||||
Container::Pill(c) => c.appearance(self),
|
||||
Container::Custom(c) => container::Appearance {
|
||||
background: (*c).into(),
|
||||
..container::Appearance::default()
|
||||
},
|
||||
},
|
||||
Theme::Legacy => match style {
|
||||
Container::Transparent => container::Appearance {
|
||||
background: iced::Color::TRANSPARENT.into(),
|
||||
..container::Appearance::default()
|
||||
},
|
||||
Container::Background => container::Appearance {
|
||||
background: color::legacy::BACKGROUND.into(),
|
||||
..container::Appearance::default()
|
||||
},
|
||||
Container::Foreground => container::Appearance {
|
||||
background: color::legacy::FOREGROUND.into(),
|
||||
..container::Appearance::default()
|
||||
},
|
||||
Container::Border => container::Appearance {
|
||||
background: iced::Color::TRANSPARENT.into(),
|
||||
border_width: 1.0,
|
||||
border_color: color::legacy::BORDER_GREY.into(),
|
||||
..container::Appearance::default()
|
||||
},
|
||||
Container::Card(c) => c.appearance(self),
|
||||
Container::Badge(c) => c.appearance(self),
|
||||
Container::Pill(c) => c.appearance(self),
|
||||
Container::Custom(c) => container::Appearance {
|
||||
background: (*c).into(),
|
||||
..container::Appearance::default()
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Copy, Clone, Default)]
|
||||
pub enum Card {
|
||||
#[default]
|
||||
Simple,
|
||||
Invalid,
|
||||
Warning,
|
||||
Error,
|
||||
}
|
||||
|
||||
impl Card {
|
||||
fn appearance(&self, theme: &Theme) -> iced::widget::container::Appearance {
|
||||
match theme {
|
||||
Theme::Light => match self {
|
||||
Card::Simple => container::Appearance {
|
||||
background: color::GREY.into(),
|
||||
..container::Appearance::default()
|
||||
},
|
||||
Card::Invalid => container::Appearance {
|
||||
background: color::GREY.into(),
|
||||
text_color: color::BLACK.into(),
|
||||
border_width: 1.0,
|
||||
border_color: color::RED,
|
||||
..container::Appearance::default()
|
||||
},
|
||||
Card::Error => container::Appearance {
|
||||
background: color::GREY.into(),
|
||||
text_color: color::RED.into(),
|
||||
border_width: 1.0,
|
||||
border_color: color::RED,
|
||||
..container::Appearance::default()
|
||||
},
|
||||
Card::Warning => container::Appearance {
|
||||
background: color::ORANGE.into(),
|
||||
text_color: color::GREY.into(),
|
||||
..container::Appearance::default()
|
||||
},
|
||||
},
|
||||
Theme::Dark => match self {
|
||||
Card::Simple => container::Appearance {
|
||||
background: color::LIGHT_BLACK.into(),
|
||||
..container::Appearance::default()
|
||||
},
|
||||
Card::Invalid => container::Appearance {
|
||||
background: color::LIGHT_BLACK.into(),
|
||||
text_color: color::BLACK.into(),
|
||||
border_width: 1.0,
|
||||
border_color: color::RED,
|
||||
..container::Appearance::default()
|
||||
},
|
||||
Card::Error => container::Appearance {
|
||||
background: color::LIGHT_BLACK.into(),
|
||||
text_color: color::RED.into(),
|
||||
border_width: 1.0,
|
||||
border_color: color::RED,
|
||||
..container::Appearance::default()
|
||||
},
|
||||
Card::Warning => container::Appearance {
|
||||
background: color::ORANGE.into(),
|
||||
text_color: color::GREY.into(),
|
||||
..container::Appearance::default()
|
||||
},
|
||||
},
|
||||
Theme::Legacy => match self {
|
||||
Card::Simple => container::Appearance {
|
||||
background: color::legacy::FOREGROUND.into(),
|
||||
border_radius: 10.0,
|
||||
border_color: color::legacy::BORDER_GREY,
|
||||
border_width: 1.0,
|
||||
..container::Appearance::default()
|
||||
},
|
||||
Card::Invalid => container::Appearance {
|
||||
background: color::legacy::FOREGROUND.into(),
|
||||
text_color: iced::Color::BLACK.into(),
|
||||
border_width: 1.0,
|
||||
border_radius: 10.0,
|
||||
border_color: color::legacy::ALERT,
|
||||
..container::Appearance::default()
|
||||
},
|
||||
Card::Error => container::Appearance {
|
||||
background: color::legacy::FOREGROUND.into(),
|
||||
text_color: color::legacy::ALERT.into(),
|
||||
border_width: 1.0,
|
||||
border_radius: 10.0,
|
||||
border_color: color::legacy::ALERT,
|
||||
..container::Appearance::default()
|
||||
},
|
||||
Card::Warning => container::Appearance {
|
||||
border_radius: 0.0,
|
||||
text_color: iced::Color::BLACK.into(),
|
||||
background: color::legacy::WARNING.into(),
|
||||
border_color: color::legacy::WARNING,
|
||||
..container::Appearance::default()
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Copy, Clone, Default)]
|
||||
pub enum Badge {
|
||||
#[default]
|
||||
Standard,
|
||||
Bitcoin,
|
||||
}
|
||||
|
||||
impl Badge {
|
||||
fn appearance(&self, _theme: &Theme) -> iced::widget::container::Appearance {
|
||||
match self {
|
||||
Self::Standard => container::Appearance {
|
||||
border_radius: 40.0,
|
||||
background: color::legacy::BACKGROUND.into(),
|
||||
..container::Appearance::default()
|
||||
},
|
||||
Self::Bitcoin => container::Appearance {
|
||||
border_radius: 40.0,
|
||||
background: color::legacy::WARNING.into(),
|
||||
text_color: iced::Color::WHITE.into(),
|
||||
..container::Appearance::default()
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Copy, Clone, Default)]
|
||||
pub enum Pill {
|
||||
#[default]
|
||||
Simple,
|
||||
InversePrimary,
|
||||
Primary,
|
||||
Success,
|
||||
}
|
||||
|
||||
impl Pill {
|
||||
fn appearance(&self, _theme: &Theme) -> iced::widget::container::Appearance {
|
||||
match self {
|
||||
Self::Primary => container::Appearance {
|
||||
background: color::legacy::PRIMARY.into(),
|
||||
border_radius: 10.0,
|
||||
text_color: iced::Color::WHITE.into(),
|
||||
..container::Appearance::default()
|
||||
},
|
||||
Self::InversePrimary => container::Appearance {
|
||||
background: color::legacy::FOREGROUND.into(),
|
||||
border_radius: 10.0,
|
||||
text_color: color::legacy::PRIMARY.into(),
|
||||
..container::Appearance::default()
|
||||
},
|
||||
Self::Success => container::Appearance {
|
||||
background: color::legacy::SUCCESS.into(),
|
||||
border_radius: 10.0,
|
||||
text_color: iced::Color::WHITE.into(),
|
||||
..container::Appearance::default()
|
||||
},
|
||||
Self::Simple => container::Appearance {
|
||||
background: color::legacy::BACKGROUND.into(),
|
||||
border_radius: 10.0,
|
||||
text_color: iced::Color::BLACK.into(),
|
||||
..container::Appearance::default()
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -151,6 +355,7 @@ pub enum Button {
|
||||
Secondary,
|
||||
Destructive,
|
||||
Transparent,
|
||||
TransparentBorder,
|
||||
}
|
||||
|
||||
impl button::StyleSheet for Theme {
|
||||
@ -183,7 +388,7 @@ impl button::StyleSheet for Theme {
|
||||
border_color: iced::Color::TRANSPARENT,
|
||||
text_color: color::LIGHT_GREY,
|
||||
},
|
||||
Button::Transparent => button::Appearance {
|
||||
Button::Transparent | Button::TransparentBorder => button::Appearance {
|
||||
shadow_offset: iced::Vector::default(),
|
||||
background: iced::Color::TRANSPARENT.into(),
|
||||
border_radius: 10.0,
|
||||
@ -217,7 +422,7 @@ impl button::StyleSheet for Theme {
|
||||
border_color: iced::Color::TRANSPARENT,
|
||||
text_color: color::LIGHT_BLACK,
|
||||
},
|
||||
Button::Transparent => button::Appearance {
|
||||
Button::Transparent | Button::TransparentBorder => button::Appearance {
|
||||
shadow_offset: iced::Vector::default(),
|
||||
background: iced::Color::TRANSPARENT.into(),
|
||||
border_radius: 10.0,
|
||||
@ -226,6 +431,40 @@ impl button::StyleSheet for Theme {
|
||||
text_color: color::LIGHT_GREY,
|
||||
},
|
||||
},
|
||||
Theme::Legacy => match style {
|
||||
Button::Primary => button::Appearance {
|
||||
shadow_offset: iced::Vector::default(),
|
||||
background: color::legacy::PRIMARY.into(),
|
||||
border_radius: 10.0,
|
||||
border_width: 0.0,
|
||||
border_color: iced::Color::TRANSPARENT,
|
||||
text_color: color::legacy::FOREGROUND,
|
||||
},
|
||||
Button::Destructive => button::Appearance {
|
||||
shadow_offset: iced::Vector::default(),
|
||||
background: color::legacy::FOREGROUND.into(),
|
||||
border_radius: 10.0,
|
||||
border_width: 0.0,
|
||||
border_color: color::legacy::ALERT,
|
||||
text_color: color::legacy::ALERT,
|
||||
},
|
||||
Button::Transparent | Button::TransparentBorder => button::Appearance {
|
||||
shadow_offset: iced::Vector::default(),
|
||||
background: iced::Color::TRANSPARENT.into(),
|
||||
border_radius: 10.0,
|
||||
border_width: 0.0,
|
||||
border_color: iced::Color::TRANSPARENT,
|
||||
text_color: iced::Color::BLACK,
|
||||
},
|
||||
Button::Secondary => button::Appearance {
|
||||
shadow_offset: iced::Vector::default(),
|
||||
background: iced::Color::TRANSPARENT.into(),
|
||||
border_radius: 10.0,
|
||||
border_width: 1.2,
|
||||
border_color: color::legacy::BORDER_GREY,
|
||||
text_color: iced::Color::BLACK,
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
@ -264,6 +503,14 @@ impl button::StyleSheet for Theme {
|
||||
border_color: iced::Color::TRANSPARENT,
|
||||
text_color: color::LIGHT_GREY,
|
||||
},
|
||||
Button::TransparentBorder => button::Appearance {
|
||||
shadow_offset: iced::Vector::default(),
|
||||
background: color::DARK_GREY.into(),
|
||||
border_radius: 10.0,
|
||||
border_width: 1.0,
|
||||
border_color: color::LIGHT_BLACK,
|
||||
text_color: color::LIGHT_GREY,
|
||||
},
|
||||
},
|
||||
Theme::Dark => match style {
|
||||
Button::Primary => button::Appearance {
|
||||
@ -298,7 +545,102 @@ impl button::StyleSheet for Theme {
|
||||
border_color: iced::Color::TRANSPARENT,
|
||||
text_color: color::LIGHT_GREY,
|
||||
},
|
||||
Button::TransparentBorder => button::Appearance {
|
||||
shadow_offset: iced::Vector::default(),
|
||||
background: color::DARK_GREY.into(),
|
||||
border_radius: 10.0,
|
||||
border_width: 1.0,
|
||||
border_color: color::LIGHT_GREY,
|
||||
text_color: color::LIGHT_GREY,
|
||||
},
|
||||
},
|
||||
Theme::Legacy => match style {
|
||||
Button::Primary => button::Appearance {
|
||||
shadow_offset: iced::Vector::default(),
|
||||
background: color::legacy::PRIMARY.into(),
|
||||
border_radius: 10.0,
|
||||
border_width: 0.0,
|
||||
border_color: iced::Color::TRANSPARENT,
|
||||
text_color: color::legacy::FOREGROUND,
|
||||
},
|
||||
Button::Destructive => button::Appearance {
|
||||
shadow_offset: iced::Vector::default(),
|
||||
background: color::legacy::FOREGROUND.into(),
|
||||
border_radius: 10.0,
|
||||
border_width: 0.0,
|
||||
border_color: color::legacy::ALERT,
|
||||
text_color: color::legacy::ALERT,
|
||||
},
|
||||
Button::Transparent => button::Appearance {
|
||||
shadow_offset: iced::Vector::default(),
|
||||
background: iced::Color::TRANSPARENT.into(),
|
||||
border_radius: 10.0,
|
||||
border_width: 0.0,
|
||||
border_color: iced::Color::TRANSPARENT,
|
||||
text_color: iced::Color::BLACK,
|
||||
},
|
||||
Button::TransparentBorder => button::Appearance {
|
||||
shadow_offset: iced::Vector::default(),
|
||||
background: iced::Color::TRANSPARENT.into(),
|
||||
border_radius: 10.0,
|
||||
border_width: 1.0,
|
||||
border_color: iced::Color::BLACK,
|
||||
text_color: iced::Color::BLACK,
|
||||
},
|
||||
Button::Secondary => button::Appearance {
|
||||
shadow_offset: iced::Vector::default(),
|
||||
background: iced::Color::TRANSPARENT.into(),
|
||||
border_radius: 10.0,
|
||||
border_width: 1.0,
|
||||
border_color: iced::Color::BLACK,
|
||||
text_color: iced::Color::BLACK,
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Copy, Clone, Default)]
|
||||
pub enum Form {
|
||||
#[default]
|
||||
Simple,
|
||||
Invalid,
|
||||
}
|
||||
|
||||
impl text_input::StyleSheet for Theme {
|
||||
type Style = Form;
|
||||
fn active(&self, style: &Self::Style) -> text_input::Appearance {
|
||||
match style {
|
||||
Form::Simple => text_input::Appearance {
|
||||
background: iced::Background::Color(color::legacy::FOREGROUND),
|
||||
border_radius: 5.0,
|
||||
border_width: 1.0,
|
||||
border_color: color::legacy::DARK_GREY,
|
||||
},
|
||||
Form::Invalid => text_input::Appearance {
|
||||
background: iced::Background::Color(color::legacy::FOREGROUND),
|
||||
border_radius: 5.0,
|
||||
border_width: 1.0,
|
||||
border_color: color::legacy::ALERT,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
fn focused(&self, style: &Self::Style) -> text_input::Appearance {
|
||||
text_input::Appearance {
|
||||
..self.active(style)
|
||||
}
|
||||
}
|
||||
|
||||
fn placeholder_color(&self, _style: &Self::Style) -> iced::Color {
|
||||
iced::Color::from_rgb(0.7, 0.7, 0.7)
|
||||
}
|
||||
|
||||
fn value_color(&self, _style: &Self::Style) -> iced::Color {
|
||||
iced::Color::from_rgb(0.3, 0.3, 0.3)
|
||||
}
|
||||
|
||||
fn selection_color(&self, _style: &Self::Style) -> iced::Color {
|
||||
iced::Color::from_rgb(0.8, 0.8, 1.0)
|
||||
}
|
||||
}
|
||||
|
||||
25
gui/ui/src/util.rs
Normal file
25
gui/ui/src/util.rs
Normal file
@ -0,0 +1,25 @@
|
||||
/// 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)
|
||||
}
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user