Add pane tabs
User can add and remove wallet with tabs
This commit is contained in:
parent
e771a42f45
commit
9f1de060eb
47
Cargo.lock
generated
47
Cargo.lock
generated
@ -2384,6 +2384,23 @@ dependencies = [
|
||||
"thiserror 1.0.69",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "iced_aw"
|
||||
version = "0.12.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "582c517a94ce3205da98e9c10b26bb71aa36b7d7d084441d826dc912711d1bac"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"chrono",
|
||||
"getrandom 0.3.1",
|
||||
"iced",
|
||||
"iced_fonts",
|
||||
"itertools 0.14.0",
|
||||
"num-format",
|
||||
"num-traits",
|
||||
"web-time",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "iced_core"
|
||||
version = "0.13.2"
|
||||
@ -2403,6 +2420,15 @@ dependencies = [
|
||||
"web-time",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "iced_fonts"
|
||||
version = "0.1.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "df7deb0800a850ee25c8a42559f72c0f249e577feb3aad37b9b65dc1e517e52a"
|
||||
dependencies = [
|
||||
"iced_core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "iced_futures"
|
||||
version = "0.13.2"
|
||||
@ -2781,6 +2807,15 @@ dependencies = [
|
||||
"either",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "itertools"
|
||||
version = "0.14.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2b192c782037fadd9cfa75548310488aabdbf3d2da73885b31bd0abd03351285"
|
||||
dependencies = [
|
||||
"either",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "itoa"
|
||||
version = "1.0.14"
|
||||
@ -3020,6 +3055,7 @@ dependencies = [
|
||||
"flate2",
|
||||
"hex",
|
||||
"iced",
|
||||
"iced_aw",
|
||||
"iced_runtime",
|
||||
"jsonrpc 0.12.1",
|
||||
"liana",
|
||||
@ -3047,6 +3083,7 @@ dependencies = [
|
||||
"bitcoin",
|
||||
"chrono",
|
||||
"iced",
|
||||
"iced_aw",
|
||||
"iced_core",
|
||||
"iced_runtime",
|
||||
"unicode-segmentation",
|
||||
@ -3512,6 +3549,16 @@ dependencies = [
|
||||
"num-traits",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "num-format"
|
||||
version = "0.4.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a652d9771a63711fd3c3deb670acfbe5c30a4072e664d7a3bf5a9e1056ac72c3"
|
||||
dependencies = [
|
||||
"arrayvec",
|
||||
"itoa",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "num-integer"
|
||||
version = "0.1.46"
|
||||
|
||||
@ -23,6 +23,7 @@ backtrace = "0.3"
|
||||
hex = "0.4.3"
|
||||
|
||||
iced = { version = "0.13.1", default-features = false, features = ["tokio", "svg", "qr_code", "image", "lazy", "wgpu", "advanced", "tiny-skia"] }
|
||||
iced_aw = { version = "0.12.2", features = ["context_menu"] }
|
||||
iced_runtime = "0.13.1"
|
||||
|
||||
# Used to verify RFC-compliance of an email
|
||||
|
||||
@ -182,13 +182,13 @@ impl App {
|
||||
)
|
||||
}
|
||||
|
||||
pub fn title(&self) -> String {
|
||||
pub fn title(&self) -> &str {
|
||||
if let Some(alias) = &self.wallet.alias {
|
||||
if !alias.is_empty() {
|
||||
return format!("- {}", alias);
|
||||
return alias;
|
||||
}
|
||||
}
|
||||
String::new()
|
||||
"Liana wallet"
|
||||
}
|
||||
|
||||
fn set_current_panel(&mut self, menu: Menu) -> Task<Message> {
|
||||
|
||||
@ -10,18 +10,18 @@ extern crate serde;
|
||||
extern crate serde_json;
|
||||
|
||||
use liana::miniscript::bitcoin;
|
||||
use liana_ui::widget::Element;
|
||||
use liana_ui::widget::{Column, Element};
|
||||
|
||||
pub mod pane;
|
||||
pub mod tab;
|
||||
|
||||
use crate::{dir::LianaDirectory, logger::Logger, VERSION};
|
||||
|
||||
pub struct GUI {
|
||||
state: tab::Tab,
|
||||
pane: pane::Pane,
|
||||
config: Config,
|
||||
// We may change the directory of log outputs later
|
||||
_logger: Logger,
|
||||
// if set up, it overrides the level filter of the logger.
|
||||
_log_level: Option<LevelFilter>,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
@ -33,7 +33,7 @@ pub enum Key {
|
||||
pub enum Message {
|
||||
CtrlC,
|
||||
FontLoaded(Result<(), iced::font::Error>),
|
||||
Tab(tab::Message),
|
||||
Pane(pane::Message),
|
||||
KeyPressed(Key),
|
||||
Event(iced::Event),
|
||||
}
|
||||
@ -54,11 +54,7 @@ async fn ctrl_c() -> Result<(), ()> {
|
||||
|
||||
impl GUI {
|
||||
pub fn title(&self) -> String {
|
||||
match &self.state.0 {
|
||||
tab::State::Installer(_) => format!("Liana v{} Installer", VERSION),
|
||||
tab::State::App(a) => format!("Liana v{} {}", VERSION, a.title()),
|
||||
_ => format!("Liana v{}", VERSION),
|
||||
}
|
||||
format!("Liana v{}", VERSION)
|
||||
}
|
||||
|
||||
pub fn new((config, log_level): (Config, Option<LevelFilter>)) -> (GUI, Task<Message>) {
|
||||
@ -68,13 +64,13 @@ impl GUI {
|
||||
log_level.unwrap_or_else(|| log_level.unwrap_or(LevelFilter::INFO)),
|
||||
);
|
||||
let mut cmds = vec![Task::perform(ctrl_c(), |_| Message::CtrlC)];
|
||||
let (state, cmd) = tab::Tab::new(config.liana_directory, config.network);
|
||||
cmds.push(cmd.map(Message::Tab));
|
||||
let (pane, cmd) = pane::Pane::new(&config);
|
||||
cmds.push(cmd.map(Message::Pane));
|
||||
(
|
||||
Self {
|
||||
state,
|
||||
pane,
|
||||
config,
|
||||
_logger: logger,
|
||||
_log_level: log_level,
|
||||
},
|
||||
Task::batch(cmds),
|
||||
)
|
||||
@ -84,7 +80,7 @@ impl GUI {
|
||||
match message {
|
||||
Message::CtrlC
|
||||
| Message::Event(iced::Event::Window(iced::window::Event::CloseRequested)) => {
|
||||
self.state.stop();
|
||||
self.pane.stop();
|
||||
iced::window::get_latest().and_then(iced::window::close)
|
||||
}
|
||||
Message::KeyPressed(Key::Tab(shift)) => {
|
||||
@ -95,14 +91,14 @@ impl GUI {
|
||||
focus_next()
|
||||
}
|
||||
}
|
||||
Message::Tab(msg) => self.state.update(msg).map(Message::Tab),
|
||||
Message::Pane(msg) => self.pane.update(msg, &self.config).map(Message::Pane),
|
||||
_ => Task::none(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn subscription(&self) -> Subscription<Message> {
|
||||
Subscription::batch(vec![
|
||||
self.state.subscription().map(Message::Tab),
|
||||
self.pane.subscription().map(Message::Pane),
|
||||
iced::event::listen_with(|event, status, _| match (&event, status) {
|
||||
(
|
||||
Event::Keyboard(keyboard::Event::KeyPressed {
|
||||
@ -122,7 +118,10 @@ impl GUI {
|
||||
}
|
||||
|
||||
pub fn view(&self) -> Element<Message> {
|
||||
self.state.view().map(Message::Tab)
|
||||
Column::new()
|
||||
.push(self.pane.tabs_menu_view().map(Message::Pane))
|
||||
.push(self.pane.view().map(Message::Pane))
|
||||
.into()
|
||||
}
|
||||
|
||||
pub fn scale_factor(&self) -> f64 {
|
||||
|
||||
152
liana-gui/src/gui/pane.rs
Normal file
152
liana-gui/src/gui/pane.rs
Normal file
@ -0,0 +1,152 @@
|
||||
use iced::{Subscription, Task};
|
||||
use iced_aw::ContextMenu;
|
||||
use liana_ui::{component::text::*, icon::plus_icon, theme, widget::*};
|
||||
|
||||
use crate::gui::Config;
|
||||
|
||||
use super::tab;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum Message {
|
||||
Tab(usize, tab::Message),
|
||||
View(ViewMessage),
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub enum ViewMessage {
|
||||
FocusTab(usize),
|
||||
CloseTab(usize),
|
||||
AddTab,
|
||||
}
|
||||
|
||||
pub struct Pane {
|
||||
tabs: Vec<tab::Tab>,
|
||||
|
||||
// this is an index in the tabs array
|
||||
focused_tab: usize,
|
||||
|
||||
// used to generate tabs ids.
|
||||
tabs_created: usize,
|
||||
}
|
||||
|
||||
impl Pane {
|
||||
pub fn new(cfg: &Config) -> (Self, Task<Message>) {
|
||||
let (state, task) = tab::State::new(cfg.liana_directory.clone(), cfg.network);
|
||||
(
|
||||
Self {
|
||||
tabs: vec![tab::Tab::new(1, state)],
|
||||
focused_tab: 0,
|
||||
tabs_created: 1,
|
||||
},
|
||||
task.map(|msg| Message::Tab(1, msg)),
|
||||
)
|
||||
}
|
||||
|
||||
fn add_tab(&mut self, cfg: &Config) -> Task<Message> {
|
||||
let (state, task) = tab::State::new(cfg.liana_directory.clone(), cfg.network);
|
||||
self.tabs_created += 1;
|
||||
let id = self.tabs_created;
|
||||
self.tabs.push(tab::Tab::new(id, state));
|
||||
self.focused_tab = self.tabs.len() - 1;
|
||||
task.map(move |msg| Message::Tab(id, msg))
|
||||
}
|
||||
|
||||
fn remove_tab(&mut self, i: usize) {
|
||||
let mut tab = self.tabs.remove(i);
|
||||
tab.stop();
|
||||
self.focused_tab = if self.tabs.is_empty() {
|
||||
0
|
||||
} else if i < self.tabs.len() - 1 {
|
||||
i
|
||||
} else {
|
||||
self.tabs.len() - 1
|
||||
};
|
||||
}
|
||||
|
||||
pub fn update(&mut self, message: Message, cfg: &Config) -> Task<Message> {
|
||||
match message {
|
||||
Message::Tab(id, msg) => self
|
||||
.tabs
|
||||
.iter_mut()
|
||||
.find(|t| t.id == id)
|
||||
.map(|t| t.update(msg).map(move |msg| Message::Tab(id, msg)))
|
||||
.unwrap_or(Task::none()),
|
||||
Message::View(ViewMessage::FocusTab(i)) => {
|
||||
if i < self.tabs.len() {
|
||||
self.focused_tab = i;
|
||||
}
|
||||
Task::none()
|
||||
}
|
||||
Message::View(ViewMessage::AddTab) => self.add_tab(cfg),
|
||||
Message::View(ViewMessage::CloseTab(i)) => {
|
||||
self.remove_tab(i);
|
||||
Task::none()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn subscription(&self) -> Subscription<Message> {
|
||||
let subs: Vec<Subscription<Message>> = self
|
||||
.tabs
|
||||
.iter()
|
||||
.map(|t| {
|
||||
t.subscription()
|
||||
.with(t.id)
|
||||
.map(|(id, msg)| Message::Tab(id, msg))
|
||||
})
|
||||
.collect();
|
||||
Subscription::batch(subs)
|
||||
}
|
||||
|
||||
pub fn stop(&mut self) {
|
||||
self.tabs.iter_mut().for_each(|t| t.stop());
|
||||
}
|
||||
|
||||
pub fn tabs_menu_view(&self) -> Element<Message> {
|
||||
let mut menu = Row::new().spacing(3);
|
||||
for (i, tab) in self.tabs.iter().enumerate() {
|
||||
let title = tab.title();
|
||||
menu = menu.push(ContextMenu::new(
|
||||
Into::<Element<ViewMessage>>::into(
|
||||
Button::new(if title.len() < 20 {
|
||||
Row::new().push(p1_regular(title)).push(p1_regular(
|
||||
&" ".to_string()[..21 - title.len()],
|
||||
))
|
||||
} else {
|
||||
Row::new()
|
||||
.push(p1_regular(&title[..17]))
|
||||
.push(p1_regular("..."))
|
||||
})
|
||||
.style(if i == self.focused_tab {
|
||||
theme::button::tab_active
|
||||
} else {
|
||||
theme::button::tab
|
||||
})
|
||||
.on_press(ViewMessage::FocusTab(i)),
|
||||
),
|
||||
move || {
|
||||
Button::new(p1_regular("Close"))
|
||||
.style(theme::button::secondary)
|
||||
.on_press(ViewMessage::CloseTab(i))
|
||||
.width(100)
|
||||
.into()
|
||||
},
|
||||
));
|
||||
}
|
||||
menu = menu.push(
|
||||
Button::new(plus_icon())
|
||||
.style(theme::button::tab)
|
||||
.on_press(ViewMessage::AddTab),
|
||||
);
|
||||
Into::<Element<ViewMessage>>::into(menu.wrap()).map(Message::View)
|
||||
}
|
||||
|
||||
pub fn view(&self) -> Element<Message> {
|
||||
if let Some(t) = self.tabs.get(self.focused_tab) {
|
||||
let id = t.id;
|
||||
t.view().map(move |msg| Message::Tab(id, msg))
|
||||
} else {
|
||||
Row::new().into()
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -29,8 +29,6 @@ use crate::{
|
||||
},
|
||||
};
|
||||
|
||||
pub struct Tab(pub State);
|
||||
|
||||
pub enum State {
|
||||
Launcher(Box<Launcher>),
|
||||
Installer(Box<Installer>),
|
||||
@ -39,6 +37,19 @@ pub enum State {
|
||||
App(App),
|
||||
}
|
||||
|
||||
impl State {
|
||||
pub fn new(
|
||||
directory: LianaDirectory,
|
||||
network: Option<bitcoin::Network>,
|
||||
) -> (Self, Task<Message>) {
|
||||
let (launcher, command) = Launcher::new(directory, network);
|
||||
(
|
||||
State::Launcher(Box::new(launcher)),
|
||||
command.map(|msg| Message::Launch(Box::new(msg))),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum Message {
|
||||
Launch(Box<launcher::Message>),
|
||||
@ -48,20 +59,28 @@ pub enum Message {
|
||||
Login(Box<login::Message>),
|
||||
}
|
||||
|
||||
pub struct Tab {
|
||||
pub id: usize,
|
||||
pub state: State,
|
||||
}
|
||||
|
||||
impl Tab {
|
||||
pub fn new(
|
||||
directory: LianaDirectory,
|
||||
network: Option<bitcoin::Network>,
|
||||
) -> (Self, Task<Message>) {
|
||||
let (launcher, command) = Launcher::new(directory, network);
|
||||
(
|
||||
Tab(State::Launcher(Box::new(launcher))),
|
||||
command.map(|msg| Message::Launch(Box::new(msg))),
|
||||
)
|
||||
pub fn new(id: usize, state: State) -> Self {
|
||||
Tab { id, state }
|
||||
}
|
||||
|
||||
pub fn title(&self) -> &str {
|
||||
match &self.state {
|
||||
State::Installer(_) => "Installer",
|
||||
State::Loader(_) => "Loading...",
|
||||
State::Launcher(_) => "Launcher",
|
||||
State::Login(_) => "Login",
|
||||
State::App(a) => a.title(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn update(&mut self, message: Message) -> Task<Message> {
|
||||
match (&mut self.0, message) {
|
||||
match (&mut self.state, message) {
|
||||
(State::Launcher(l), Message::Launch(msg)) => match *msg {
|
||||
launcher::Message::Install(datadir, network, init) => {
|
||||
if !datadir.exists() {
|
||||
@ -77,19 +96,19 @@ impl Tab {
|
||||
}
|
||||
}
|
||||
let (install, command) = Installer::new(datadir, network, None, init);
|
||||
self.0 = State::Installer(Box::new(install));
|
||||
self.state = State::Installer(Box::new(install));
|
||||
command.map(|msg| Message::Install(Box::new(msg)))
|
||||
}
|
||||
launcher::Message::Run(datadir_path, cfg, network, settings) => {
|
||||
if settings.remote_backend_auth.is_some() {
|
||||
let (login, command) =
|
||||
login::LianaLiteLogin::new(datadir_path, network, settings);
|
||||
self.0 = State::Login(Box::new(login));
|
||||
self.state = State::Login(Box::new(login));
|
||||
command.map(|msg| Message::Login(Box::new(msg)))
|
||||
} else {
|
||||
let (loader, command) =
|
||||
Loader::new(datadir_path, cfg, network, None, None, settings);
|
||||
self.0 = State::Loader(Box::new(loader));
|
||||
self.state = State::Loader(Box::new(loader));
|
||||
command.map(|msg| Message::Load(Box::new(msg)))
|
||||
}
|
||||
}
|
||||
@ -98,7 +117,7 @@ impl Tab {
|
||||
(State::Login(l), Message::Login(msg)) => match *msg {
|
||||
login::Message::View(login::ViewMessage::BackToLauncher(network)) => {
|
||||
let (launcher, command) = Launcher::new(l.datadir.clone(), Some(network));
|
||||
self.0 = State::Launcher(Box::new(launcher));
|
||||
self.state = State::Launcher(Box::new(launcher));
|
||||
command.map(|msg| Message::Launch(Box::new(msg)))
|
||||
}
|
||||
login::Message::Install(remote_backend) => {
|
||||
@ -108,7 +127,7 @@ impl Tab {
|
||||
remote_backend,
|
||||
installer::UserFlow::CreateWallet,
|
||||
);
|
||||
self.0 = State::Installer(Box::new(install));
|
||||
self.state = State::Installer(Box::new(install));
|
||||
command.map(|msg| Message::Install(Box::new(msg)))
|
||||
}
|
||||
login::Message::Run(Ok((backend_client, wallet, coins))) => {
|
||||
@ -129,7 +148,7 @@ impl Tab {
|
||||
config,
|
||||
);
|
||||
|
||||
self.0 = State::App(app);
|
||||
self.state = State::App(app);
|
||||
command.map(|msg| Message::Run(Box::new(msg)))
|
||||
}
|
||||
_ => l.update(*msg).map(|msg| Message::Login(Box::new(msg))),
|
||||
@ -139,7 +158,7 @@ impl Tab {
|
||||
if settings.remote_backend_auth.is_some() {
|
||||
let (login, command) =
|
||||
login::LianaLiteLogin::new(i.datadir.clone(), i.network, *settings);
|
||||
self.0 = State::Login(Box::new(login));
|
||||
self.state = State::Login(Box::new(login));
|
||||
command.map(|msg| Message::Login(Box::new(msg)))
|
||||
} else {
|
||||
let cfg = app::Config::from_file(
|
||||
@ -158,12 +177,12 @@ impl Tab {
|
||||
i.context.backup.take(),
|
||||
*settings,
|
||||
);
|
||||
self.0 = State::Loader(Box::new(loader));
|
||||
self.state = State::Loader(Box::new(loader));
|
||||
command.map(|msg| Message::Load(Box::new(msg)))
|
||||
}
|
||||
} else if let installer::Message::BackToLauncher(network) = *msg {
|
||||
let (launcher, command) = Launcher::new(i.destination_path(), Some(network));
|
||||
self.0 = State::Launcher(Box::new(launcher));
|
||||
self.state = State::Launcher(Box::new(launcher));
|
||||
command.map(|msg| Message::Launch(Box::new(msg)))
|
||||
} else {
|
||||
i.update(*msg).map(|msg| Message::Install(Box::new(msg)))
|
||||
@ -173,7 +192,7 @@ impl Tab {
|
||||
loader::Message::View(loader::ViewMessage::SwitchNetwork) => {
|
||||
let (launcher, command) =
|
||||
Launcher::new(loader.datadir_path.clone(), Some(loader.network));
|
||||
self.0 = State::Launcher(Box::new(launcher));
|
||||
self.state = State::Launcher(Box::new(launcher));
|
||||
command.map(|msg| Message::Launch(Box::new(msg)))
|
||||
}
|
||||
loader::Message::Synced(Ok((wallet, cache, daemon, bitcoind, backup))) => {
|
||||
@ -204,7 +223,7 @@ impl Tab {
|
||||
bitcoind,
|
||||
false,
|
||||
);
|
||||
self.0 = State::App(app);
|
||||
self.state = State::App(app);
|
||||
command.map(|msg| Message::Run(Box::new(msg)))
|
||||
}
|
||||
}
|
||||
@ -221,7 +240,7 @@ impl Tab {
|
||||
bitcoind,
|
||||
restored_from_backup,
|
||||
);
|
||||
self.0 = State::App(app);
|
||||
self.state = State::App(app);
|
||||
command.map(|msg| Message::Run(Box::new(msg)))
|
||||
}
|
||||
loader::Message::App(Err(e), _) => {
|
||||
@ -239,7 +258,7 @@ impl Tab {
|
||||
}
|
||||
|
||||
pub fn subscription(&self) -> Subscription<Message> {
|
||||
Subscription::batch(vec![match &self.0 {
|
||||
Subscription::batch(vec![match &self.state {
|
||||
State::Installer(v) => v.subscription().map(|msg| Message::Install(Box::new(msg))),
|
||||
State::Loader(v) => v.subscription().map(|msg| Message::Load(Box::new(msg))),
|
||||
State::App(v) => v.subscription().map(|msg| Message::Run(Box::new(msg))),
|
||||
@ -249,7 +268,7 @@ impl Tab {
|
||||
}
|
||||
|
||||
pub fn view(&self) -> Element<Message> {
|
||||
match &self.0 {
|
||||
match &self.state {
|
||||
State::Installer(v) => v.view().map(|msg| Message::Install(Box::new(msg))),
|
||||
State::App(v) => v.view().map(|msg| Message::Run(Box::new(msg))),
|
||||
State::Launcher(v) => v.view().map(|msg| Message::Launch(Box::new(msg))),
|
||||
@ -259,7 +278,7 @@ impl Tab {
|
||||
}
|
||||
|
||||
pub fn stop(&mut self) {
|
||||
match &mut self.0 {
|
||||
match &mut self.state {
|
||||
State::Loader(s) => s.stop(),
|
||||
State::Launcher(s) => s.stop(),
|
||||
State::Installer(s) => s.stop(),
|
||||
|
||||
@ -7,6 +7,7 @@ edition = "2021"
|
||||
|
||||
[dependencies]
|
||||
iced = { version = "0.13.1", default-features = false, features = ["svg", "image", "lazy", "qr_code", "advanced"] }
|
||||
iced_aw = { version = "0.12.2", features = ["context_menu"] }
|
||||
iced_core = { version = "0.13.2" }
|
||||
iced_runtime = { version = "0.13.2" }
|
||||
unicode-segmentation = "1.0"
|
||||
|
||||
@ -145,3 +145,14 @@ fn button(p: &Button, status: Status) -> Style {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn tab(theme: &Theme, status: Status) -> Style {
|
||||
let mut style = button(&theme.colors.buttons.secondary, status);
|
||||
style.border.radius = 0.0.into();
|
||||
style.border.width = 0.0;
|
||||
style
|
||||
}
|
||||
|
||||
pub fn tab_active(theme: &Theme, _status: Status) -> Style {
|
||||
tab(theme, Status::Pressed)
|
||||
}
|
||||
|
||||
22
liana-ui/src/theme/context_menu.rs
Normal file
22
liana-ui/src/theme/context_menu.rs
Normal file
@ -0,0 +1,22 @@
|
||||
use iced::Background;
|
||||
use iced_aw::widget::context_menu::{Catalog, Status, Style};
|
||||
|
||||
use super::Theme;
|
||||
|
||||
impl Catalog for Theme {
|
||||
type Class<'a> = Box<dyn Fn(&Theme, Status) -> Style + 'a>;
|
||||
|
||||
fn default<'a>() -> Self::Class<'a> {
|
||||
Box::new(primary)
|
||||
}
|
||||
|
||||
fn style(&self, class: &Self::Class<'_>, status: Status) -> Style {
|
||||
class(self, status)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn primary(_theme: &Theme, _status: Status) -> Style {
|
||||
Style {
|
||||
background: Background::Color(iced::Color::TRANSPARENT),
|
||||
}
|
||||
}
|
||||
@ -4,6 +4,7 @@ pub mod button;
|
||||
pub mod card;
|
||||
pub mod checkbox;
|
||||
pub mod container;
|
||||
pub mod context_menu;
|
||||
pub mod notification;
|
||||
pub mod overlay;
|
||||
pub mod palette;
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user