Skip to content

Repository files navigation

sweeten your daily iced brew

Crates.io Documentation License Made with iced

Overview

sweeten provides drop-in replacements for common iced widgets with extra features for more complex use cases. Each widget is designed to feel like the upstream version with extra capabilities layered on top.

Installation

If you're using the latest iced release:

cargo add sweeten

If you're tracking iced from git, add this to your Cargo.toml:

sweeten = { git = "https://github.com/airstrike/sweeten", branch = "master" }

Widgets

Button

Adds focus and blur methods:

  • .on_focus(Message) fires when the button gains keyboard focus
  • .on_blur(Message) fires when it loses focus

Pairs with sweeten::widget::operation::{focus_next, focus_previous} to build keyboard-navigable forms across any focusable widget.

Toggler

Animates state changes. The handle slides between positions and the fill color crossfades between the off and on styles.

toggler(self.is_on)
    .label("Enable notifications")
    .on_toggle(Message::Toggled)

Checkbox

Animated checkmark with fade and scale on toggle, and a crossfaded fill and border.

checkbox(self.is_checked)
    .label("Subscribe to updates")
    .on_toggle(Message::Toggled)

The press and release gesture matches native checkboxes: pressing inside arms the checkbox and releasing inside fires the toggle. Pressing outside and dragging in, or pressing inside and dragging out before release, cancels.

Five built-in styles ship: primary, secondary, success, danger, and text (a monochrome variant that uses the theme's body text color for the fill, pairing naturally with text-only buttons). Each variant uses <swatch>.base for the active state, <swatch>.strong for hovered, and fades toward the page background when disabled.

ProgressBar

Animates between values. Each render whose value differs from the currently-displayed value eases toward the new target over 150ms, so stepping the bar through discrete checkpoints reveals a smooth fill instead of jumps.

progress_bar(0.0..=100.0, self.progress)
    .girth(4.0)
    .on_idle(Message::LoadingSettled)

The optional .on_idle(f32) fires once the easing animation settles at its target, making it useful for gating follow-up actions (dismissing a splash, navigating, etc.) on the bar reaching a specific value.

MouseArea

Adds on_press_with for capturing the click position via a closure:

mouse_area("Click me and I'll tell you where!",)
    .on_press_with(|point| Message::ClickWithPoint(point)),

PickList

Supports titled groups of options, clearing the selection with a "None" entry, disabling items (inline or via a predicate), arbitrary widgets as option content, focus (Tab reaches it, Enter/Space/arrows open it), and keyboard interaction — arrow keys, Home/End, and typeahead move the highlighted option, Enter selects it, and Escape closes the menu:

use sweeten::pick_list;
use sweeten::widget::pick_list::{group, options};

pick_list(
    self.selected_language,
    options![
        None,
        group("Imperative", [Language::C, Language::Javascript]),
        group("Functional", [Language::Elm, Language::Haskell]),
    ],
    Language::to_string,
)
.on_select(Message::Pick)
.on_deselect(Message::Clear)
.separator(true)
.disabled(|language| matches!(language, Language::Javascript))
.placeholder("Choose a language...");

The view function may return a String or any Element, so options can carry icons or custom layouts.

TextInput

Adds focus-related features:

  • .on_focus and .on_blur methods for handling focus events
  • Sweetened focus_next and focus_previous focus management functions, which return the ID of the focused element

Row and Column

Adds drag-and-drop reordering of children via .on_drag:

use sweeten::widget::column;
use sweeten::widget::drag::DragEvent;

column(items.iter().map(|s| s.as_str().into()))
    .spacing(5)
    .on_drag(Message::Reorder)
    .into()

Table

Column headers are optional: pass None to table::column() for a headerless column, and when every column is headerless the header row is skipped entirely. Additional features on top of upstream:

  • .sticky_header(true) pins the header row when scrolling inside a parent scrollable
  • .header_underline_height(px) draws a distinct separator below the header (independent thickness and color from regular row separators)
  • .border(px) draws an outline around the entire table
use sweeten::widget::table::{self, table};

table(
    [
        table::column(Some(text("Name").into()), |r: Row| text(r.name).into()),
        table::column(None, |r: Row| text(r.age).into()),
    ],
    rows,
)
.sticky_header(true)
.header_underline_height(2)
.border(1)

List

A virtualized list that only materializes visible items, ported from iced's feat/list-widget-redux branch. Suitable for large or unbounded data sets where creating a widget per row would be too expensive.

use sweeten::widget::list;

let content = list::Content::from_iter(
    (0..10_000).map(|id| format!("Item {id}"))
);

list(&content, |_index, label| text(label).into())
    .spacing(5)

FitText

A text widget that auto-scales its font size to fit the bounds it is laid out into. Like CSS' clamp(min, ideal, max), but the "ideal" is solved for instead of specified: sweeten binary-searches the size range and picks the largest font that still fits.

use iced::Fill;
use sweeten::widget::fit_text;

fit_text("Big headline")
    .max_size(120)
    .min_size(16)
    .width(Fill)
    .height(Fill)
    .center()

Both min_size and max_size are optional; call neither and the font scales within [1.0, 1024.0] pixels by default.

Transition

A single-slot container that animates a slide whenever its child value changes. The new content slides into the canonical position from the edge opposite the configured Direction, while the previous content slides off the same-side edge.

use sweeten::widget::transition::{self, Direction};

transition::transition(self.phrase.clone(), |s: &String| {
    text(s.clone()).size(22).into()
})
.direction(Direction::Up)
.into()

Direction is sugar for the more general Mode knob (.mode(Mode::Slide(d))).

Examples

See examples/ for the full list, or run one directly:

cargo run --example mouse_area
cargo run --example pick_list
cargo run --example text_input
cargo run --example fit_text
cargo run --example list
cargo run --example transition
cargo run --example table
cargo run --example checkbox
cargo run --example progress_bar

Contributing

Contributions are welcome. Fork the repository, create a feature branch, implement your changes with tests, and submit a PR.

License

MIT

Acknowledgements

About

sweeten your daily iced brew

Topics

Resources

Stars

51 stars

Watchers

3 watching

Forks

Releases

Packages

Used by

Contributors

Languages