Skip to content

Commit

Permalink
feat: add set_badge_count for Linux, iOS; set_badge_label for Mac…
Browse files Browse the repository at this point in the history
…os; `set_overlay_icon` for Windows (#1002)
  • Loading branch information
ahqsoftwares authored Nov 5, 2024
1 parent aff33fb commit 1f72c24
Show file tree
Hide file tree
Showing 20 changed files with 330 additions and 8 deletions.
5 changes: 5 additions & 0 deletions .changes/badge_count.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"tao": patch
---

Add `WindowExtUnix::set_badge_count` for Linux, `WindowExtIos::set_badge_count` for iOS, `WindowExtMacos::set_badge_label` for Macos, `MacdowExtWindows::set_overlay_icon` for Windows
Binary file added examples/icon.ico
Binary file not shown.
129 changes: 129 additions & 0 deletions examples/overlay.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,129 @@
// Copyright 2014-2021 The winit contributors
// Copyright 2021-2023 Tauri Programme within The Commons Conservancy
// SPDX-License-Identifier: Apache-2.0

use std::env::current_dir;
use tao::{
event::{ElementState, Event, KeyEvent, WindowEvent},
event_loop::{ControlFlow, EventLoop},
keyboard::{Key, ModifiersState},
window::WindowBuilder,
};

#[cfg(any(
target_os = "linux",
target_os = "dragonfly",
target_os = "freebsd",
target_os = "netbsd",
target_os = "openbsd"
))]
use tao::platform::unix::WindowExtUnix;

#[cfg(target_os = "macos")]
use tao::platform::macos::WindowExtMacOS;

#[cfg(target_os = "ios")]
use tao::platform::ios::WindowExtIOS;

#[cfg(windows)]
use tao::{
dpi::PhysicalSize, platform::windows::IconExtWindows, platform::windows::WindowExtWindows,
window::Icon,
};

#[allow(clippy::single_match)]
fn main() {
env_logger::init();
let event_loop = EventLoop::new();

let window = WindowBuilder::new().build(&event_loop).unwrap();

let mut modifiers = ModifiersState::default();

eprintln!("Key mappings:");
#[cfg(windows)]
eprintln!(" [any key]: Show the Overlay Icon");
#[cfg(not(windows))]
eprintln!(" [1-5]: Show a Badge count");
eprintln!(" Ctrl+1: Clear");

event_loop.run(move |event, _, control_flow| {
*control_flow = ControlFlow::Wait;

match event {
Event::WindowEvent {
event: WindowEvent::CloseRequested,
..
} => *control_flow = ControlFlow::Exit,
Event::WindowEvent { event, .. } => match event {
WindowEvent::ModifiersChanged(new_state) => {
modifiers = new_state;
}
WindowEvent::KeyboardInput {
event:
KeyEvent {
logical_key: Key::Character(key_str),
state: ElementState::Released,
..
},
..
} => {
let _count = match key_str {
"1" => 1,
"2" => 2,
"3" => 3,
"4" => 4,
"5" => 5,
_ => 20,
};

if modifiers.is_empty() {
#[cfg(windows)]
{
let mut path = current_dir().unwrap();
path.push("./examples/icon.ico");
let icon = Icon::from_path(path, Some(PhysicalSize::new(32, 32))).unwrap();

window.set_overlay_icon(Some(&icon));
}

#[cfg(any(
target_os = "linux",
target_os = "dragonfly",
target_os = "freebsd",
target_os = "netbsd",
target_os = "openbsd"
))]
window.set_badge_count(Some(_count), None);

#[cfg(target_os = "macos")]
window.set_badge_label(_count.to_string().into());

#[cfg(target_os = "ios")]
window.set_badge_count(_count);
} else if modifiers.control_key() && key_str == "1" {
#[cfg(windows)]
window.set_overlay_icon(None);

#[cfg(any(
target_os = "linux",
target_os = "dragonfly",
target_os = "freebsd",
target_os = "netbsd",
target_os = "openbsd"
))]
window.set_badge_count(None, None);

#[cfg(target_os = "macos")]
window.set_badge_label(None);

#[cfg(target_os = "ios")]
window.set_badge_count(0);
}
}
_ => {}
},
_ => {}
}
});
}
22 changes: 21 additions & 1 deletion src/platform/ios.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,9 @@
use std::os::raw::c_void;

use crate::{
event_loop::EventLoop,
event_loop::{EventLoop, EventLoopWindowTarget},
monitor::{MonitorHandle, VideoMode},
platform_impl::set_badge_count,
window::{Window, WindowBuilder},
};

Expand Down Expand Up @@ -98,6 +99,9 @@ pub trait WindowExtIOS {
/// and then calls
/// [`-[UIViewController setNeedsStatusBarAppearanceUpdate]`](https://developer.apple.com/documentation/uikit/uiviewcontroller/1621354-setneedsstatusbarappearanceupdat?language=objc).
fn set_prefers_status_bar_hidden(&self, hidden: bool);

/// Sets the badge count on iOS launcher. 0 hides the count
fn set_badge_count(&self, count: i32);
}

impl WindowExtIOS for Window {
Expand Down Expand Up @@ -142,6 +146,22 @@ impl WindowExtIOS for Window {
fn set_prefers_status_bar_hidden(&self, hidden: bool) {
self.window.set_prefers_status_bar_hidden(hidden)
}

#[inline]
fn set_badge_count(&self, count: i32) {
self.window.set_badge_count(count)
}
}

pub trait EventLoopWindowTargetExtIOS {
/// Sets the badge count on iOS launcher. 0 hides the count
fn set_badge_count(&self, count: i32);
}

impl<T> EventLoopWindowTargetExtIOS for EventLoopWindowTarget<T> {
fn set_badge_count(&self, count: i32) {
set_badge_count(count)
}
}

/// Additional methods on [`WindowBuilder`] that are specific to iOS.
Expand Down
17 changes: 16 additions & 1 deletion src/platform/macos.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ use crate::{
dpi::{LogicalSize, Position},
event_loop::{EventLoop, EventLoopWindowTarget},
monitor::MonitorHandle,
platform_impl::{get_aux_state_mut, Parent},
platform_impl::{get_aux_state_mut, set_badge_label, Parent},
window::{Window, WindowBuilder},
};

Expand Down Expand Up @@ -84,6 +84,9 @@ pub trait WindowExtMacOS {
///
/// <https://developer.apple.com/documentation/appkit/nswindow/1419167-titlebarappearstransparent>
fn set_titlebar_transparent(&self, transparent: bool);

/// Sets the badge label on the taskbar
fn set_badge_label(&self, label: Option<String>);
}

impl WindowExtMacOS for Window {
Expand Down Expand Up @@ -161,6 +164,11 @@ impl WindowExtMacOS for Window {
fn set_titlebar_transparent(&self, transparent: bool) {
self.window.set_titlebar_transparent(transparent);
}

#[inline]
fn set_badge_label(&self, label: Option<String>) {
self.window.set_badge_label(label);
}
}

/// Corresponds to `NSApplicationActivationPolicy`.
Expand Down Expand Up @@ -388,6 +396,9 @@ pub trait EventLoopWindowTargetExtMacOS {
/// To set the activation policy before the app starts running, see
/// [`EventLoopExtMacOS::set_activation_policy`](crate::platform::macos::EventLoopExtMacOS::set_activation_policy).
fn set_activation_policy_at_runtime(&self, activation_policy: ActivationPolicy);

/// Sets the badge label on macos dock
fn set_badge_label(&self, label: Option<String>);
}

impl<T> EventLoopWindowTargetExtMacOS for EventLoopWindowTarget<T> {
Expand Down Expand Up @@ -415,4 +426,8 @@ impl<T> EventLoopWindowTargetExtMacOS for EventLoopWindowTarget<T> {
let ns_activation_policy: NSApplicationActivationPolicy = activation_policy.into();
unsafe { msg_send![app, setActivationPolicy: ns_activation_policy] }
}

fn set_badge_label(&self, label: Option<String>) {
set_badge_label(label);
}
}
14 changes: 14 additions & 0 deletions src/platform/unix.rs
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,8 @@ pub trait WindowExtUnix {

/// Whether to show the window icon in the taskbar or not.
fn set_skip_taskbar(&self, skip: bool) -> Result<(), ExternalError>;

fn set_badge_count(&self, count: Option<i64>, desktop_filename: Option<String>);
}

impl WindowExtUnix for Window {
Expand All @@ -100,6 +102,10 @@ impl WindowExtUnix for Window {
let window = UnixWindow::new_from_gtk_window(&event_loop_window_target.p, window)?;
Ok(Window { window: window })
}

fn set_badge_count(&self, count: Option<i64>, desktop_filename: Option<String>) {
self.window.set_badge_count(count, desktop_filename);
}
}

pub trait WindowBuilderExtUnix {
Expand Down Expand Up @@ -208,6 +214,9 @@ pub trait EventLoopWindowTargetExtUnix {

/// Returns the gtk application for this event loop.
fn gtk_app(&self) -> &gtk::Application;

/// Sets the badge count on the taskbar
fn set_badge_count(&self, count: Option<i64>, desktop_filename: Option<String>);
}

impl<T> EventLoopWindowTargetExtUnix for EventLoopWindowTarget<T> {
Expand Down Expand Up @@ -249,6 +258,11 @@ impl<T> EventLoopWindowTargetExtUnix for EventLoopWindowTarget<T> {
fn gtk_app(&self) -> &gtk::Application {
&self.p.app
}

#[inline]
fn set_badge_count(&self, count: Option<i64>, desktop_filename: Option<String>) {
self.p.set_badge_count(count, desktop_filename);
}
}

unsafe extern "C" fn x_error_callback(
Expand Down
8 changes: 8 additions & 0 deletions src/platform/windows.rs
Original file line number Diff line number Diff line change
Expand Up @@ -159,6 +159,9 @@ pub trait WindowExtWindows {
/// This sets `ICON_BIG`. A good ceiling here is 256x256.
fn set_taskbar_icon(&self, taskbar_icon: Option<Icon>);

/// This sets the overlay icon
fn set_overlay_icon(&self, icon: Option<&Icon>);

/// Returns the current window theme.
fn theme(&self) -> Theme;

Expand Down Expand Up @@ -238,6 +241,11 @@ impl WindowExtWindows for Window {
fn set_rtl(&self, rtl: bool) {
self.window.set_rtl(rtl)
}

#[inline]
fn set_overlay_icon(&self, icon: Option<&Icon>) {
self.window.set_overlay_icon(icon);
}
}

/// Additional methods on `WindowBuilder` that are specific to Windows.
Expand Down
10 changes: 10 additions & 0 deletions src/platform_impl/ios/badge.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
use objc::runtime::{Class, Object};
use objc::{msg_send, sel, sel_impl};

pub fn set_badge_count(count: i32) {
unsafe {
let ui_application = Class::get("UIApplication").expect("Failed to get UIApplication class");
let app: *mut Object = msg_send![ui_application, sharedApplication];
let _: () = msg_send![app, setApplicationIconBadgeNumber:count];
}
}
7 changes: 6 additions & 1 deletion src/platform_impl/ios/event_loop.rs
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ use crate::platform_impl::platform::{
CFRunLoopSourceInvalidate, CFRunLoopSourceRef, CFRunLoopSourceSignal, CFRunLoopWakeUp,
NSStringRust, UIApplicationMain, UIUserInterfaceIdiom,
},
monitor, view, MonitorHandle,
monitor, set_badge_count, view, MonitorHandle,
};

#[non_exhaustive]
Expand Down Expand Up @@ -94,6 +94,11 @@ impl<T: 'static> EventLoopWindowTarget<T> {
debug!("`EventLoopWindowTarget::cursor_position` is ignored on iOS");
Ok((0, 0).into())
}

/// Sets badge count on iOS launcher. 0 hides the count
pub fn set_badge_count(&self, count: i32) {
set_badge_count(count);
}
}

#[derive(Default, Debug, Copy, Clone, PartialEq, Eq, Hash)]
Expand Down
3 changes: 2 additions & 1 deletion src/platform_impl/ios/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,7 @@ macro_rules! assert_main_thread {
}

mod app_state;
mod badge;
mod event_loop;
mod ffi;
mod keycode;
Expand All @@ -90,8 +91,8 @@ pub use self::{
monitor::{MonitorHandle, VideoMode},
window::{PlatformSpecificWindowBuilderAttributes, Window, WindowId},
};

pub(crate) use crate::icon::NoIcon as PlatformIcon;
pub(crate) use badge::set_badge_count;

// todo: implement iOS keyboard event
#[derive(Debug, Clone, Eq, PartialEq, Hash)]
Expand Down
7 changes: 6 additions & 1 deletion src/platform_impl/ios/window.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ use crate::{
id, CGFloat, CGPoint, CGRect, CGSize, UIEdgeInsets, UIInterfaceOrientationMask, UIRectEdge,
UIScreenOverscanCompensation,
},
monitor, view, EventLoopWindowTarget, MonitorHandle,
monitor, set_badge_count, view, EventLoopWindowTarget, MonitorHandle,
},
window::{
CursorIcon, Fullscreen, ResizeDirection, Theme, UserAttentionType, WindowAttributes,
Expand Down Expand Up @@ -440,6 +440,11 @@ impl Inner {
pub fn theme(&self) -> Theme {
Theme::Light
}

/// Sets badge count on iOS launcher. 0 hides the count
pub fn set_badge_count(&self, count: i32) {
set_badge_count(count);
}
}

pub struct Window {
Expand Down
14 changes: 14 additions & 0 deletions src/platform_impl/linux/event_loop.rs
Original file line number Diff line number Diff line change
Expand Up @@ -160,6 +160,16 @@ impl<T> EventLoopWindowTarget<T> {
}
}

#[inline]
pub fn set_badge_count(&self, count: Option<i64>, desktop_filename: Option<String>) {
if let Err(e) = self.window_requests_tx.send((
WindowId::dummy(),
WindowRequest::BadgeCount(count, desktop_filename),
)) {
log::warn!("Fail to send update progress bar request: {e}");
}
}

#[inline]
pub fn set_theme(&self, theme: Option<Theme>) {
if let Err(e) = self
Expand Down Expand Up @@ -432,6 +442,7 @@ impl<T: 'static> EventLoop<T> {
};
}
WindowRequest::ProgressBarState(_) => unreachable!(),
WindowRequest::BadgeCount(_, _) => unreachable!(),
WindowRequest::SetTheme(_) => unreachable!(),
WindowRequest::WireUpEvents {
transparent,
Expand Down Expand Up @@ -921,6 +932,9 @@ impl<T: 'static> EventLoop<T> {
WindowRequest::ProgressBarState(state) => {
taskbar.update(state);
}
WindowRequest::BadgeCount(count, desktop_filename) => {
taskbar.update_count(count, desktop_filename);
}
WindowRequest::SetTheme(theme) => {
if let Some(settings) = Settings::default() {
match theme {
Expand Down
Loading

0 comments on commit 1f72c24

Please sign in to comment.