Skip to content

Commit 0d320a0

Browse files
committed
tray menus with always-on-top toggle
1 parent a68c8a5 commit 0d320a0

File tree

4 files changed

+144
-12
lines changed

4 files changed

+144
-12
lines changed

Cargo.lock

Lines changed: 20 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

apps/desktop/src-tauri/tauri.conf.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
{
22
"$schema": "https://schema.tauri.app/config/2",
33
"version": "0.0.2",
4-
"productName": "Hyprnote",
4+
"productName": "Hyprnote Dev",
55
"mainBinaryName": "Hyprnote Dev",
66
"identifier": "com.hyprnote.dev",
77
"build": {

plugins/tray/Cargo.toml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,11 @@ specta-typescript = { workspace = true }
1616

1717
[dependencies]
1818
tauri = { workspace = true, features = ["tray-icon", "image-png"] }
19+
tauri-plugin-store2 = { workspace = true }
1920
tauri-plugin-windows = { workspace = true }
2021

2122
specta = { workspace = true }
23+
strum = { workspace = true, features = ["derive"] }
2224
tauri-specta = { workspace = true, features = ["derive", "typescript"] }
25+
26+
webbrowser = "1.0.4"

plugins/tray/src/ext.rs

Lines changed: 119 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,29 @@
1+
use crate::PLUGIN_NAME;
12
use tauri::{
23
image::Image,
3-
menu::{Menu, MenuId, MenuItem, PredefinedMenuItem},
4+
menu::{CheckMenuItem, Menu, MenuId, MenuItem, PredefinedMenuItem},
45
tray::TrayIconBuilder,
5-
AppHandle, Manager, Result,
6+
AppHandle, Result,
67
};
78

89
pub enum TrayItem {
9-
Open,
10+
Info,
11+
Github,
12+
Twitter,
13+
Discord,
1014
Quit,
15+
AlwaysOnTop,
1116
}
1217

1318
impl From<TrayItem> for MenuId {
1419
fn from(value: TrayItem) -> Self {
1520
match value {
16-
TrayItem::Open => "open_hypr",
21+
TrayItem::Info => "info_hypr",
22+
TrayItem::Github => "github_hypr",
23+
TrayItem::Twitter => "twitter_hypr",
24+
TrayItem::Discord => "discord_hypr",
1725
TrayItem::Quit => "quit_hypr",
26+
TrayItem::AlwaysOnTop => "always_on_top_hypr",
1827
}
1928
.into()
2029
}
@@ -24,8 +33,12 @@ impl From<MenuId> for TrayItem {
2433
fn from(id: MenuId) -> Self {
2534
let id = id.0.as_str();
2635
match id {
27-
"open_hypr" => TrayItem::Open,
36+
"info_hypr" => TrayItem::Info,
37+
"github_hypr" => TrayItem::Github,
38+
"twitter_hypr" => TrayItem::Twitter,
39+
"discord_hypr" => TrayItem::Discord,
2840
"quit_hypr" => TrayItem::Quit,
41+
"always_on_top_hypr" => TrayItem::AlwaysOnTop,
2942
_ => unreachable!(),
3043
}
3144
}
@@ -35,16 +48,35 @@ pub trait TrayPluginExt<R: tauri::Runtime> {
3548
fn create_tray(&self) -> Result<()>;
3649
}
3750

51+
#[derive(Debug, PartialEq, Eq, Hash, strum::Display)]
52+
enum StoreKey {
53+
MainWindowAlwaysOnTop,
54+
}
55+
56+
impl tauri_plugin_store2::ScopedStoreKey for StoreKey {}
57+
3858
impl<T: tauri::Manager<tauri::Wry>> TrayPluginExt<tauri::Wry> for T {
3959
fn create_tray(&self) -> Result<()> {
4060
let app = self.app_handle();
4161

62+
let store = {
63+
use tauri_plugin_store2::StorePluginExt;
64+
app.scoped_store::<StoreKey>(PLUGIN_NAME).unwrap()
65+
};
66+
67+
let always_on_top = always_on_top_menu(app, always_on_top_state(&store))?;
68+
4269
let menu = Menu::with_items(
4370
app,
4471
&[
45-
&MenuItem::with_id(app, TrayItem::Open, "Open", true, None::<&str>)?,
72+
&info_menu(app)?,
73+
&github_menu(app)?,
74+
&twitter_menu(app)?,
75+
&discord_menu(app)?,
76+
&PredefinedMenuItem::separator(app)?,
77+
&always_on_top,
4678
&PredefinedMenuItem::separator(app)?,
47-
&MenuItem::with_id(app, TrayItem::Quit, "Quit", true, None::<&str>)?,
79+
&quit_menu(app)?,
4880
],
4981
)?;
5082

@@ -57,10 +89,34 @@ impl<T: tauri::Manager<tauri::Wry>> TrayPluginExt<tauri::Wry> for T {
5789
.show_menu_on_left_click(true)
5890
.on_menu_event({
5991
move |app: &AppHandle, event| match TrayItem::from(event.id.clone()) {
60-
TrayItem::Open => {
61-
if let Some(window) = app.get_webview_window("main") {
62-
window.show().unwrap();
63-
window.set_focus().unwrap();
92+
TrayItem::Info => {}
93+
TrayItem::Github => {
94+
let _ = webbrowser::open("https://github.com/fastrepl/hyprnote");
95+
}
96+
TrayItem::Twitter => {
97+
let _ = webbrowser::open("https://hyprnote.com/x");
98+
}
99+
TrayItem::Discord => {
100+
let _ = webbrowser::open("https://hyprnote.com/discord");
101+
}
102+
TrayItem::AlwaysOnTop => {
103+
let next_always_on_top = !always_on_top_state(&store);
104+
105+
let toggled = {
106+
use tauri_plugin_windows::HyprWindowId;
107+
if let Some(window) = HyprWindowId::Main.get(app) {
108+
window.set_always_on_top(next_always_on_top).is_ok()
109+
} else {
110+
false
111+
}
112+
};
113+
114+
if toggled {
115+
store
116+
.set(StoreKey::MainWindowAlwaysOnTop, next_always_on_top)
117+
.unwrap();
118+
119+
always_on_top.set_checked(next_always_on_top).unwrap();
64120
}
65121
}
66122
TrayItem::Quit => {
@@ -77,3 +133,55 @@ impl<T: tauri::Manager<tauri::Wry>> TrayPluginExt<tauri::Wry> for T {
77133
Ok(())
78134
}
79135
}
136+
137+
fn info_menu<R: tauri::Runtime>(app: &AppHandle<R>) -> Result<MenuItem<R>> {
138+
let info = app.package_info();
139+
140+
let display_name = match info.name.as_str() {
141+
"Hyprnote" => format!("Hyprnote v{}", &info.version),
142+
"Hyprnote Dev" => format!("Hyprnote v{} (dev)", &info.version),
143+
"Hyprnote Nightly" => format!("Hyprnote v{} (nightly)", &info.version),
144+
_ => unreachable!(),
145+
};
146+
147+
MenuItem::with_id(app, TrayItem::Info, &display_name, false, None::<&str>)
148+
}
149+
150+
fn github_menu<R: tauri::Runtime>(app: &AppHandle<R>) -> Result<MenuItem<R>> {
151+
MenuItem::with_id(app, TrayItem::Github, "GitHub", true, None::<&str>)
152+
}
153+
154+
fn twitter_menu<R: tauri::Runtime>(app: &AppHandle<R>) -> Result<MenuItem<R>> {
155+
MenuItem::with_id(app, TrayItem::Twitter, "Twitter", true, None::<&str>)
156+
}
157+
158+
fn discord_menu<R: tauri::Runtime>(app: &AppHandle<R>) -> Result<MenuItem<R>> {
159+
MenuItem::with_id(app, TrayItem::Discord, "Discord", true, None::<&str>)
160+
}
161+
162+
fn quit_menu<R: tauri::Runtime>(app: &AppHandle<R>) -> Result<MenuItem<R>> {
163+
MenuItem::with_id(app, TrayItem::Quit, "Quit Completely", true, Some("cmd+q"))
164+
}
165+
166+
fn always_on_top_menu<R: tauri::Runtime>(
167+
app: &AppHandle<R>,
168+
initial_state: bool,
169+
) -> Result<CheckMenuItem<R>> {
170+
CheckMenuItem::with_id(
171+
app,
172+
TrayItem::AlwaysOnTop,
173+
"Always on top",
174+
true,
175+
initial_state,
176+
Some("cmd+shift+t"),
177+
)
178+
}
179+
180+
fn always_on_top_state<R: tauri::Runtime>(
181+
store: &tauri_plugin_store2::ScopedStore<R, StoreKey>,
182+
) -> bool {
183+
store
184+
.get::<bool>(StoreKey::MainWindowAlwaysOnTop)
185+
.unwrap_or(Some(false))
186+
.unwrap_or(false)
187+
}

0 commit comments

Comments
 (0)