Skip to content

Commit c222074

Browse files
authored
Merge pull request #292 from MystikoLab/ui-improvements
Main launcher UI improvements
2 parents 45b35a2 + 049feb1 commit c222074

16 files changed

Lines changed: 180 additions & 82 deletions

File tree

docs/lucide.ttf

801 KB
Binary file not shown.

src/app.rs

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
//! Main logic for the app
22
use std::collections::HashMap;
33

4-
use crate::app::apps::{App, AppCommand, ICNS_ICON};
4+
use crate::app::apps::{App, AppCommand, AppIcon, ICNS_ICON};
55
use crate::commands::Function;
66
use crate::config::{Config, MainPage, Position, Shelly, ThemeMode};
77
use crate::debounce::DebouncePolicy;
@@ -17,10 +17,10 @@ pub mod tile;
1717

1818
use iced::window::{self, Id, Settings};
1919
/// The default window width
20-
pub const WINDOW_WIDTH: f32 = 500.;
20+
pub const WINDOW_WIDTH: f32 = 550.;
2121

2222
/// The default window height
23-
pub const DEFAULT_WINDOW_HEIGHT: f32 = 100.;
23+
pub const DEFAULT_WINDOW_HEIGHT: f32 = 150.;
2424

2525
/// Maximum file search results returned by a single mdfind invocation.
2626
pub const FILE_SEARCH_MAX_RESULTS: u32 = 400;
@@ -281,7 +281,7 @@ impl ToApps for HashMap<String, String> {
281281
)),
282282
search_name: key.to_owned(),
283283
desc: "Switch Modes".to_string(),
284-
icons: icons.clone(),
284+
icons: AppIcon::from_handle(icons.clone()),
285285
display_name,
286286
}
287287
})
@@ -292,7 +292,7 @@ impl ToApps for HashMap<String, String> {
292292
ranking: 0,
293293
open_command: AppCommand::Message(Message::SwitchMode("Default".to_string())),
294294
desc: "Change mode".to_string(),
295-
icons: icons.clone(),
295+
icons: AppIcon::from_handle(icons.clone()),
296296
display_name: "Default mode".to_string(),
297297
search_name: "default".to_string(),
298298
});

src/app/apps.rs

Lines changed: 95 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -5,11 +5,13 @@
55
use std::io::Cursor;
66

77
use iced::{
8-
Alignment,
8+
Alignment, Element,
99
Length::{self, Fill},
10+
border::Radius,
1011
widget::{
1112
Button, Row, Text, container,
1213
image::{Handle, Viewer},
14+
space,
1315
text::Wrapping,
1416
},
1517
};
@@ -18,7 +20,10 @@ use crate::{
1820
app::{Message, Page, RUSTCAST_DESC_NAME},
1921
clipboard::ClipBoardContentType,
2022
commands::Function,
21-
styles::{favourite_button_style, result_button_style, result_row_container_style},
23+
styles::{
24+
clipboard_icon, emoji_icon, favourite_button_style, filesearch_icon, info_icon, quit_icon,
25+
refresh_icon, result_button_style, result_row_container_style, settings_icon,
26+
},
2227
utils::icns_data_to_handle,
2328
};
2429

@@ -44,11 +49,65 @@ pub struct App {
4449
pub ranking: i32,
4550
pub open_command: AppCommand,
4651
pub desc: String,
47-
pub icons: Option<iced::widget::image::Handle>,
52+
pub icons: AppIcon,
4853
pub display_name: String,
4954
pub search_name: String,
5055
}
5156

57+
#[derive(Debug, Clone)]
58+
pub enum AppIcon {
59+
IconFromFont(fn() -> Text<'static>),
60+
ImageHandle(iced::widget::image::Handle),
61+
None,
62+
}
63+
64+
impl PartialEq for AppIcon {
65+
fn eq(&self, other: &Self) -> bool {
66+
match (self, other) {
67+
(Self::IconFromFont(_), Self::IconFromFont(_)) => true,
68+
(Self::ImageHandle(l0), Self::ImageHandle(r0)) => l0 == r0,
69+
(AppIcon::None, AppIcon::None) => true,
70+
_ => false,
71+
}
72+
}
73+
}
74+
75+
impl AppIcon {
76+
pub fn render(&self) -> Element<'static, Message> {
77+
match self {
78+
Self::IconFromFont(icon_fn) => container(
79+
icon_fn()
80+
.center()
81+
.size(20)
82+
.height(Length::Fill)
83+
.width(Length::Fill),
84+
)
85+
.style(|theme| container::Style {
86+
background: None,
87+
border: iced::Border {
88+
color: theme.palette().text,
89+
width: 0.,
90+
radius: Radius::new(10),
91+
},
92+
..Default::default()
93+
})
94+
.height(30)
95+
.width(30)
96+
.into(),
97+
Self::ImageHandle(handle) => Viewer::new(handle)
98+
.height(30)
99+
.width(30)
100+
.scale_step(0.)
101+
.into(),
102+
Self::None => space().into(),
103+
}
104+
}
105+
106+
pub fn from_handle(handle: Option<Handle>) -> AppIcon {
107+
handle.map(AppIcon::ImageHandle).unwrap_or(AppIcon::None)
108+
}
109+
}
110+
52111
impl PartialEq for App {
53112
fn eq(&self, other: &Self) -> bool {
54113
self.search_name == other.search_name
@@ -59,7 +118,7 @@ impl PartialEq for App {
59118
}
60119

61120
impl App {
62-
pub fn new(name: String, icon: Option<Handle>, desc: String, command: AppCommand) -> Self {
121+
pub fn new(name: String, icon: AppIcon, desc: String, command: AppCommand) -> Self {
63122
Self {
64123
ranking: 0,
65124
open_command: command,
@@ -75,7 +134,7 @@ impl App {
75134
.filter(|x| x.unicode_version() < emojis::UnicodeVersion::new(17, 13))
76135
.map(|x| App {
77136
ranking: 0,
78-
icons: None,
137+
icons: AppIcon::None,
79138
display_name: x.to_string(),
80139
search_name: x.name().to_string(),
81140
open_command: AppCommand::Function(Function::CopyToClipboard(
@@ -89,8 +148,6 @@ impl App {
89148
pub fn basic_apps() -> Vec<App> {
90149
let app_version = option_env!("APP_VERSION").unwrap_or("Unknown Version");
91150

92-
let icons = icns_data_to_handle(ICNS_ICON.to_vec());
93-
94151
let ferris_handle =
95152
image::ImageReader::new(Cursor::new(include_bytes!("../../docs/ferris_rs.png")))
96153
.with_guessed_format()
@@ -105,7 +162,7 @@ impl App {
105162
open_command: AppCommand::Function(Function::OpenWebsite(
106163
"https://ferris.rs".to_string(),
107164
)),
108-
icons: ferris_handle,
165+
icons: AppIcon::from_handle(ferris_handle),
109166
desc: "Easter Egg".to_string(),
110167
display_name: "Ferris Plushies".to_string(),
111168
search_name: "ferris.rs".to_string(),
@@ -114,63 +171,63 @@ impl App {
114171
ranking: 0,
115172
open_command: AppCommand::Function(Function::Quit),
116173
desc: RUSTCAST_DESC_NAME.to_string(),
117-
icons: icons.clone(),
174+
icons: AppIcon::IconFromFont(quit_icon),
118175
display_name: "Quit RustCast".to_string(),
119176
search_name: "quit".to_string(),
120177
},
121178
App {
122179
ranking: 0,
123180
open_command: AppCommand::Function(Function::QuitAllApps),
124181
desc: RUSTCAST_DESC_NAME.to_string(),
125-
icons: icons.clone(),
182+
icons: AppIcon::IconFromFont(quit_icon),
126183
display_name: "Quit All Apps".to_string(),
127184
search_name: "quit all apps".to_string(),
128185
},
129186
App {
130187
ranking: 0,
131188
open_command: AppCommand::Message(Message::OpenSettingsWindow),
132189
desc: RUSTCAST_DESC_NAME.to_string(),
133-
icons: icons.clone(),
190+
icons: AppIcon::IconFromFont(settings_icon),
134191
display_name: "Open RustCast Preferences".to_string(),
135192
search_name: "settings".to_string(),
136193
},
137194
App {
138195
ranking: 0,
139196
open_command: AppCommand::Message(Message::SwitchToPage(Page::EmojiSearch)),
140197
desc: RUSTCAST_DESC_NAME.to_string(),
141-
icons: icons.clone(),
198+
icons: AppIcon::IconFromFont(emoji_icon),
142199
display_name: "Search for an Emoji".to_string(),
143200
search_name: "emoji".to_string(),
144201
},
145202
App {
146203
ranking: 0,
147204
open_command: AppCommand::Message(Message::SwitchToPage(Page::ClipboardHistory)),
148205
desc: RUSTCAST_DESC_NAME.to_string(),
149-
icons: icons.clone(),
206+
icons: AppIcon::IconFromFont(clipboard_icon),
150207
display_name: "Clipboard History".to_string(),
151208
search_name: "clipboard".to_string(),
152209
},
153210
App {
154211
ranking: 0,
155212
open_command: AppCommand::Message(Message::SwitchToPage(Page::FileSearch)),
156213
desc: RUSTCAST_DESC_NAME.to_string(),
157-
icons: icons.clone(),
214+
icons: AppIcon::IconFromFont(filesearch_icon),
158215
display_name: "Search for a file".to_string(),
159216
search_name: "file search".to_string(),
160217
},
161218
App {
162219
ranking: 0,
163220
open_command: AppCommand::Message(Message::ReloadConfig),
164221
desc: RUSTCAST_DESC_NAME.to_string(),
165-
icons: icons.clone(),
222+
icons: AppIcon::IconFromFont(refresh_icon),
166223
display_name: "Reload RustCast".to_string(),
167224
search_name: "refresh".to_string(),
168225
},
169226
App {
170227
ranking: 0,
171228
open_command: AppCommand::Display,
172229
desc: RUSTCAST_DESC_NAME.to_string(),
173-
icons: icons.clone(),
230+
icons: AppIcon::IconFromFont(info_icon),
174231
display_name: format!("Current RustCast Version: {app_version}"),
175232
search_name: "version".to_string(),
176233
},
@@ -204,7 +261,7 @@ impl App {
204261
ranking: 0,
205262
open_command: AppCommand::Function(Function::TileWindow(pos.clone())),
206263
desc: "Window Tiling".to_string(),
207-
icons: icons.clone(),
264+
icons: AppIcon::from_handle(icons.clone()),
208265
display_name: name.to_string(),
209266
search_name: name.to_lowercase(),
210267
})
@@ -222,46 +279,40 @@ impl App {
222279
let focused = focussed_id == id_num;
223280

224281
// Title + subtitle (Raycast style)
225-
let text_block = iced::widget::Column::new()
226-
.spacing(2)
227-
.push(
228-
Text::new(self.display_name)
229-
.font(theme.font())
230-
.size(16)
231-
.wrapping(Wrapping::None)
232-
.color(theme.text_color(1.0)),
233-
)
234-
.push(
235-
Text::new(self.desc)
236-
.font(theme.font())
237-
.size(13)
238-
.color(theme.text_color(0.55)),
239-
);
282+
let text_block = Text::new(self.display_name)
283+
.font(theme.font())
284+
.size(16)
285+
.wrapping(Wrapping::None)
286+
.color(theme.text_color(1.0));
287+
let subtitle_block = container(
288+
Text::new(self.desc)
289+
.font(theme.font())
290+
.size(13)
291+
.width(Length::Fill)
292+
.align_x(Alignment::End)
293+
.color(theme.text_color(0.55)),
294+
);
240295

241296
let mut row = Row::new()
242297
.align_y(Alignment::Center)
243298
.width(Fill)
244299
.spacing(10)
245-
.height(50);
300+
.height(40);
246301

247-
if theme.show_icons
248-
&& let Some(icon) = &self.icons
249-
{
250-
row = row.push(
251-
container(Viewer::new(icon).height(40).width(40))
252-
.width(40)
253-
.height(40),
254-
);
302+
if theme.show_icons {
303+
row = row.push(container(self.icons.render()).width(30).height(30));
255304
}
256-
row = row.push(container(text_block).width(Fill));
305+
row = row
306+
.push(container(text_block).width(Fill))
307+
.push(subtitle_block);
257308

258309
let name = self.search_name.clone();
259310
let theme_clone = theme.clone();
260311
let is_favourite = self.ranking == -1;
261312
row = row.push(
262313
Button::new(Text::new("♥️").width(Length::Fill).align_x(Alignment::End))
263314
.on_press_with(move || Message::ToggleFavouriteApp(name.clone()))
264-
.width(Length::Fill)
315+
.width(20)
265316
.style(move |_, status| favourite_button_style(&theme_clone, status, is_favourite)),
266317
);
267318

@@ -278,7 +329,7 @@ impl App {
278329
.style(move |_, _| result_button_style(&theme_clone))
279330
.width(Fill)
280331
.padding(0)
281-
.height(50);
332+
.height(40);
282333

283334
container(content)
284335
.id(format!("result-{}", id_num))

src/app/tile.rs

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ use iced::futures::SinkExt;
1818
use iced::futures::channel::mpsc::{Sender, channel};
1919
use iced::keyboard::Modifiers;
2020
use iced::{
21-
Subscription, Theme, futures,
21+
Subscription, futures,
2222
keyboard::{self, key::Named},
2323
stream,
2424
};
@@ -232,8 +232,8 @@ impl Hotkeys {
232232

233233
impl Tile {
234234
/// This returns the theme of the window
235-
pub fn theme(&self, _: window::Id) -> Option<Theme> {
236-
Some(self.theme.clone())
235+
pub fn theme(&self, _: window::Id) -> Option<iced::Theme> {
236+
Some(self.config.theme.clone().into())
237237
}
238238

239239
/// This handles the subscriptions of the window
@@ -606,7 +606,7 @@ fn handle_file_search() -> impl futures::Stream<Item = Message> {
606606
#[allow(clippy::items_after_test_module)]
607607
mod tests {
608608
use super::*;
609-
use crate::app::apps::{App, AppCommand};
609+
use crate::app::apps::{App, AppCommand, AppIcon};
610610
use crate::commands::Function;
611611
use iced::futures::StreamExt;
612612
use tokio::io::{AsyncWriteExt, duplex};
@@ -618,7 +618,7 @@ mod tests {
618618
"/Applications/{name}.app"
619619
))),
620620
desc: "Application".to_string(),
621-
icons: None,
621+
icons: AppIcon::None,
622622
display_name: name.to_string(),
623623
search_name: name.to_lowercase(),
624624
}

src/app/tile/elm.rs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -177,7 +177,7 @@ pub fn view(tile: &Tile, wid: window::Id) -> Element<'_, Message> {
177177
Page::ClipboardHistory => 385,
178178
// Height of each emoji is EMOJI_HEIGHT + 20 for padding
179179
Page::EmojiSearch => std::cmp::min(tile.results.len().div_ceil(6) * 90, 290),
180-
_ => std::cmp::min(tile.results.len() * 60, 290),
180+
_ => std::cmp::min(tile.results.len() * 56, 290),
181181
};
182182

183183
let theme = tile.config.theme.clone();
@@ -216,14 +216,15 @@ pub fn view(tile: &Tile, wid: window::Id) -> Element<'_, Message> {
216216
text_color: None,
217217
background: None,
218218
border: iced::Border {
219-
color: Color::TRANSPARENT,
219+
color: Color::WHITE,
220220
width: 0.,
221221
radius: Radius::new(15),
222222
},
223223
..Default::default()
224224
});
225225

226226
container(contents)
227+
.padding(10)
227228
.style(|_| contents_style(&tile.config.theme))
228229
.into()
229230
} else {

0 commit comments

Comments
 (0)