A data-first Rust-native UI design toolkit.

Related tags

GUI druid
Overview

Druid

A data-first Rust-native UI toolkit.

crates.io docs.rs license chat

Druid is an experimental Rust-native UI toolkit. Its main goal is to offer a polished user experience. There are many factors to this goal, including performance, a rich palette of interactions (hence a widget library to support them), and playing well with the native platform. See the goals section for more details.

Druid's current development is largely driven by its use in Runebender, a new font editor.

We have been doing periodic releases of Druid on crates.io, but it is under active development and its API might change. All changes are documented in the changelog.

For an overview of some key concepts, see the (work in progress) Druid book.

Contributions

A very good place to ask questions and discuss development work is our Zulip chat instance, in the #druid-help and #druid channels, respectively.

We gladly accept contributions via GitHub pull requests. Please see CONTRIBUTING.md for more details.

Example

Here's a simple counter example app.

use druid::widget::{Button, Flex, Label};
use druid::{AppLauncher, LocalizedString, PlatformError, Widget, WidgetExt, WindowDesc};

fn main() -> Result<(), PlatformError> {
    let main_window = WindowDesc::new(ui_builder());
    let data = 0_u32;
    AppLauncher::with_window(main_window)
        .use_env_tracing()
        .launch(data)
}

fn ui_builder() -> impl Widget<u32> {
    // The label text will be computed dynamically based on the current locale and count
    let text =
        LocalizedString::new("hello-counter").with_arg("count", |data: &u32, _env| (*data).into());
    let label = Label::new(text).padding(5.0).center();
    let button = Button::new("increment")
        .on_click(|_ctx, data, _env| *data += 1)
        .padding(5.0);

    Flex::column().with_child(label).with_child(button)
}

Check out the the examples folder for a more comprehensive demonstration of Druid's existing functionality and widgets.

Screenshots

calc.rs example flex.rs example custom_widget.rs example

Using Druid

An explicit goal of Druid is to be easy to build, so please open an issue if you run into any difficulties. Druid is available on crates.io and should work as a lone dependency (it re-exports all the parts of druid-shell, piet, and kurbo that you'll need):

druid = "0.7.0"

Since Druid is currently in fast-evolving state, you might prefer to drink from the firehose:

druid = { git = "https://github.com/linebender/druid.git" }

Platform notes

Linux

On Linux, Druid requires gtk+3; see GTK installation page. (On ubuntu-based distro, running sudo apt-get install libgtk-3-dev from the terminal will do the job.)

Alternatively, there is an X11 backend available, although it is currently missing quite a few features. You can try it out with --features=x11.

Goals

Druid's goal is to make it easy to write and deploy high quality desktop applications with a smooth and polished user experience on all common platforms. In order to achieve this we strive for a variety of things:

  • Make it easy to build and package on all supported platforms.
  • Implement abstractions to avoid platform specific quirks.
  • Respect platform conventions and expectations.
  • Handle display resolution and scaling reliably with little effort.
  • Enable easy, yet powerful internationalization.
  • Offer robust accessibility support.
  • Produce small and fast binaries with low memory usage.
  • Have a small dependency tree, a high quality code base and good organization.
  • Focus on powerful, desktop-grade applications.
  • Provide a flexible set of layouts and common widgets.
  • Ease creation of custom components and application logic as needed.

Non-Goals

In order to fulfill those goals, we cannot support every use case. Luckily the Rust community is working on a variety of different libraries with different goals, so here are some of Druid's non-goals and possible alternatives that can offer those capabilities:

  • Use the the platform-native widgets or mimic them. (Relm)
  • Embed easily into custom render pipelines. (Conrod)
  • Adhere to a specific architectural style such as Elm. (Iced, Relm)
  • Support rendering to HTML when targeting the web. (Iced, Moxie)

Druid is just one of many ongoing Rust-native GUI experiments. If it doesn't suit your use case, perhaps one of the others will!

Concepts

druid-shell

The Druid toolkit uses druid-shell for a platform-abstracting application shell. druid-shell is responsible for starting a native platform runloop, listening to events, converting them into a platform-agnostic representation, and calling a user-provided handler with them.

While druid-shell is being developed with the Druid toolkit in mind, it is intended to be general enough that it could be reused by other projects interested in experimenting with Rust GUI. The druid-shell crate includes a couple of non-druid examples.

piet

Druid relies on the Piet library for drawing and text layout. Piet is a 2D graphics abstraction with multiple backends: piet-direct2d, piet-coregraphics, piet-cairo, piet-web, and piet-svg are currently available, and a GPU backend is planned. In terms of Druid platform support via Piet, macOS uses piet-coregraphics, Linux uses piet-cairo, Windows uses piet-direct2d, and web uses piet-web.

use druid::kurbo::{BezPath, Point, Rect};
use druid::piet::Color;

// Create an arbitrary bezier path
// (ctx.size() returns the size of the layout rect we're painting in)
let mut path = BezPath::new();
path.move_to(Point::ORIGIN);
path.quad_to(
    (80.0, 90.0),
    (ctx.size().width, ctx.size().height),
);
// Create a color
let stroke_color = Color::rgb8(0x00, 0x80, 0x00);
// Stroke the path with thickness 1.0
ctx.stroke(path, &stroke_color, 1.0);

// Rectangles: the path for practical people
let rect = Rect::from_origin_size((10., 10.), (100., 100.));
// Note the Color:rgba8 which includes an alpha channel (7F in this case)
let fill_color = Color::rgba8(0x00, 0x00, 0x00, 0x7F);
ctx.fill(rect, &fill_color);

widgets

Widgets in Druid (text boxes, buttons, layout components, etc.) are objects which implement the Widget trait. The trait is parametrized by a type (T) for associated data. All trait methods (event, lifecycle, update, paint, and layout) are provided with access to this data, and in the case of event the reference is mutable, so that events can directly update the data.

Whenever the application data changes, the framework traverses the widget hierarchy with an update method.

All the widget trait methods are provided with a corresponding context (EventCtx, LifeCycleCtx, UpdateCtx, LayoutCtx, PaintCtx). The widget can request things and cause actions by calling methods on that context.

In addition, all trait methods are provided with an environment Env, which includes the current theme parameters (colors, dimensions, etc.).

impl<T: Data> Widget<T> for Button<T> {
    fn event(&mut self, ctx: &mut EventCtx, event: &Event, data: &mut T, env: &Env) {
      ...
    }

    fn lifecycle(&mut self, ctx: &mut LifeCycleCtx, event: &LifeCycle, data: &T, env: &Env) {
      ...
    }

    fn update(&mut self, ctx: &mut UpdateCtx, old_data: &T, data: &T, env: &Env) {
      ...
    }

    fn layout(&mut self, ctx: &mut LayoutCtx, bc: &BoxConstraints, data: &T, env: &Env) -> Size {
      ...
    }

    fn paint(&mut self, ctx: &mut PaintCtx, data: &T, env: &Env) {
      ...
    }
}

Druid provides a number of basic utility and layout widgets and it's easy to implement your own. You can also compose widgets into new widgets:

fn build_widget() -> impl Widget<u32> {
    let mut col = Flex::column();
    for i in 0..30 {
        let button = Button::new(format!("Button {}", i).padding(5.0);
        col.add_child(button);
    }
    Scroll::new(col)
}

layout

Druid's layout protocol is strongly inspired by Flutter's box layout model. In Druid, widgets are passed a BoxConstraint that provides them a minimum and maximum size for layout. Widgets are also responsible for computing appropriate constraints for their children if applicable.

data

Druid uses a Data trait to represent value types. These should be cheap to compare and cheap to clone.

In general, you can use derive to generate a Data impl for your types.

#[derive(Clone, Data)]
struct AppState {
    which: bool,
    value: f64,
}

lens

The Lens datatype gives access to a part of a larger data structure. Like Data, this can be derived. Derived lenses are accessed as associated constants with the same name as the field.

#[derive(Clone, Data, Lens)]
struct AppState {
    which: bool,
    value: f64,
}

To use the lens, wrap your widget with LensWrap (note the conversion of CamelCase to snake_case):

LensWrap::new(WidgetThatExpectsf64::new(), AppState::value);

Alternatively, lenses for structs, tuples, and indexable containers can be constructed on-demand with the lens macro:

LensWrap::new(WidgetThatExpectsf64::new(), lens!(AppState, value));

This is particularly useful when working with types defined in another crate.

Authors

The main authors are Raph Levien and Colin Rofls, with much support from an active and friendly community.

Comments
  • Screen Module, window titlebar, position, size, DPI

    Screen Module, window titlebar, position, size, DPI

    Changes:

    All changes are only implemented on Windows (but have skeletons for other platforms)

    • Added Screen module, where you can get infomation about the screen and monitors.

    • Added setting window size, position, maximized and minimized state.

    • Added option to disable the default titlebar.

    • Added option to let windows handle a custom titlebar like the default one (move, double click to maximize, etc (based on win-settings)).

    • Changed window creation to use window's default window size if the user does not specify a window size.

    • Implemented a solution for re-entrancy on Windows. #1068

    • Improved DPI on windows (10), should now work correctly across monitors with different dpi settings

    This should close the Window's side of #1044, #778, #634, #427. And #626

    Some use-cases I could think of for this:

    • Restore window size/position on next launch, as they where when it was closed.
    • Create custom look for titlebar.
    • Layout multiple windows (I think GIMP does/used to do this: picture).
    • Create window(s) on a particular monitor.
    • Change size to fit content, or make content fit window size (This might require additional API).
    • Create window at a spesific point (For example an app that shows information about files when users hovers over them or pop up windows, like confirm dialogs/error messages etc)
    shell/win S-needs-review 
    opened by rhzk 27
  • Wayland

    Wayland

    A wayland backend for druid-shell.

    Very much not ready for inclusion (might never be), but I'm opening the PR early so others can experiment if they like.

    Currently some of the size calculations are broken, and there's no use input handling at all. But wayland is fun to hack on (trust me) so feel free to have a play. :smile:

    Update

    You should be able to run the empty_window example without it crashing! It draws a gradient so we can check that the memory is all aligned nicely. I'm just beginning to work on input, currently incorporating xkb so I can translate scancodes to the nice things people expect. But its 11pm here so maybe tomorrow. I've pushed the current state.

    No region invalidation yet so the invalidate example might trigger epilepsy.

    Update2

    I've got to start work next week, so I've done all I can for now. You can run druid examples directly on wayland and it works! (sort of). Definitely not ready for the prime time, but I'm going to try to get the branch ready for merging, so work can continue in-tree, when I (or someone else) next gets chance.

    Update3

    I've done some more work. I've refactored out the buffer management code, in an attempt to make things more modular. I've also moved to individual fields having interior mutability rather than the whole struct. A lot of fields can be Cell rather than RefCell, potentially saving a few nanoseconds. :) I've also moved to holding weak references most places, which matches how other backends work. Most druid examples will run now, although many will exhibit incorrect behavior.

    enhancement discussion shell D-Hard 
    opened by derekdreery 26
  • Window white flash on init

    Window white flash on init

    Is it possible to prevent the white flash that happens before the GUI is initially drawn? Maybe just being able to choose black rather then a white flash is enough. Seeing it in the examples on Windows.

    shell/win 
    opened by Adarma 24
  • wayland: rebased and merge into master

    wayland: rebased and merge into master

    pull all the changes from wayland into master since we're down to just a few missing pieces in wayland (mostly pieces also missing within the x11 backend) time to let it soak in master.

    opened by james-lawrence 23
  • Scroll behavior component

    Scroll behavior component

    This is the work I outlined in https://github.com/linebender/druid/issues/73#issuecomment-658316701

    Put simply this allows any widget to very easily support scrolling on its own while picking and choosing what behaviour to support yet remaining consistent with other scrolling widgets.

    • The original Scroll widget has been renamed to AbsoluteScroll to indicate that it provides bi-directional scrolling for a child which has an absolute size. It can no longer restrict scrolling to a specific axis.
    • List now manages its own scrolling instead of having to be wrapped in a Scroll. It has gained the ability to change the layout axis to horizontal with the .horizontal() builder fn. The layout code stretches children to full size on the non-primary axis like the Flutter ListView as this solves some issues specifically with inset scrollbars which will come after this patch.
    • The now public methods and such on the scroll_component have been documented, including making some things like scrollbar point hit tests more well defined

    There are more things I want to do (specifically inset scrollbars) and a few existing bugs I noticed I want to fix but this patch should be functionally identical to existing behaviour to avoid issues with reviewing and scope.

    As an example here is the list example after changing the .fix_height(50.0) to .fix_width(200.0) and adding .horizontal() to the second List construction image

    widget architecture S-needs-review 
    opened by ForLoveOfCats 23
  • Segmentation fault (core dumped) when using MenuDesc widget and svg feature

    Segmentation fault (core dumped) when using MenuDesc widget and svg feature

    I got Segmentation fault (core dumped) crash when I enabled svg feature and attaching menu to WindowDesc.

    • Using svg feature while NOT attaching a menu to window run just fine.
    • Attaching menu to window while NOT using svg will run just fine.

    Replicated in

    • 63e34fa2 ( latest master, when this issue is filed)
    • 93620d1

    To quickly replicate: Add svg to the default feature in druid's Cargo.toml

    [features]
    default = ["gtk","svg"]
    

    then run the example which uses menu:

    cargo run --example multiwin
    
    DEBUG [druid::localization] available locales [], current en-US
    DEBUG [druid::localization] resolved: [en-US]
    INFO  [multiwin] Window added, id: WindowId(1)
    Segmentation fault (core dumped)
    

    OS used: Ubuntu 18.04.5 LTS x86_64 Kernel: 4.15.0-118-generic

    I wasn't able to replicate this in the published 0.6.0 crate.

    shell/gtk crash 
    opened by ivanceras 19
  • Problems with TextBox::fix_width()

    Problems with TextBox::fix_width()

    Whenever I change TextBoxs width, the textbox resizes, but debug_paint_layout shows it's CollisionBox still being original size. image I can click into CollisionBox, but I cannot click outside of it. The layouts work correctly though.

    EDIT: Flex still takes the original size and not the "fixed" one.

    bug widget 
    opened by MGlolenstine 19
  • Do we need `WindowHandle::set_level`?

    Do we need `WindowHandle::set_level`?

    Is it ever necessary to change the window level after it's already created? For windows that aren't yet created, we have WindowBuilder::set_level. There was some concern in #1785 whether WindowHandle::set_level will work on x11, but I don't know about any other platforms...

    discussion 
    opened by jneem 18
  • Implemented scaling for widgets

    Implemented scaling for widgets

    This adds scaling support to widgets

    One can control the scaling a widget recieves with the Scale widget "Scale::new(value, child)". The scale is then propagated down the tree unless overwritten again by a new Scale widget.

    I have updated Button, CheckBox, Label, Padding, ProgressBar, RadioGroup, Scroll, SizedBox, Slider, Spinner, Stepper, Switch and TextBox to support scaling.

    I did not update the image drawing widgets as I am unsure if they should be scaled, and in that case it needs to be done on the gpu-side?

    I did think about a few different ways to implement this but I wanted to avoid any breaking change and I wanted the widgets to have control over how they scale, but if anyone thinks of a better way feel free to suggest it.

    There is also a example showing dynamic and static scaling on most widgets.

    opened by rhzk 18
  • Add debug option to display widget ids

    Add debug option to display widget ids

    This will paint the id of each widget of some subtree in the bottom right of that widget's bounds; useful for debugging various bits of event flow and widget behaviour.

    Screen Shot 2020-04-24 at 1 32 24 PM

    opened by cmyr 18
  • Designing a unified multi-click API

    Designing a unified multi-click API

    While working on #843 I also started thinking about double-clicks and how to best do them. I think that the behavior should match across platforms so that druid users can just read the druid docs and not worry about platform specifics.

    Double-clicks are good, triple-clicks possibly worth it, but what about quadruple-clicks or even more? I'm not sure there's really much value in going beyond three. It's definitely more work.

    • Windows natively supports only up to double, so will need a custom implementation anyway.
    • GTK seems to support triple clicks, so we can probably piggyback on that for up to triple.
    • macOS supports up to triple on most buttons, but seems to be practically unbounded for left and right buttons. We can piggyback on the platform for up to triple, including unbounded_count % 3.
    • Web has some support via UIEvent.detail but I haven't tested how well it works.

    Custom implementations (which Windows will need anyway) will need to be careful to follow the user configured OS setting for double click timeout.

    Thus I propose the following unified API:

    • Support for up to triple-clicks.
    • Both MouseDown and MouseUp contain the click count.
    • No extra double or triple click events, it's merely an extra event property for those who care.
    discussion shell 
    opened by xStrom 18
  • Drag & Drop interfaces in Druid

    Drag & Drop interfaces in Druid

    Hi, I'm trying to find a good framework for creating a drag and drop interface to build neural network models entirely within a GUI. Is it possible to do this in Druid? I'll need to be able to click and drag a "Module" (a widget I'd implement myself), create connections between modules, and zoom in and out.

    opened by RylanYancey 0
  • Druid Image Loading Example does not work.

    Druid Image Loading Example does not work.

    image

    I'm getting this error on this lines of code in Image.rs example:

    fn build_widget(state: &AppState) -> Box<dyn Widget<AppState>> {
        let png_data = ImageBuf::from_data(include_bytes!("./assets/PicWithAlpha.png")).unwrap(); // <- error here
    

    It seems Piet no longer supports creating images in this manner? I'm using the latest Github version.

    opened by RylanYancey 0
  • pop-up window without a parent use config position

    pop-up window without a parent use config position

    My app has only one pop-up window, but it always shows in top left corner of the screen.

    The reason is that the parent window is not considered empty and the calculation of the relative position of the child window fails.

    WindowDesc::new(app_ui())
        .window_size(size)
        .set_level(WindowLevel::Tooltip(WindowHandle::default()))
    
    opened by yim7 2
  • Scroll bug when using ZStack

    Scroll bug when using ZStack

    When using a ZStack inside a Scroll you can't scroll when the ZStack is hot.

    Example:

    use druid::{Color, Widget, RenderContext, WindowDesc, AppLauncher, WidgetExt, UnitPoint};
    use druid::widget::{CrossAxisAlignment, Flex, Label, Painter, Scroll, SizedBox, ZStack};
    
    fn main() {
        let window = WindowDesc::new(build_ui());
    
        AppLauncher::with_window(window)
            .log_to_console()
            .launch(())
            .unwrap();
    }
    
    fn build_ui() -> impl Widget<()> {
        let mut container = Flex::column()
            .cross_axis_alignment(CrossAxisAlignment::Fill);
    
        for _ in 0..10 {
            container.add_child(SizedBox::empty().height(200.0));
            container.add_child(
                ZStack::new(
                    Label::new("Base layer")
                        .align_vertical(UnitPoint::TOP)
                        .expand_width()
                        .fix_height(200.0)
                        .background(Color::grey8(20))
                    )
                    .with_centered_child(
                        Label::new("Overlay")
                            .center()
                            .fix_height(100.0)
                            .background(Color::grey8(0))
                    )
            );
        }
    
        Scroll::new(container)
            .vertical()
    }
    

    This happens because ZStack uses ctx.set_handled() to indicate to a widget that the position the cursor is on is overlapped by another widget. Therefore Scroll thinks one of its children already used the MouseWheel event and does not scroll.

    opened by xarvic 6
Releases(v0.7.0)
Modular FFXIV data toolkit written in rust.

ironworks Modular FFXIV data toolkit written in rust. ironworks is pre-1.0, and as such its API should be considered unstable. Breaking API changes wi

Saxon Landers 9 Sep 10, 2022
The Rust UI-Toolkit.

The Orbital Widget Toolkit is a cross-platform (G)UI toolkit for building scalable user interfaces with the programming language Rust. It's based on t

Redox OS 3.7k Sep 23, 2022
SixtyFPS is a toolkit to efficiently develop fluid graphical user interfaces for any display: embedded devices and desktop applications. We support multiple programming languages, such as Rust, C++ or JavaScript.

SixtyFPS is a toolkit to efficiently develop fluid graphical user interfaces for any display: embedded devices and desktop applications. We support multiple programming languages, such as Rust, C++ or JavaScript.

SixtyFPS 4.9k Sep 22, 2022
Cross-platform GUI toolkit written in Rust

Tuix is a cross-platform GUI toolkit written in Rust. The driving principle behind tuix is to be a self-contained, small-as-possible, but still fast,

George Atkinson 167 Aug 12, 2022
A Rust binding of the wxWidgets cross platform toolkit.

wxRust master: / mac(0.10): This is a Rust binding for the wxWidgets cross platform toolkit. API wxRust API documentation How it works The wxRust libr

KENZ 126 Sep 21, 2022
A light windows GUI toolkit for rust

Native Windows GUI Welcome to Native Windows GUI (aka NWG). A rust library to develop native GUI applications on the desktop for Microsoft Windows. NW

Gabriel Dube 1.5k Sep 21, 2022
A cross-platform GUI toolkit in Rust

NXUI - Native X UI A cross-platform GUI toolkit in Rust NXUI is a GUI toolkit that calls OS native APIs as much as possible to achieve fast operation.

らて 11 Jun 3, 2022
A graphical user interface toolkit for audio plugins.

HexoTK - A graphic user interface toolkit for audio plugins State of Development Super early! Building cargo run --example demo TODO / Features Every

Weird Constructor 13 Sep 17, 2022
Truly cross platform, truly native. multiple backend GUI for rust

WIP: Sauron-native a rust UI library that conquers all platforms ranging from desktop to mobile devices. An attempt to create a truly native, truly cr

Jovansonlee Cesar 627 Sep 17, 2022
Cross-platform native Rust menu library

A cross-platform Rust library for managing the native operating system menus.

Mads Marquart 12 Aug 31, 2022
Native Rust library for FastCGI

The FastCGI Rust implementation. Description gfcgi is a native Rust library for FastCGI. This library supports multithreaded socket listeners and HTTP

Andrey Gridchin 9 Aug 7, 2022
Winsafe-examples - Examples of native Windows applications written in Rust with WinSafe.

WinSafe examples This repo contains several examples of native Win32 applications written in Rust with WinSafe. All examples follow the same program s

Rodrigo 34 Sep 18, 2022
Example for Rust Android Native Development

Android console application example project based on rust & cargo-xdk

Zhuo Zhang 4 Mar 17, 2022
Simple and portable (but not inflexible) GUI library in C that uses the native GUI technologies of each platform it supports.

libui: a portable GUI library for C This README is being written. Status It has come to my attention that I have not been particularly clear about how

Pietro Gagliardi 10.3k Sep 23, 2022
OS-native file dialogs on Linux, OS X and Windows

nfd-rs nfd-rs is a Rust binding to the library nativefiledialog, that provides a convenient cross-platform interface to opening file dialogs on Linux,

Saurav Sachidanand 150 Sep 16, 2022
A tiny, neat C library that portably invokes native file open and save dialogs.

Native File Dialog A tiny, neat C library that portably invokes native file open, folder select and save dialogs. Write dialog code once and have it p

Michael Labbe 1.4k Sep 20, 2022
OS native dialogs for Windows, MacOS, and Linux

?? nfd2 nfd2 is a Rust binding to the nativefiledialog library, that provides a convenient cross-platform interface to opening file dialogs on Windows

Embark 33 May 15, 2022
Native Maps for Web, Mobile and Desktop

mapr Native Maps for Web, Mobile and Linux A map rendering library written in Rust. Example | Book | API | Chat in Matrix Space Project State This pro

MapLibre 872 Sep 24, 2022
Generate Anchor IDL for Native Solana Programs.

Native Solana To Anchor IDL Autogenerate Anchor IDL from programs written in Native Solana. Disclaimer The instructions must follow strict set of rule

acheron 67 Sep 2, 2022