Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support for drawing custom headerbars #759

Open
kaimast opened this issue Mar 2, 2021 · 6 comments
Open

Support for drawing custom headerbars #759

kaimast opened this issue Mar 2, 2021 · 6 comments
Labels
feature New feature or request rendering shell

Comments

@kaimast
Copy link

kaimast commented Mar 2, 2021

On most Unix desktop environments applications can draw an application-specific CSD that replaces the native window title bar.
Here are screenshots of Telegram doing this: electron/electron#11907 (comment)

It would be cool to do this in iced as well, for example, to draw a semi-transparent title bar over a 3d scene.
This should be possible to implement once winit lands support for it (see here)

@hecrj hecrj added the feature New feature or request label Mar 3, 2021
@akhilman
Copy link
Contributor

akhilman commented Dec 1, 2021

I think winit already have almost everything required to do client side decoration.

All we need is a way to call drag_window, set_maximized, set_minimized, set_fullscreen from winit::window::Window, and drag_resize_window when rust-windowing/winit#725 will be resolved. Also set_cursor_grab and set_cursor_visible might be useful in some cases.

I think the iced::Command can be extended to include commands to control the window.

@tonogram
Copy link

tonogram commented Nov 10, 2022

It would also be a nice touch if we can make the title bar transparent on MacOS, a la Tauri 1.2. Not sure what the best interface for this would be, seeing as it's a platform-specific thing, but it looks pleasant and would certainly be good to have available.

(If anyone who actually knows how windowing works wants to implement this, this is the thing you need.)

@juxuanu
Copy link

juxuanu commented Oct 11, 2024

Given this is not closed, does it mean one cannot achieve headerbars akin to GTK's/Adwaita's in Iced? Just curious.

@kaimast
Copy link
Author

kaimast commented Oct 11, 2024

Given this is not closed, does it mean one cannot achieve headerbars akin to GTK's/Adwaita's in Iced? Just curious.

You can disable window decorations in winit and then draw whatever you want. There are just no widgets to do this easily.

I was hoping iced could at least have a widget that lets me drag the associated window, so I can build my own title bar with it.

@tsuza
Copy link
Contributor

tsuza commented Oct 12, 2024

Given this is not closed, does it mean one cannot achieve headerbars akin to GTK's/Adwaita's in Iced? Just curious.

You can disable window decorations in winit and then draw whatever you want. There are just no widgets to do this easily.

I was hoping iced could at least have a widget that lets me drag the associated window, so I can build my own title bar with it.

Use a MouseArea and then use window::drag when you're inside it.

@kennethnym
Copy link

for anyone who wants to achieve transparent title bar with content underneath, i pieced together a small snippet of doing so, which sets the style whenever a new window is opened:

impl App {
    pub fn subscription(&self) -> Subscription<Message> {
        iced::event::listen().map(Message::IcedEventReceived)
    }

    pub fn update(&mut self, message: Message) -> Task<Message> {
        match message {
            Message::IcedEventReceived(iced::event::Event::Window(iced::window::Event::Opened { .. })) => {
                iced::window::get_latest().and_then(|id| {
                    iced::window::run_with_handle(id, |handle| {
                        on_window_opened(handle);
                        Message::WindowOpened
                    })
                })
            }
            _ => Task::none()
        }
    }

    pub fn view(&self) -> iced::Element<Message> {
        // omitted
    }
}

fn on_window_opened(handle: WindowHandle) {
    if let RawWindowHandle::AppKit(raw_handle) = handle.as_raw() {
        let ns_view = raw_handle.ns_view.as_ptr();
        // SAFETY: The pointer came from `WindowHandle`, which ensures
        // that the `AppKitWindowHandle` contains a valid pointer to an
        // `NSView`.
        // Unwrap is fine, since the pointer came from `NonNull`.
        let ns_view: Retained<NSView> = unsafe { Retained::retain(ns_view.cast()) }.unwrap();
        let window = ns_view.window().expect("view was not installed in a window");
        window.setStyleMask(window.styleMask() | NSFullSizeContentViewWindowMask);
        window.setTitlebarAppearsTransparent(true);
    }
}

then, simply pass App::subscription to your iced::run/iced::application call:

fn main() -> iced::Result {
    iced::application("my app", app::App::update, app::App::view)
        .subscription(app::App::subscription) // pass it here
        .run()
}

the gist is to obtain the underlying raw window handle, then you can obtain the attached NSView, which you can then use to obtain NSWindow from which you can customize the window however you want.

(make sure you have objc2 and objc2-app-kit installed)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
feature New feature or request rendering shell
Projects
None yet
Development

No branches or pull requests

7 participants