Skip to content

Commit 674f3fe

Browse files
authored
Merge pull request #237 from RustCastLabs/favourited-apps
add a favourited apps ability
2 parents be27ca0 + badf748 commit 674f3fe

8 files changed

Lines changed: 119 additions & 21 deletions

File tree

src/app.rs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ use std::collections::HashMap;
33

44
use crate::app::apps::{App, AppCommand, ICNS_ICON};
55
use crate::commands::Function;
6-
use crate::config::{Config, Shelly};
6+
use crate::config::{Config, MainPage, Shelly};
77
use crate::debounce::DebouncePolicy;
88
use crate::utils::icns_data_to_handle;
99
use crate::{app::tile::ExtSender, clipboard::ClipBoardContentType};
@@ -82,6 +82,7 @@ pub enum Message {
8282
WriteConfig(bool),
8383
SaveRanking,
8484
LoadRanking,
85+
ToggleFavouriteApp(String),
8586
UpdateAvailable,
8687
ResizeWindow(Id, f32),
8788
OpenWindow,
@@ -125,7 +126,7 @@ pub enum SetConfigFields {
125126
SearchUrl(String),
126127
HapticFeedback(bool),
127128
ShowMenubarIcon(bool),
128-
AutoSuggest(bool),
129+
SetPage(MainPage),
129130
Modes(Editable<(String, String)>),
130131
Aliases(Editable<(String, String)>),
131132
SearchDirs(Editable<String>),

src/app/apps.rs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -211,6 +211,14 @@ impl App {
211211
}
212212
row = row.push(container(text_block).width(Fill));
213213

214+
let name = self.search_name.clone();
215+
let theme_clone = theme.clone();
216+
row = row.push(
217+
Button::new("♥️")
218+
.on_press_with(move || Message::ToggleFavouriteApp(name.clone()))
219+
.style(move |_, _| result_button_style(&theme_clone)),
220+
);
221+
214222
let msg = on_press.or(match self.open_command.clone() {
215223
AppCommand::Function(func) => Some(Message::RunFunction(func)),
216224
AppCommand::Message(msg) => Some(msg),

src/app/pages/settings.rs

Lines changed: 35 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -6,16 +6,19 @@ use iced::widget::Slider;
66
use iced::widget::Space;
77
use iced::widget::TextInput;
88
use iced::widget::checkbox;
9+
use iced::widget::radio;
910
use iced::widget::text_input;
1011

1112
use crate::app::Editable;
1213
use crate::app::SetConfigBufferFields;
1314
use crate::app::SetConfigThemeFields;
1415
use crate::commands::Function;
16+
use crate::config::MainPage;
1517
use crate::config::Shelly;
1618
use crate::styles::delete_button_style;
1719
use crate::styles::settings_add_button_style;
1820
use crate::styles::settings_checkbox_style;
21+
use crate::styles::settings_radio_button_style;
1922
use crate::styles::settings_save_button_style;
2023
use crate::styles::settings_slider_style;
2124
use crate::styles::settings_text_input_item_style;
@@ -130,16 +133,40 @@ pub fn settings_page(config: Config) -> Element<'static, Message> {
130133
]);
131134

132135
let theme_clone = theme.clone();
133-
let auto_suggest = settings_item_row([
136+
let auto_suggest = settings_item_column([
134137
settings_hint_text(theme.clone(), "Suggestions on open"),
135-
checkbox(config.auto_suggest)
136-
.style(move |_, _| settings_checkbox_style(&theme_clone))
137-
.on_toggle(|input| Message::SetConfig(SetConfigFields::AutoSuggest(input)))
138+
settings_item_row([
139+
radio(
140+
"Favourites",
141+
MainPage::Favourites,
142+
Some(config.main_page),
143+
|page| Message::SetConfig(SetConfigFields::SetPage(page)),
144+
)
145+
.style({
146+
let theme_clone = theme_clone.clone();
147+
move |_, _| settings_radio_button_style(&theme_clone.clone())
148+
})
138149
.into(),
139-
notice_item(
140-
theme.clone(),
141-
"If an empty query should give you your most used actions",
142-
),
150+
radio(
151+
"Frequently Used",
152+
MainPage::FrequentlyUsed,
153+
Some(config.main_page),
154+
|page| Message::SetConfig(SetConfigFields::SetPage(page)),
155+
)
156+
.style({
157+
let theme_clone = theme_clone.clone();
158+
move |_, _| settings_radio_button_style(&theme_clone.clone())
159+
})
160+
.into(),
161+
radio("Nothing", MainPage::Blank, Some(config.main_page), |page| {
162+
Message::SetConfig(SetConfigFields::SetPage(page))
163+
})
164+
.style(move |_, _| settings_radio_button_style(&theme_clone.clone()))
165+
.into(),
166+
])
167+
.spacing(30)
168+
.into(),
169+
notice_item(theme.clone(), "What an empty query should show"),
143170
]);
144171

145172
let theme_clone = theme.clone();

src/app/tile.rs

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -109,6 +109,19 @@ impl AppIndex {
109109
ranked
110110
}
111111

112+
fn get_favourites(&self) -> Vec<App> {
113+
self.by_name
114+
.values()
115+
.filter_map(|x| {
116+
if x.ranking == -1 {
117+
Some(x.to_owned())
118+
} else {
119+
None
120+
}
121+
})
122+
.collect()
123+
}
124+
112125
fn empty() -> AppIndex {
113126
AppIndex {
114127
by_name: HashMap::new(),

src/app/tile/elm.rs

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -189,10 +189,9 @@ pub fn view(tile: &Tile, wid: window::Id) -> Element<'_, Message> {
189189
.height(height as u32);
190190

191191
let text = if tile.query_lc.is_empty() {
192-
if tile.config.auto_suggest && tile.page == Page::Main {
193-
"Frequently used".to_string()
194-
} else {
195-
tile.page.to_string()
192+
match &tile.page {
193+
Page::Main => tile.config.main_page.to_string(),
194+
page => page.to_string(),
196195
}
197196
} else {
198197
match results_count {

src/app/tile/update.rs

Lines changed: 24 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ use crate::app::{Message, Page, tile::Tile};
3232
use crate::calculator::Expr;
3333
use crate::commands::Function;
3434
use crate::config::Config;
35+
use crate::config::MainPage;
3536
use crate::debounce::DebouncePolicy;
3637
use crate::quit::get_open_apps;
3738
use crate::unit_conversion;
@@ -50,7 +51,7 @@ pub fn handle_update(tile: &mut Tile, message: Message) -> Task<Message> {
5051
tile.focused = true;
5152
tile.visible = true;
5253

53-
if tile.page == Page::Main && tile.query_lc.is_empty() && tile.config.auto_suggest {
54+
if tile.page == Page::Main && tile.query_lc.is_empty() {
5455
window::latest()
5556
.map(|x| x.unwrap())
5657
.map(|id| Message::SearchQueryChanged(String::new(), id))
@@ -436,6 +437,21 @@ pub fn handle_update(tile: &mut Tile, message: Message) -> Task<Message> {
436437
])
437438
}
438439

440+
Message::ToggleFavouriteApp(app_name) => {
441+
let ranking = match tile.options.by_name.get(&app_name) {
442+
None => return Task::none(),
443+
Some(app) => {
444+
if app.ranking == -1 {
445+
0
446+
} else {
447+
-1
448+
}
449+
}
450+
};
451+
tile.options.set_ranking(&app_name, ranking);
452+
Task::none()
453+
}
454+
439455
Message::UpdateApps => {
440456
let mut new_options = get_installed_apps(tile.config.theme.show_icons);
441457
new_options.extend(tile.config.shells.iter().map(|x| x.to_app()));
@@ -678,7 +694,7 @@ pub fn handle_update(tile: &mut Tile, message: Message) -> Task<Message> {
678694

679695
SetConfigFields::SearchUrl(url) => final_config.search_url = url,
680696
SetConfigFields::PlaceHolder(placeholder) => final_config.placeholder = placeholder,
681-
SetConfigFields::AutoSuggest(status) => final_config.auto_suggest = status,
697+
SetConfigFields::SetPage(page) => final_config.main_page = page,
682698
SetConfigFields::DebounceDelay(delay) => final_config.debounce_delay = delay,
683699
SetConfigFields::HapticFeedback(haptic_feedback) => {
684700
final_config.haptic_feedback = haptic_feedback
@@ -860,8 +876,12 @@ fn execute_query(tile: &mut Tile, id: Id) -> Task<Message> {
860876
_ => {}
861877
}
862878

863-
if tile.page == Page::Main && tile.query_lc.is_empty() && tile.config.auto_suggest {
864-
tile.results = tile.frequent_results();
879+
if tile.page == Page::Main && tile.query_lc.is_empty() {
880+
tile.results = match tile.config.main_page {
881+
MainPage::FrequentlyUsed => tile.frequent_results(),
882+
MainPage::Blank => vec![],
883+
MainPage::Favourites => tile.options.get_favourites(),
884+
};
865885
return resize_for_results_count(id, tile.results.len());
866886
}
867887

src/config.rs

Lines changed: 21 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ pub struct Config {
2020
pub toggle_hotkey: String,
2121
pub clipboard_hotkey: String,
2222
pub buffer_rules: Buffer,
23+
pub main_page: MainPage,
2324
pub theme: Theme,
2425
pub placeholder: String,
2526
pub search_url: String,
@@ -29,7 +30,6 @@ pub struct Config {
2930
pub modes: HashMap<String, String>,
3031
pub aliases: HashMap<String, String>,
3132
pub search_dirs: Vec<String>,
32-
pub auto_suggest: bool,
3333
pub log_path: String,
3434
pub debounce_delay: u64,
3535
}
@@ -46,7 +46,7 @@ impl Default for Config {
4646
search_url: "https://duckduckgo.com/search?q=%s".to_string(),
4747
haptic_feedback: false,
4848
show_trayicon: true,
49-
auto_suggest: true,
49+
main_page: MainPage::default(),
5050
search_dirs: vec!["~".to_string()],
5151
log_path: "/tmp/rustcast.log".to_string(),
5252
modes: HashMap::new(),
@@ -57,6 +57,25 @@ impl Default for Config {
5757
}
5858
}
5959

60+
#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, Default, Eq, Copy)]
61+
#[serde(rename_all = "lowercase")]
62+
pub enum MainPage {
63+
Favourites,
64+
FrequentlyUsed,
65+
#[default]
66+
Blank,
67+
}
68+
69+
impl std::fmt::Display for MainPage {
70+
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
71+
f.write_str(match self {
72+
MainPage::Blank => "♥️ Rustcast",
73+
MainPage::Favourites => "Favourites",
74+
MainPage::FrequentlyUsed => "Frequently Used",
75+
})
76+
}
77+
}
78+
6079
/// The settings you can set for the theme
6180
#[derive(Debug, Clone, Deserialize, Serialize, PartialEq)]
6281
#[serde(default)]

src/styles.rs

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
use crate::config::Theme as ConfigTheme;
33
use iced::Shadow;
44
use iced::border::Radius;
5-
use iced::widget::{button, checkbox, container, scrollable, slider};
5+
use iced::widget::{button, checkbox, container, radio, scrollable, slider};
66
use iced::{Background, Border, Color, widget::text_input};
77

88
/// Helper: mix base color with white (simple “tint”)
@@ -12,6 +12,7 @@ pub fn tint(mut c: Color, amount: f32) -> Color {
1212
c.b = c.b + (1.0 - c.b) * amount;
1313
c
1414
}
15+
1516
/// Helper: apply alpha
1617
pub fn with_alpha(mut c: Color, a: f32) -> Color {
1718
c.a = a;
@@ -119,6 +120,16 @@ pub fn results_scrollbar_style(tile: &ConfigTheme) -> scrollable::Style {
119120
}
120121
}
121122

123+
pub fn settings_radio_button_style(theme: &ConfigTheme) -> radio::Style {
124+
radio::Style {
125+
background: Background::Color(Color::TRANSPARENT),
126+
dot_color: theme.text_color(0.4),
127+
border_width: 1.,
128+
border_color: theme.text_color(0.7),
129+
text_color: Some(theme.text_color(1.)),
130+
}
131+
}
132+
122133
/// Each rustcast results rows style
123134
pub fn result_row_container_style(tile: &ConfigTheme, focused: bool) -> container::Style {
124135
container::Style {

0 commit comments

Comments
 (0)