feat(segmented-button): icon support with state ergonomics
This commit is contained in:
parent
197d5a1c14
commit
dd3ff2e622
5 changed files with 244 additions and 66 deletions
|
|
@ -1,3 +1,6 @@
|
||||||
|
// Copyright 2022 System76 <info@system76.com>
|
||||||
|
// SPDX-License-Identifier: MPL-2.0
|
||||||
|
|
||||||
use super::state::State;
|
use super::state::State;
|
||||||
use super::style::StyleSheet;
|
use super::style::StyleSheet;
|
||||||
use super::widget::{SegmentedButton, SegmentedVariant};
|
use super::widget::{SegmentedButton, SegmentedVariant};
|
||||||
|
|
@ -18,7 +21,10 @@ pub fn horizontal_segmented_button<Message, Renderer, Data>(
|
||||||
state: &State<Data>,
|
state: &State<Data>,
|
||||||
) -> SegmentedButton<Horizontal, Message, Renderer>
|
) -> SegmentedButton<Horizontal, Message, Renderer>
|
||||||
where
|
where
|
||||||
Renderer: iced_native::Renderer + iced_native::text::Renderer,
|
Renderer: iced_native::Renderer
|
||||||
|
+ iced_native::text::Renderer
|
||||||
|
+ iced_native::image::Renderer
|
||||||
|
+ iced_native::svg::Renderer,
|
||||||
Renderer::Theme: StyleSheet,
|
Renderer::Theme: StyleSheet,
|
||||||
{
|
{
|
||||||
SegmentedButton::new(&state.inner)
|
SegmentedButton::new(&state.inner)
|
||||||
|
|
@ -26,7 +32,10 @@ where
|
||||||
|
|
||||||
impl<'a, Message, Renderer> SegmentedVariant for SegmentedButton<'a, Horizontal, Message, Renderer>
|
impl<'a, Message, Renderer> SegmentedVariant for SegmentedButton<'a, Horizontal, Message, Renderer>
|
||||||
where
|
where
|
||||||
Renderer: iced_native::Renderer + iced_native::text::Renderer,
|
Renderer: iced_native::Renderer
|
||||||
|
+ iced_native::text::Renderer
|
||||||
|
+ iced_native::image::Renderer
|
||||||
|
+ iced_native::svg::Renderer,
|
||||||
Renderer::Theme: StyleSheet,
|
Renderer::Theme: StyleSheet,
|
||||||
{
|
{
|
||||||
type Renderer = Renderer;
|
type Renderer = Renderer;
|
||||||
|
|
|
||||||
|
|
@ -35,7 +35,7 @@
|
||||||
//! Then use it in the view method to create segmented button widgets.
|
//! Then use it in the view method to create segmented button widgets.
|
||||||
//!
|
//!
|
||||||
//! ```ignore
|
//! ```ignore
|
||||||
//! let widget = segmentend_button(&application.state)
|
//! let widget = horizontal_segmentend_button(&application.state)
|
||||||
//! .style(theme::SegmentedButton::Selection)
|
//! .style(theme::SegmentedButton::Selection)
|
||||||
//! .height(Length::Units(32))
|
//! .height(Length::Units(32))
|
||||||
//! .on_activate(AppMessage::Selected);
|
//! .on_activate(AppMessage::Selected);
|
||||||
|
|
@ -51,7 +51,7 @@ mod vertical;
|
||||||
mod widget;
|
mod widget;
|
||||||
|
|
||||||
pub use self::horizontal::{horizontal_segmented_button, Horizontal, HorizontalSegmentedButton};
|
pub use self::horizontal::{horizontal_segmented_button, Horizontal, HorizontalSegmentedButton};
|
||||||
pub use self::state::{ButtonContent, Key, SecondaryState, SharedWidgetState, State};
|
pub use self::state::{Content, Key, SecondaryState, SharedWidgetState, State};
|
||||||
pub use self::style::{Appearance, ButtonAppearance, ButtonStatusAppearance, StyleSheet};
|
pub use self::style::{Appearance, ButtonAppearance, ButtonStatusAppearance, StyleSheet};
|
||||||
pub use self::vertical::{vertical_segmented_button, Vertical, VerticalSegmentedButton};
|
pub use self::vertical::{vertical_segmented_button, Vertical, VerticalSegmentedButton};
|
||||||
pub use self::widget::{SegmentedButton, SegmentedVariant};
|
pub use self::widget::{SegmentedButton, SegmentedVariant};
|
||||||
|
|
|
||||||
|
|
@ -1,9 +1,12 @@
|
||||||
// Copyright 2022 System76 <info@system76.com>
|
// Copyright 2022 System76 <info@system76.com>
|
||||||
// SPDX-License-Identifier: MPL-2.0
|
// SPDX-License-Identifier: MPL-2.0
|
||||||
|
|
||||||
|
use derive_setters::Setters;
|
||||||
use slotmap::{SecondaryMap, SlotMap};
|
use slotmap::{SecondaryMap, SlotMap};
|
||||||
use std::borrow::Cow;
|
use std::borrow::Cow;
|
||||||
|
|
||||||
|
use crate::widget::IconSource;
|
||||||
|
|
||||||
slotmap::new_key_type! {
|
slotmap::new_key_type! {
|
||||||
/// An ID for a segmented button
|
/// An ID for a segmented button
|
||||||
pub struct Key;
|
pub struct Key;
|
||||||
|
|
@ -31,7 +34,7 @@ impl<Data> Default for State<Data> {
|
||||||
#[derive(Default)]
|
#[derive(Default)]
|
||||||
pub struct SharedWidgetState {
|
pub struct SharedWidgetState {
|
||||||
/// The content used for drawing segmented buttons.
|
/// The content used for drawing segmented buttons.
|
||||||
pub buttons: SlotMap<Key, ButtonContent>,
|
pub buttons: SlotMap<Key, Content>,
|
||||||
|
|
||||||
/// The actively-selected segmented button.
|
/// The actively-selected segmented button.
|
||||||
pub active: Key,
|
pub active: Key,
|
||||||
|
|
@ -41,6 +44,16 @@ pub struct SharedWidgetState {
|
||||||
pub type SecondaryState<Data> = SecondaryMap<Key, Data>;
|
pub type SecondaryState<Data> = SecondaryMap<Key, Data>;
|
||||||
|
|
||||||
impl<Data> State<Data> {
|
impl<Data> State<Data> {
|
||||||
|
#[must_use]
|
||||||
|
pub fn builder() -> Builder<Data> {
|
||||||
|
Builder(Self::default())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Activates this button.
|
||||||
|
pub fn activate(&mut self, key: Key) {
|
||||||
|
self.inner.active = key;
|
||||||
|
}
|
||||||
|
|
||||||
/// The ID of the active button.
|
/// The ID of the active button.
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub fn active(&self) -> Key {
|
pub fn active(&self) -> Key {
|
||||||
|
|
@ -53,54 +66,126 @@ impl<Data> State<Data> {
|
||||||
self.data(self.active())
|
self.data(self.active())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Convenience method for batching multiple operations
|
||||||
|
#[must_use]
|
||||||
|
pub fn batch(&mut self) -> Batch<Data> {
|
||||||
|
Batch(self)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Enables or disables a button
|
||||||
|
#[must_use]
|
||||||
|
pub fn content(&self, key: Key) -> Option<&Content> {
|
||||||
|
self.inner.buttons.get(key)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Enables or disables a button
|
||||||
|
#[must_use]
|
||||||
|
pub fn content_mut(&mut self, key: Key) -> Option<&mut Content> {
|
||||||
|
self.inner.buttons.get_mut(key)
|
||||||
|
}
|
||||||
|
|
||||||
/// Get the application data for a button.
|
/// Get the application data for a button.
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub fn data(&self, key: Key) -> Option<&Data> {
|
pub fn data(&self, key: Key) -> Option<&Data> {
|
||||||
self.data.get(key)
|
self.data.get(key)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Get mutable application data for a button.
|
||||||
|
#[must_use]
|
||||||
|
pub fn data_mut(&mut self, key: Key) -> Option<&mut Data> {
|
||||||
|
self.data.get_mut(key)
|
||||||
|
}
|
||||||
|
|
||||||
/// Insert a new button.
|
/// Insert a new button.
|
||||||
pub fn insert(&mut self, content: impl Into<ButtonContent>, data: Data) -> Key {
|
pub fn insert(&mut self, content: impl Into<Content>, data: Data) -> Key {
|
||||||
let key = self.inner.buttons.insert(content.into());
|
let key = self.inner.buttons.insert(content.into());
|
||||||
self.data.insert(key, data);
|
self.data.insert(key, data);
|
||||||
key
|
key
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Inserts and activates a button.
|
||||||
|
pub fn insert_active(&mut self, content: impl Into<Content>, data: Data) -> Key {
|
||||||
|
let key = self.insert(content, data);
|
||||||
|
self.activate(key);
|
||||||
|
key
|
||||||
|
}
|
||||||
|
|
||||||
/// Removes a button.
|
/// Removes a button.
|
||||||
pub fn remove(&mut self, key: Key) -> Option<Data> {
|
pub fn remove(&mut self, key: Key) -> Option<Data> {
|
||||||
self.inner.buttons.remove(key);
|
self.inner.buttons.remove(key);
|
||||||
self.data.remove(key)
|
self.data.remove(key)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Activates this button.
|
|
||||||
pub fn activate(&mut self, key: Key) {
|
|
||||||
self.inner.active = key;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Data to be drawn in a segmented button.
|
/// Data to be drawn in a segmented button.
|
||||||
pub struct ButtonContent {
|
#[derive(Default, Setters)]
|
||||||
pub text: Cow<'static, str>,
|
pub struct Content {
|
||||||
|
#[setters(strip_option, into)]
|
||||||
|
/// The label to display in this button.
|
||||||
|
pub text: Option<Cow<'static, str>>,
|
||||||
|
|
||||||
|
#[setters(strip_option, into)]
|
||||||
|
/// An optionally-displayed icon beside the label.
|
||||||
|
pub icon: Option<IconSource<'static>>,
|
||||||
|
|
||||||
|
/// Whether the button is clickable or not.
|
||||||
|
pub enabled: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<String> for ButtonContent {
|
impl From<String> for Content {
|
||||||
fn from(text: String) -> Self {
|
fn from(text: String) -> Self {
|
||||||
ButtonContent {
|
Self::from(Cow::Owned(text))
|
||||||
text: Cow::Owned(text),
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<&'static str> for ButtonContent {
|
impl From<&'static str> for Content {
|
||||||
fn from(text: &'static str) -> Self {
|
fn from(text: &'static str) -> Self {
|
||||||
ButtonContent {
|
Self::from(Cow::Borrowed(text))
|
||||||
text: Cow::Borrowed(text),
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<Cow<'static, str>> for ButtonContent {
|
impl From<Cow<'static, str>> for Content {
|
||||||
fn from(text: Cow<'static, str>) -> Self {
|
fn from(text: Cow<'static, str>) -> Self {
|
||||||
ButtonContent { text }
|
Content::default().text(text)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct Builder<Data>(State<Data>);
|
||||||
|
|
||||||
|
impl<Data> Builder<Data> {
|
||||||
|
pub fn insert(mut self, content: impl Into<Content>, data: Data) -> Self {
|
||||||
|
self.0.insert(content, data);
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn insert_active(mut self, content: impl Into<Content>, data: Data) -> Self {
|
||||||
|
self.0.insert_active(content, data);
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn build(self) -> State<Data> {
|
||||||
|
self.0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Convenience type for batching multiple operations
|
||||||
|
pub struct Batch<'a, Data>(&'a mut State<Data>);
|
||||||
|
|
||||||
|
impl<'a, Data> Batch<'a, Data> {
|
||||||
|
/// Insert a new button.
|
||||||
|
pub fn insert(self, content: impl Into<Content>, data: Data) -> Self {
|
||||||
|
self.0.insert(content, data);
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Inserts and activates a button.
|
||||||
|
pub fn insert_active(self, content: impl Into<Content>, data: Data) -> Self {
|
||||||
|
self.0.insert_active(content, data);
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Removes a button.
|
||||||
|
pub fn remove(&mut self, key: Key) {
|
||||||
|
self.0.remove(key);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,6 @@
|
||||||
|
// Copyright 2022 System76 <info@system76.com>
|
||||||
|
// SPDX-License-Identifier: MPL-2.0
|
||||||
|
|
||||||
use super::state::State;
|
use super::state::State;
|
||||||
use super::style::StyleSheet;
|
use super::style::StyleSheet;
|
||||||
use super::widget::{SegmentedButton, SegmentedVariant};
|
use super::widget::{SegmentedButton, SegmentedVariant};
|
||||||
|
|
@ -18,7 +21,10 @@ pub fn vertical_segmented_button<Message, Renderer, Data>(
|
||||||
state: &State<Data>,
|
state: &State<Data>,
|
||||||
) -> SegmentedButton<Vertical, Message, Renderer>
|
) -> SegmentedButton<Vertical, Message, Renderer>
|
||||||
where
|
where
|
||||||
Renderer: iced_native::Renderer + iced_native::text::Renderer,
|
Renderer: iced_native::Renderer
|
||||||
|
+ iced_native::text::Renderer
|
||||||
|
+ iced_native::image::Renderer
|
||||||
|
+ iced_native::svg::Renderer,
|
||||||
Renderer::Theme: StyleSheet,
|
Renderer::Theme: StyleSheet,
|
||||||
{
|
{
|
||||||
SegmentedButton::new(&state.inner)
|
SegmentedButton::new(&state.inner)
|
||||||
|
|
@ -26,7 +32,10 @@ where
|
||||||
|
|
||||||
impl<'a, Message, Renderer> SegmentedVariant for SegmentedButton<'a, Vertical, Message, Renderer>
|
impl<'a, Message, Renderer> SegmentedVariant for SegmentedButton<'a, Vertical, Message, Renderer>
|
||||||
where
|
where
|
||||||
Renderer: iced_native::Renderer + iced_native::text::Renderer,
|
Renderer: iced_native::Renderer
|
||||||
|
+ iced_native::text::Renderer
|
||||||
|
+ iced_native::image::Renderer
|
||||||
|
+ iced_native::svg::Renderer,
|
||||||
Renderer::Theme: StyleSheet,
|
Renderer::Theme: StyleSheet,
|
||||||
{
|
{
|
||||||
type Renderer = Renderer;
|
type Renderer = Renderer;
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,6 @@
|
||||||
|
// Copyright 2022 System76 <info@system76.com>
|
||||||
|
// SPDX-License-Identifier: MPL-2.0
|
||||||
|
|
||||||
use std::marker::PhantomData;
|
use std::marker::PhantomData;
|
||||||
|
|
||||||
use super::state::{Key, SharedWidgetState};
|
use super::state::{Key, SharedWidgetState};
|
||||||
|
|
@ -34,26 +37,33 @@ pub trait SegmentedVariant {
|
||||||
#[derive(Setters)]
|
#[derive(Setters)]
|
||||||
pub struct SegmentedButton<'a, Variant, Message, Renderer>
|
pub struct SegmentedButton<'a, Variant, Message, Renderer>
|
||||||
where
|
where
|
||||||
Renderer: iced_native::Renderer + iced_native::text::Renderer,
|
Renderer: iced_native::Renderer
|
||||||
|
+ iced_native::text::Renderer
|
||||||
|
+ iced_native::image::Renderer
|
||||||
|
+ iced_native::svg::Renderer,
|
||||||
Renderer::Theme: StyleSheet,
|
Renderer::Theme: StyleSheet,
|
||||||
{
|
{
|
||||||
/// Contains application state also used for drawing.
|
/// Contains application state also used for drawing.
|
||||||
#[setters(skip)]
|
#[setters(skip)]
|
||||||
pub(super) state: &'a SharedWidgetState,
|
pub(super) state: &'a SharedWidgetState,
|
||||||
|
/// Padding around a button.
|
||||||
|
pub(super) button_padding: [u16; 4],
|
||||||
|
/// Desired height of a button.
|
||||||
|
pub(super) button_height: u16,
|
||||||
|
/// Spacing between icon and text in button.
|
||||||
|
pub(super) button_spacing: u16,
|
||||||
/// Desired font for active tabs.
|
/// Desired font for active tabs.
|
||||||
pub(super) font_active: Renderer::Font,
|
pub(super) font_active: Renderer::Font,
|
||||||
/// Desired font for hovered tabs.
|
/// Desired font for hovered tabs.
|
||||||
pub(super) font_hovered: Renderer::Font,
|
pub(super) font_hovered: Renderer::Font,
|
||||||
/// Desired font for inactive tabs.
|
/// Desired font for inactive tabs.
|
||||||
pub(super) font_inactive: Renderer::Font,
|
pub(super) font_inactive: Renderer::Font,
|
||||||
|
/// Size of icon
|
||||||
|
pub(super) icon_size: u16,
|
||||||
/// Desired width of the widget.
|
/// Desired width of the widget.
|
||||||
pub(super) width: Length,
|
pub(super) width: Length,
|
||||||
/// Desired height of the widget.
|
/// Desired height of the widget.
|
||||||
pub(super) height: Length,
|
pub(super) height: Length,
|
||||||
/// Padding around a button.
|
|
||||||
pub(super) button_padding: [u16; 4],
|
|
||||||
/// Desired height of a button.
|
|
||||||
pub(super) button_height: u16,
|
|
||||||
/// Desired spacing between buttons.
|
/// Desired spacing between buttons.
|
||||||
pub(super) spacing: u16,
|
pub(super) spacing: u16,
|
||||||
/// Style to draw the widget in.
|
/// Style to draw the widget in.
|
||||||
|
|
@ -69,7 +79,10 @@ where
|
||||||
|
|
||||||
impl<'a, Variant, Message, Renderer> SegmentedButton<'a, Variant, Message, Renderer>
|
impl<'a, Variant, Message, Renderer> SegmentedButton<'a, Variant, Message, Renderer>
|
||||||
where
|
where
|
||||||
Renderer: iced_native::Renderer + iced_native::text::Renderer,
|
Renderer: iced_native::Renderer
|
||||||
|
+ iced_native::text::Renderer
|
||||||
|
+ iced_native::image::Renderer
|
||||||
|
+ iced_native::svg::Renderer,
|
||||||
Renderer::Theme: StyleSheet,
|
Renderer::Theme: StyleSheet,
|
||||||
Self: SegmentedVariant<Renderer = Renderer>,
|
Self: SegmentedVariant<Renderer = Renderer>,
|
||||||
{
|
{
|
||||||
|
|
@ -77,13 +90,15 @@ where
|
||||||
pub fn new(state: &'a SharedWidgetState) -> Self {
|
pub fn new(state: &'a SharedWidgetState) -> Self {
|
||||||
Self {
|
Self {
|
||||||
state,
|
state,
|
||||||
|
button_padding: [4, 4, 4, 4],
|
||||||
|
button_height: 32,
|
||||||
|
button_spacing: 4,
|
||||||
font_active: Renderer::Font::default(),
|
font_active: Renderer::Font::default(),
|
||||||
font_hovered: Renderer::Font::default(),
|
font_hovered: Renderer::Font::default(),
|
||||||
font_inactive: Renderer::Font::default(),
|
font_inactive: Renderer::Font::default(),
|
||||||
|
icon_size: 24,
|
||||||
height: Length::Shrink,
|
height: Length::Shrink,
|
||||||
width: Length::Fill,
|
width: Length::Fill,
|
||||||
button_padding: [4, 4, 4, 4],
|
|
||||||
button_height: 32,
|
|
||||||
spacing: 0,
|
spacing: 0,
|
||||||
style: <Renderer::Theme as StyleSheet>::Style::default(),
|
style: <Renderer::Theme as StyleSheet>::Style::default(),
|
||||||
on_activate: None,
|
on_activate: None,
|
||||||
|
|
@ -98,20 +113,6 @@ where
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(super) fn measure_button(
|
|
||||||
&self,
|
|
||||||
renderer: &Renderer,
|
|
||||||
text: &str,
|
|
||||||
text_size: u16,
|
|
||||||
bounds: Size,
|
|
||||||
) -> (f32, f32) {
|
|
||||||
let (mut w, mut h) = renderer.measure(text, text_size, Default::default(), bounds);
|
|
||||||
w += f32::from(self.button_padding[0]) + f32::from(self.button_padding[2]);
|
|
||||||
h += f32::from(self.button_padding[1]) + f32::from(self.button_padding[3]);
|
|
||||||
h = h.max(f32::from(self.button_height));
|
|
||||||
(w, h)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(super) fn max_button_dimensions(
|
pub(super) fn max_button_dimensions(
|
||||||
&self,
|
&self,
|
||||||
renderer: &Renderer,
|
renderer: &Renderer,
|
||||||
|
|
@ -122,11 +123,32 @@ where
|
||||||
let mut height = 0.0f32;
|
let mut height = 0.0f32;
|
||||||
|
|
||||||
for (_, content) in self.state.buttons.iter() {
|
for (_, content) in self.state.buttons.iter() {
|
||||||
let (w, h) = self.measure_button(renderer, &content.text, text_size, bounds);
|
let mut button_width = 0.0f32;
|
||||||
height = height.max(h);
|
let mut button_height = 0.0f32;
|
||||||
width = width.max(w);
|
|
||||||
|
// Add text to measurement if text was given.
|
||||||
|
if let Some(text) = content.text.as_deref() {
|
||||||
|
let (w, h) = renderer.measure(text, text_size, Default::default(), bounds);
|
||||||
|
|
||||||
|
button_width = w;
|
||||||
|
button_height = h;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add icon to measurement if icon was given.
|
||||||
|
if content.icon.is_some() {
|
||||||
|
button_width += f32::from(self.icon_size) + f32::from(self.button_spacing);
|
||||||
|
button_height = f32::from(self.icon_size);
|
||||||
|
}
|
||||||
|
|
||||||
|
height = height.max(button_height);
|
||||||
|
width = width.max(button_width);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Add button padding to the max size found
|
||||||
|
width += f32::from(self.button_padding[0]) + f32::from(self.button_padding[2]);
|
||||||
|
height += f32::from(self.button_padding[1]) + f32::from(self.button_padding[3]);
|
||||||
|
height = height.max(f32::from(self.button_height));
|
||||||
|
|
||||||
(width, height)
|
(width, height)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -134,7 +156,10 @@ where
|
||||||
impl<'a, Variant, Message, Renderer> Widget<Message, Renderer>
|
impl<'a, Variant, Message, Renderer> Widget<Message, Renderer>
|
||||||
for SegmentedButton<'a, Variant, Message, Renderer>
|
for SegmentedButton<'a, Variant, Message, Renderer>
|
||||||
where
|
where
|
||||||
Renderer: iced_native::Renderer + iced_native::text::Renderer,
|
Renderer: iced_native::Renderer
|
||||||
|
+ iced_native::text::Renderer
|
||||||
|
+ iced_native::image::Renderer
|
||||||
|
+ iced_native::svg::Renderer,
|
||||||
Renderer::Theme: StyleSheet,
|
Renderer::Theme: StyleSheet,
|
||||||
Self: SegmentedVariant<Renderer = Renderer>,
|
Self: SegmentedVariant<Renderer = Renderer>,
|
||||||
Message: 'static + Clone,
|
Message: 'static + Clone,
|
||||||
|
|
@ -215,6 +240,7 @@ where
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[allow(clippy::too_many_lines)]
|
||||||
fn draw(
|
fn draw(
|
||||||
&self,
|
&self,
|
||||||
tree: &Tree,
|
tree: &Tree,
|
||||||
|
|
@ -245,7 +271,7 @@ where
|
||||||
|
|
||||||
// Draw each of the buttons in the widget.
|
// Draw each of the buttons in the widget.
|
||||||
for (nth, (key, content)) in self.state.buttons.iter().enumerate() {
|
for (nth, (key, content)) in self.state.buttons.iter().enumerate() {
|
||||||
let bounds = self.variant_button_bounds(bounds, nth);
|
let mut bounds = self.variant_button_bounds(bounds, nth);
|
||||||
|
|
||||||
let (status_appearance, font) = if self.state.active == key {
|
let (status_appearance, font) = if self.state.active == key {
|
||||||
(appearance.active, &self.font_active)
|
(appearance.active, &self.font_active)
|
||||||
|
|
@ -255,9 +281,6 @@ where
|
||||||
(appearance.inactive, &self.font_inactive)
|
(appearance.inactive, &self.font_inactive)
|
||||||
};
|
};
|
||||||
|
|
||||||
let x = bounds.center_x();
|
|
||||||
let y = bounds.center_y();
|
|
||||||
|
|
||||||
let button_appearance = if nth == 0 {
|
let button_appearance = if nth == 0 {
|
||||||
status_appearance.first
|
status_appearance.first
|
||||||
} else if nth + 1 == button_amount {
|
} else if nth + 1 == button_amount {
|
||||||
|
|
@ -298,16 +321,64 @@ where
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Draw the text in this button.
|
let y = bounds.center_y();
|
||||||
renderer.fill_text(iced_native::text::Text {
|
let text_size = renderer.default_size();
|
||||||
content: &content.text,
|
|
||||||
size: f32::from(renderer.default_size()),
|
// Draw the image beside the text.
|
||||||
bounds: Rectangle { x, y, ..bounds },
|
let horizontal_alignment = if let Some(icon) = &content.icon {
|
||||||
color: status_appearance.text_color,
|
bounds.x += f32::from(self.button_padding[0]);
|
||||||
font: font.clone(),
|
bounds.y += f32::from(self.button_padding[1]);
|
||||||
horizontal_alignment: alignment::Horizontal::Center,
|
bounds.width -=
|
||||||
vertical_alignment: alignment::Vertical::Center,
|
f32::from(self.button_padding[0]) - f32::from(self.button_padding[2]);
|
||||||
});
|
bounds.height -=
|
||||||
|
f32::from(self.button_padding[1]) - f32::from(self.button_padding[3]);
|
||||||
|
|
||||||
|
let width = f32::from(self.icon_size);
|
||||||
|
let offset = width + f32::from(self.button_spacing);
|
||||||
|
bounds.y = y - width / 2.0;
|
||||||
|
|
||||||
|
let icon_bounds = Rectangle {
|
||||||
|
width,
|
||||||
|
height: width,
|
||||||
|
..bounds
|
||||||
|
};
|
||||||
|
|
||||||
|
bounds.x += offset;
|
||||||
|
bounds.width -= offset;
|
||||||
|
match icon.load(self.icon_size, None, false) {
|
||||||
|
crate::widget::icon::Handle::Image(_handle) => {
|
||||||
|
unimplemented!()
|
||||||
|
}
|
||||||
|
crate::widget::icon::Handle::Svg(handle) => {
|
||||||
|
iced_native::svg::Renderer::draw(
|
||||||
|
renderer,
|
||||||
|
handle,
|
||||||
|
Some(status_appearance.text_color),
|
||||||
|
icon_bounds,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
alignment::Horizontal::Left
|
||||||
|
} else {
|
||||||
|
bounds.x = bounds.center_x();
|
||||||
|
alignment::Horizontal::Center
|
||||||
|
};
|
||||||
|
|
||||||
|
if let Some(text) = content.text.as_deref() {
|
||||||
|
bounds.y = y;
|
||||||
|
|
||||||
|
// Draw the text in this button.
|
||||||
|
renderer.fill_text(iced_native::text::Text {
|
||||||
|
content: text,
|
||||||
|
size: f32::from(text_size),
|
||||||
|
bounds,
|
||||||
|
color: status_appearance.text_color,
|
||||||
|
font: font.clone(),
|
||||||
|
horizontal_alignment,
|
||||||
|
vertical_alignment: alignment::Vertical::Center,
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -324,7 +395,11 @@ where
|
||||||
impl<'a, Variant, Message, Renderer> From<SegmentedButton<'a, Variant, Message, Renderer>>
|
impl<'a, Variant, Message, Renderer> From<SegmentedButton<'a, Variant, Message, Renderer>>
|
||||||
for Element<'a, Message, Renderer>
|
for Element<'a, Message, Renderer>
|
||||||
where
|
where
|
||||||
Renderer: iced_native::Renderer + iced_native::text::Renderer + 'a,
|
Renderer: iced_native::Renderer
|
||||||
|
+ iced_native::text::Renderer
|
||||||
|
+ iced_native::image::Renderer
|
||||||
|
+ iced_native::svg::Renderer
|
||||||
|
+ 'a,
|
||||||
Renderer::Theme: StyleSheet,
|
Renderer::Theme: StyleSheet,
|
||||||
SegmentedButton<'a, Variant, Message, Renderer>: SegmentedVariant<Renderer = Renderer>,
|
SegmentedButton<'a, Variant, Message, Renderer>: SegmentedVariant<Renderer = Renderer>,
|
||||||
Variant: 'static,
|
Variant: 'static,
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue