improv(context_drawer): add optional header and footer element
This commit is contained in:
parent
2909d37b58
commit
3dcc47d6a7
4 changed files with 101 additions and 47 deletions
|
|
@ -71,13 +71,6 @@ use {
|
||||||
zbus::{interface, proxy, zvariant::Value},
|
zbus::{interface, proxy, zvariant::Value},
|
||||||
};
|
};
|
||||||
|
|
||||||
#[cfg(feature = "desktop")]
|
|
||||||
use {
|
|
||||||
crate::widget,
|
|
||||||
iced::{alignment::Vertical, Alignment},
|
|
||||||
std::collections::BTreeMap,
|
|
||||||
};
|
|
||||||
|
|
||||||
pub(crate) fn iced_settings<App: Application>(
|
pub(crate) fn iced_settings<App: Application>(
|
||||||
settings: Settings,
|
settings: Settings,
|
||||||
flags: App::Flags,
|
flags: App::Flags,
|
||||||
|
|
@ -470,6 +463,16 @@ where
|
||||||
Vec::new()
|
Vec::new()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Non-scrolling elements placed below the context drawer title row
|
||||||
|
fn context_drawer_header(&self) -> Option<Element<Message<Self::Message>>> {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Elements placed below the context drawer scrollable
|
||||||
|
fn context_drawer_footer(&self) -> Option<Element<Message<Self::Message>>> {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
|
||||||
/// Displays a dialog in the center of the application window when `Some`.
|
/// Displays a dialog in the center of the application window when `Some`.
|
||||||
fn dialog(&self) -> Option<Element<Self::Message>> {
|
fn dialog(&self) -> Option<Element<Self::Message>> {
|
||||||
None
|
None
|
||||||
|
|
@ -752,6 +755,8 @@ impl<App: Application> ApplicationExt for App {
|
||||||
context_drawer(
|
context_drawer(
|
||||||
&core.window.context_title,
|
&core.window.context_title,
|
||||||
self.context_header_actions(),
|
self.context_header_actions(),
|
||||||
|
self.context_drawer_header(),
|
||||||
|
self.context_drawer_footer(),
|
||||||
Message::Cosmic(cosmic::Message::ContextDrawer(false)),
|
Message::Cosmic(cosmic::Message::ContextDrawer(false)),
|
||||||
main_content,
|
main_content,
|
||||||
context.map(Message::App),
|
context.map(Message::App),
|
||||||
|
|
@ -786,6 +791,8 @@ impl<App: Application> ApplicationExt for App {
|
||||||
crate::widget::ContextDrawer::new_inner(
|
crate::widget::ContextDrawer::new_inner(
|
||||||
&core.window.context_title,
|
&core.window.context_title,
|
||||||
self.context_header_actions(),
|
self.context_header_actions(),
|
||||||
|
self.context_drawer_header(),
|
||||||
|
self.context_drawer_footer(),
|
||||||
context.map(Message::App),
|
context.map(Message::App),
|
||||||
Message::Cosmic(cosmic::Message::ContextDrawer(false)),
|
Message::Cosmic(cosmic::Message::ContextDrawer(false)),
|
||||||
context_width,
|
context_width,
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
#[cfg(feature = "desktop")]
|
#[cfg(feature = "desktop")]
|
||||||
use {
|
use {
|
||||||
crate::{
|
crate::{
|
||||||
iced::{alignment::Vertical, Alignment, Length},
|
iced::{Alignment, Length},
|
||||||
widget::{self, horizontal_space},
|
widget::{self, horizontal_space},
|
||||||
Element,
|
Element,
|
||||||
},
|
},
|
||||||
|
|
@ -124,7 +124,11 @@ pub fn about<'a, Message: Clone + 'static>(
|
||||||
about: &'a About,
|
about: &'a About,
|
||||||
on_url_press: impl Fn(String) -> Message,
|
on_url_press: impl Fn(String) -> Message,
|
||||||
) -> Element<'a, Message> {
|
) -> Element<'a, Message> {
|
||||||
let spacing = crate::theme::active().cosmic().spacing;
|
let cosmic_theme::Spacing {
|
||||||
|
space_xxs,
|
||||||
|
space_xs,
|
||||||
|
..
|
||||||
|
} = crate::theme::active().cosmic().spacing;
|
||||||
|
|
||||||
let section = |list: &'a Vec<(String, String)>, title: &'a str| {
|
let section = |list: &'a Vec<(String, String)>, title: &'a str| {
|
||||||
(!list.is_empty()).then_some({
|
(!list.is_empty()).then_some({
|
||||||
|
|
@ -138,8 +142,8 @@ pub fn about<'a, Message: Clone + 'static>(
|
||||||
.push_maybe((!url.is_empty()).then_some(
|
.push_maybe((!url.is_empty()).then_some(
|
||||||
crate::widget::icon::from_name("link-symbolic").icon(),
|
crate::widget::icon::from_name("link-symbolic").icon(),
|
||||||
))
|
))
|
||||||
.padding(spacing.space_xxs)
|
.padding(space_xxs)
|
||||||
.align_y(Vertical::Center),
|
.align_y(Alignment::Center),
|
||||||
)
|
)
|
||||||
.class(crate::theme::Button::Text)
|
.class(crate::theme::Button::Text)
|
||||||
.on_press(on_url_press(url.clone()))
|
.on_press(on_url_press(url.clone()))
|
||||||
|
|
@ -176,8 +180,8 @@ pub fn about<'a, Message: Clone + 'static>(
|
||||||
url.is_some()
|
url.is_some()
|
||||||
.then_some(crate::widget::icon::from_name("link-symbolic").icon()),
|
.then_some(crate::widget::icon::from_name("link-symbolic").icon()),
|
||||||
)
|
)
|
||||||
.padding(spacing.space_xxs)
|
.padding(space_xxs)
|
||||||
.align_y(Vertical::Center),
|
.align_y(Alignment::Center),
|
||||||
)
|
)
|
||||||
.class(crate::theme::Button::Text)
|
.class(crate::theme::Button::Text)
|
||||||
.on_press(on_url_press(url.unwrap_or(String::new())))
|
.on_press(on_url_press(url.unwrap_or(String::new())))
|
||||||
|
|
@ -187,25 +191,22 @@ pub fn about<'a, Message: Clone + 'static>(
|
||||||
let copyright = about.copyright.as_ref().map(widget::text::body);
|
let copyright = about.copyright.as_ref().map(widget::text::body);
|
||||||
let comments = about.comments.as_ref().map(widget::text::body);
|
let comments = about.comments.as_ref().map(widget::text::body);
|
||||||
|
|
||||||
widget::scrollable(
|
widget::column()
|
||||||
widget::column()
|
.push_maybe(application_icon)
|
||||||
.push_maybe(application_icon)
|
.push_maybe(application_name)
|
||||||
.push_maybe(application_name)
|
.push_maybe(author)
|
||||||
.push_maybe(author)
|
.push_maybe(version)
|
||||||
.push_maybe(version)
|
.push_maybe(license)
|
||||||
.push_maybe(license)
|
.push_maybe(links_section)
|
||||||
.push_maybe(links_section)
|
.push_maybe(developers_section)
|
||||||
.push_maybe(developers_section)
|
.push_maybe(designers_section)
|
||||||
.push_maybe(designers_section)
|
.push_maybe(artists_section)
|
||||||
.push_maybe(artists_section)
|
.push_maybe(translators_section)
|
||||||
.push_maybe(translators_section)
|
.push_maybe(documenters_section)
|
||||||
.push_maybe(documenters_section)
|
.push_maybe(comments)
|
||||||
.push_maybe(comments)
|
.push_maybe(copyright)
|
||||||
.push_maybe(copyright)
|
.align_x(Alignment::Center)
|
||||||
.align_x(Alignment::Center)
|
.spacing(space_xs)
|
||||||
.spacing(spacing.space_xs)
|
.width(Length::Fill)
|
||||||
.width(Length::Fill),
|
.into()
|
||||||
)
|
|
||||||
.spacing(spacing.space_xxxs)
|
|
||||||
.into()
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -13,7 +13,9 @@ use crate::Element;
|
||||||
/// An overlayed widget that attaches a toggleable context drawer to the view.
|
/// An overlayed widget that attaches a toggleable context drawer to the view.
|
||||||
pub fn context_drawer<'a, Message: Clone + 'static, Content, Drawer>(
|
pub fn context_drawer<'a, Message: Clone + 'static, Content, Drawer>(
|
||||||
title: &'a str,
|
title: &'a str,
|
||||||
actions: Vec<Element<'a, Message>>,
|
header_actions: Vec<Element<'a, Message>>,
|
||||||
|
header_opt: Option<Element<'a, Message>>,
|
||||||
|
footer_opt: Option<Element<'a, Message>>,
|
||||||
on_close: Message,
|
on_close: Message,
|
||||||
content: Content,
|
content: Content,
|
||||||
drawer: Drawer,
|
drawer: Drawer,
|
||||||
|
|
@ -23,5 +25,14 @@ where
|
||||||
Content: Into<Element<'a, Message>>,
|
Content: Into<Element<'a, Message>>,
|
||||||
Drawer: Into<Element<'a, Message>>,
|
Drawer: Into<Element<'a, Message>>,
|
||||||
{
|
{
|
||||||
ContextDrawer::new(title, actions, content, drawer, on_close, max_width)
|
ContextDrawer::new(
|
||||||
|
title,
|
||||||
|
header_actions,
|
||||||
|
header_opt,
|
||||||
|
footer_opt,
|
||||||
|
content,
|
||||||
|
drawer,
|
||||||
|
on_close,
|
||||||
|
max_width,
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
// Copyright 2023 System76 <info@system76.com>
|
// Copyright 2023 System76 <info@system76.com>
|
||||||
// SPDX-License-Identifier: MPL-2.0
|
// SPDX-License-Identifier: MPL-2.0
|
||||||
|
|
||||||
use crate::widget::{button, column, container, icon, row, text, LayerContainer};
|
use crate::widget::{button, column, container, icon, row, scrollable, text, LayerContainer};
|
||||||
use crate::{Apply, Element, Renderer, Theme};
|
use crate::{Apply, Element, Renderer, Theme};
|
||||||
|
|
||||||
use super::overlay::Overlay;
|
use super::overlay::Overlay;
|
||||||
|
|
@ -25,7 +25,9 @@ pub struct ContextDrawer<'a, Message> {
|
||||||
impl<'a, Message: Clone + 'static> ContextDrawer<'a, Message> {
|
impl<'a, Message: Clone + 'static> ContextDrawer<'a, Message> {
|
||||||
pub fn new_inner<Drawer>(
|
pub fn new_inner<Drawer>(
|
||||||
title: &'a str,
|
title: &'a str,
|
||||||
actions: Vec<Element<'a, Message>>,
|
header_actions: Vec<Element<'a, Message>>,
|
||||||
|
header_opt: Option<Element<'a, Message>>,
|
||||||
|
footer_opt: Option<Element<'a, Message>>,
|
||||||
drawer: Drawer,
|
drawer: Drawer,
|
||||||
on_close: Message,
|
on_close: Message,
|
||||||
max_width: f32,
|
max_width: f32,
|
||||||
|
|
@ -35,6 +37,8 @@ impl<'a, Message: Clone + 'static> ContextDrawer<'a, Message> {
|
||||||
{
|
{
|
||||||
let cosmic_theme::Spacing {
|
let cosmic_theme::Spacing {
|
||||||
space_xxs,
|
space_xxs,
|
||||||
|
space_xs,
|
||||||
|
space_s,
|
||||||
space_m,
|
space_m,
|
||||||
space_l,
|
space_l,
|
||||||
..
|
..
|
||||||
|
|
@ -46,12 +50,13 @@ impl<'a, Message: Clone + 'static> ContextDrawer<'a, Message> {
|
||||||
Some(text::heading(title).width(Length::FillPortion(1)).center())
|
Some(text::heading(title).width(Length::FillPortion(1)).center())
|
||||||
};
|
};
|
||||||
|
|
||||||
let header = row::with_capacity(3)
|
let horizontal_padding = if max_width < 392.0 { space_s } else { space_l };
|
||||||
|
|
||||||
|
let header_row = row::with_capacity(3)
|
||||||
.width(Length::Fixed(480.0))
|
.width(Length::Fixed(480.0))
|
||||||
.align_y(Alignment::Center)
|
.align_y(Alignment::Center)
|
||||||
.padding([space_m, space_l])
|
|
||||||
.push(
|
.push(
|
||||||
row::with_children(actions)
|
row::with_children(header_actions)
|
||||||
.spacing(space_xxs)
|
.spacing(space_xxs)
|
||||||
.width(Length::FillPortion(1)),
|
.width(Length::FillPortion(1)),
|
||||||
)
|
)
|
||||||
|
|
@ -64,12 +69,32 @@ impl<'a, Message: Clone + 'static> ContextDrawer<'a, Message> {
|
||||||
.width(Length::FillPortion(1))
|
.width(Length::FillPortion(1))
|
||||||
.align_x(Alignment::End),
|
.align_x(Alignment::End),
|
||||||
);
|
);
|
||||||
|
let header = column::with_capacity(2)
|
||||||
let pane = column::with_capacity(2).push(header).push(
|
.width(Length::Fixed(480.0))
|
||||||
container(drawer.into())
|
.align_x(Alignment::Center)
|
||||||
|
.spacing(space_m)
|
||||||
|
.padding([space_m, horizontal_padding])
|
||||||
|
.push(header_row)
|
||||||
|
.push_maybe(header_opt);
|
||||||
|
let footer = footer_opt.map(|element| {
|
||||||
|
container(element)
|
||||||
|
.width(Length::Fixed(480.0))
|
||||||
|
.align_y(Alignment::Center)
|
||||||
|
.padding([space_xs, horizontal_padding])
|
||||||
|
});
|
||||||
|
let pane = column::with_capacity(3)
|
||||||
|
.push(header)
|
||||||
|
.push(
|
||||||
|
scrollable(container(drawer.into()).padding([
|
||||||
|
0,
|
||||||
|
horizontal_padding,
|
||||||
|
if footer.is_some() { 0 } else { space_l },
|
||||||
|
horizontal_padding,
|
||||||
|
]))
|
||||||
.height(Length::Fill)
|
.height(Length::Fill)
|
||||||
.width(Length::Shrink),
|
.width(Length::Shrink),
|
||||||
);
|
)
|
||||||
|
.push_maybe(footer);
|
||||||
|
|
||||||
// XXX new limits do not exactly handle the max width well for containers
|
// XXX new limits do not exactly handle the max width well for containers
|
||||||
// XXX this is a hack to get around that
|
// XXX this is a hack to get around that
|
||||||
|
|
@ -90,7 +115,9 @@ impl<'a, Message: Clone + 'static> ContextDrawer<'a, Message> {
|
||||||
/// Creates an empty [`ContextDrawer`].
|
/// Creates an empty [`ContextDrawer`].
|
||||||
pub fn new<Content, Drawer>(
|
pub fn new<Content, Drawer>(
|
||||||
title: &'a str,
|
title: &'a str,
|
||||||
actions: Vec<Element<'a, Message>>,
|
header_actions: Vec<Element<'a, Message>>,
|
||||||
|
header_opt: Option<Element<'a, Message>>,
|
||||||
|
footer_opt: Option<Element<'a, Message>>,
|
||||||
content: Content,
|
content: Content,
|
||||||
drawer: Drawer,
|
drawer: Drawer,
|
||||||
on_close: Message,
|
on_close: Message,
|
||||||
|
|
@ -100,7 +127,15 @@ impl<'a, Message: Clone + 'static> ContextDrawer<'a, Message> {
|
||||||
Content: Into<Element<'a, Message>>,
|
Content: Into<Element<'a, Message>>,
|
||||||
Drawer: Into<Element<'a, Message>>,
|
Drawer: Into<Element<'a, Message>>,
|
||||||
{
|
{
|
||||||
let drawer = Self::new_inner(title, actions, drawer, on_close, max_width);
|
let drawer = Self::new_inner(
|
||||||
|
title,
|
||||||
|
header_actions,
|
||||||
|
header_opt,
|
||||||
|
footer_opt,
|
||||||
|
drawer,
|
||||||
|
on_close,
|
||||||
|
max_width,
|
||||||
|
);
|
||||||
|
|
||||||
ContextDrawer {
|
ContextDrawer {
|
||||||
id: None,
|
id: None,
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue