Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion src/app.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ use std::collections::HashMap;

use crate::app::apps::{App, AppCommand, ICNS_ICON};
use crate::commands::Function;
use crate::config::{Config, MainPage, Shelly, ThemeMode};
use crate::config::{Config, MainPage, Position, Shelly, ThemeMode};
use crate::debounce::DebouncePolicy;
use crate::platform::macos::launching::Shortcut;
use crate::utils::icns_data_to_handle;
Expand Down Expand Up @@ -177,6 +177,7 @@ pub enum SetConfigFields {
ToDefault,
ToggleHotkey(String),
ClipboardHotkey(String),
SetPosition(Position),
PlaceHolder(String),
SearchUrl(String),
ClipboardHistory(bool),
Expand Down
22 changes: 22 additions & 0 deletions src/app/pages/settings.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,12 +10,16 @@ use iced::widget::Slider;
use iced::widget::Space;
use iced::widget::TextInput;
use iced::widget::button;
use iced::widget::pick_list;
use iced::widget::radio;
use iced::widget::scrollable::Direction;
use iced::widget::scrollable::Scrollbar;
use iced::widget::text_input;
use iced::widget::toggler;

use crate::config::Position;
use crate::styles::picklist_menu_style;
use crate::styles::picklist_style;
use crate::styles::settings_contents_container_style;
use crate::styles::settings_tabs_container_style;
use crate::styles::settings_toggle_style;
Expand Down Expand Up @@ -388,13 +392,31 @@ fn general_tab(config: Box<Config>, theme: crate::config::Theme) -> Column<'stat
.into(),
]));

let theme_clone = theme.clone();
let theme_clone_2 = theme.clone();
let position_dropdown = settings_row_without_reset(settings_item_row([
settings_hint_text(
theme.clone(),
"Window Position",
Some("The position of the window"),
),
Space::new().width(Length::Fill).into(),
pick_list(Position::variants(), Some(config.window_location), |pos| {
Message::SetConfig(SetConfigFields::SetPosition(pos))
})
.style(move |_, status| picklist_style(&theme_clone, status))
.menu_style(move |_| picklist_menu_style(&theme_clone_2))
.into(),
]));

Column::from_iter([
hotkey,
cb_hotkey,
placeholder_setting,
search,
debounce,
start_at_login,
position_dropdown,
auto_update,
haptic,
tray_icon,
Expand Down
2 changes: 1 addition & 1 deletion src/app/tile.rs
Original file line number Diff line number Diff line change
Expand Up @@ -296,7 +296,7 @@ impl Tile {
} else if modifiers.command() {
s.parse::<usize>()
.ok()
.filter(|&n| n >= 1 && n <= 9)
.filter(|&n| (1..=9).contains(&n))
.map(|n| Message::OpenResult((n - 1) as u32))
} else if s == "p" && modifiers.control() {
Some(Message::ChangeFocus(ArrowKey::Up, 1))
Expand Down
9 changes: 7 additions & 2 deletions src/app/tile/elm.rs
Original file line number Diff line number Diff line change
Expand Up @@ -41,8 +41,13 @@ pub fn new(hotkeys: Hotkeys, config: &Config) -> (Tile, Task<Message>) {

let events = Event::get_events(config.event_duration);

let open = open.discard().chain(window::run(id, |handle| {
platform::window_config(&handle.window_handle().expect("Unable to get window handle"));
let pos = config.window_location;

let open = open.discard().chain(window::run(id, move |handle| {
platform::window_config(
&handle.window_handle().expect("Unable to get window handle"),
pos,
);
transform_process_to_ui_element();
}));
info!("MacOS platform config applied");
Expand Down
32 changes: 26 additions & 6 deletions src/app/tile/update.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@ use iced::widget::operation::AbsoluteOffset;
use iced::window;
use iced::window::Id;
use log::info;
use objc2::MainThreadMarker;
use objc2_app_kit::NSApplication;
use rayon::iter::IntoParallelRefIterator;
use rayon::iter::ParallelIterator;
use rayon::slice::ParallelSliceMut;
Expand Down Expand Up @@ -39,11 +41,13 @@ use crate::calculator::Expr;
use crate::commands::Function;
use crate::config::Config;
use crate::config::MainPage;
use crate::config::Position;
use crate::config::ThemeMode;
use crate::debounce::DebouncePolicy;
use crate::platform::macos::events::Event;
use crate::platform::macos::launching::Shortcut;
use crate::platform::macos::launching::global_handler;
use crate::platform::macos::screen_with_mouse;
use crate::platform::macos::{start_at_login, stop_at_login};
use crate::quit::get_open_apps;
use crate::unit_conversion;
Expand Down Expand Up @@ -114,6 +118,19 @@ pub fn handle_update(tile: &mut Tile, message: Message) -> Task<Message> {
tile.focused = true;
tile.visible = true;

let app = NSApplication::sharedApplication(MainThreadMarker::new().unwrap());

if let Some(window) = app.keyWindow()
&& tile.config.window_location != Position::Default
{
let size = window.frame().size;
window.setFrameOrigin(tile.config.window_location.point(
size.width,
size.height,
screen_with_mouse(),
));
};

if tile.page == Page::Main && tile.query_lc.is_empty() {
window::latest()
.map(|x| x.unwrap())
Expand Down Expand Up @@ -496,12 +513,12 @@ pub fn handle_update(tile: &mut Tile, message: Message) -> Task<Message> {
])
}
Message::RunFunction(command) => {
if let Function::TileWindow(pos) = &command {
if let Some(pid) = tile.frontmost.as_ref().map(|a| a.processIdentifier()) {
let ok = crate::platform::macos::window::tile_focused_window(pid, pos);
if !ok && tile.config.haptic_feedback {
perform_haptic(HapticPattern::Alignment);
}
if let Function::TileWindow(pos) = &command
&& let Some(pid) = tile.frontmost.as_ref().map(|a| a.processIdentifier())
{
let ok = crate::platform::macos::window::tile_focused_window(pid, pos);
if !ok && tile.config.haptic_feedback {
perform_haptic(HapticPattern::Alignment);
}
}
command.execute(&tile.config);
Expand Down Expand Up @@ -948,6 +965,9 @@ pub fn handle_update(tile: &mut Tile, message: Message) -> Task<Message> {
SetConfigFields::SetBufferFields(SetConfigBufferFields::ClearOnHide(clear)) => {
final_config.buffer_rules.clear_on_hide = clear;
}
SetConfigFields::SetPosition(pos) => {
final_config.window_location = pos;
}
SetConfigFields::SetBufferFields(SetConfigBufferFields::ClearOnEnter(clear)) => {
final_config.buffer_rules.clear_on_enter = clear
}
Expand Down
86 changes: 80 additions & 6 deletions src/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,9 @@
use std::{collections::HashMap, path::Path, sync::Arc};

use iced::{Font, font::Family, theme::Custom, widget::image::Handle};
use objc2::rc::Retained;
use objc2_app_kit::NSScreen;
use objc2_core_foundation::CGPoint;
use serde::{Deserialize, Serialize};

use crate::{
Expand All @@ -13,6 +16,79 @@ use crate::{
utils::handle_from_icns,
};

#[derive(Debug, Clone, Deserialize, Serialize, Default, Copy, PartialEq)]
pub enum Position {
#[default]
Default,
TopCenter,
TopRight,
TopLeft,
MiddleCenter,
MiddleRight,
MiddleLeft,
BottomCenter,
BottomRight,
BottomLeft,
}

impl Position {
pub fn variants() -> Vec<Self> {
vec![
Position::Default,
Position::TopCenter,
Position::TopRight,
Position::TopLeft,
Position::MiddleCenter,
Position::MiddleRight,
Position::MiddleLeft,
Position::BottomCenter,
Position::BottomRight,
Position::BottomLeft,
]
}

pub fn point(&self, width: f64, height: f64, screen: Retained<NSScreen>) -> CGPoint {
let frame = screen.frame();
let ox = frame.origin.x;
let oy = frame.origin.y;
let sw = frame.size.width;
let sh = frame.size.height;

match self {
Position::Default => CGPoint::new(ox, oy),
Position::TopLeft => CGPoint::new(ox, oy + sh - height),
Position::TopCenter => CGPoint::new(ox + (sw / 2.) - (width / 2.), oy + sh - height),
Position::TopRight => CGPoint::new(ox + sw - width, oy + sh - height),
Position::MiddleLeft => CGPoint::new(ox, oy + (sh / 2.) - (height / 2.)),
Position::MiddleCenter => CGPoint::new(
ox + (sw / 2.) - (width / 2.),
oy + (sh / 2.) - (height / 2.),
),
Position::MiddleRight => CGPoint::new(ox + sw - width, oy + (sh / 2.) - (height / 2.)),
Position::BottomLeft => CGPoint::new(ox, oy),
Position::BottomCenter => CGPoint::new(ox + (sw / 2.) - (width / 2.), oy),
Position::BottomRight => CGPoint::new(ox + sw - width, oy),
}
}
}

impl std::fmt::Display for Position {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Position::Default => write!(f, "Default"),
Position::TopCenter => write!(f, "Top Center"),
Position::TopRight => write!(f, "Top Right"),
Position::TopLeft => write!(f, "Top Left"),
Position::MiddleCenter => write!(f, "Middle Center"),
Position::MiddleRight => write!(f, "Middle Right"),
Position::MiddleLeft => write!(f, "Middle Left"),
Position::BottomCenter => write!(f, "Bottom Center"),
Position::BottomRight => write!(f, "Bottom Right"),
Position::BottomLeft => write!(f, "Bottom Left"),
}
}
}

/// The main config struct (effectively the config file's "schema")
#[derive(Debug, Clone, Deserialize, Serialize, PartialEq)]
#[serde(default)]
Expand All @@ -24,6 +100,7 @@ pub struct Config {
pub main_page: MainPage,
pub start_at_login: bool,
pub theme: Theme,
pub window_location: Position,
pub placeholder: String,
pub search_url: String,
pub haptic_feedback: bool,
Expand Down Expand Up @@ -56,6 +133,7 @@ impl Default for Config {
haptic_feedback: false,
auto_update: true,
show_trayicon: true,
window_location: Position::Default,
main_page: MainPage::default(),
search_dirs: vec!["~".to_string()],
log_path: "/tmp/rustcast.log".to_string(),
Expand Down Expand Up @@ -91,18 +169,14 @@ impl std::fmt::Display for MainPage {
/// The mode for the theme (dark, light, or follow system)
#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, Eq, Copy)]
#[serde(rename_all = "lowercase")]
#[derive(Default)]
pub enum ThemeMode {
#[default]
Dark,
Light,
System,
}

impl Default for ThemeMode {
fn default() -> Self {
ThemeMode::Dark
}
}

impl ThemeMode {
/// Return preset text and background colors for this mode.
pub fn presets(
Expand Down
2 changes: 1 addition & 1 deletion src/platform/macos/launching.rs
Original file line number Diff line number Diff line change
Expand Up @@ -121,7 +121,7 @@ extern "C-unwind" fn keyboard_event_callback(
_ => return unsafe { event.as_mut() },
};

if !data.targets.iter().any(|t| *t == shortcut) {
if !data.targets.contains(&shortcut) {
return unsafe { event.as_mut() };
}

Expand Down
28 changes: 27 additions & 1 deletion src/platform/macos/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,11 @@ pub mod urlscheme;
pub mod window;

use iced::wgpu::rwh::WindowHandle;
use objc2::Message;
use objc2::rc::Retained;
use objc2_app_kit::{NSEvent, NSScreen};

use crate::config::Position;

pub(super) use self::discovery::get_installed_apps;
pub(super) use self::haptics::perform_haptic;
Expand Down Expand Up @@ -48,8 +53,29 @@ pub(super) fn set_activation_policy_accessory() {
app.setActivationPolicy(NSApplicationActivationPolicy::Accessory);
}

pub fn screen_with_mouse() -> Retained<NSScreen> {
use objc2::MainThreadMarker;

let mtm = MainThreadMarker::new().expect("must be on main thread");
let mouse = NSEvent::mouseLocation();
let screens = NSScreen::screens(mtm);

screens
.iter()
.find(|s| {
let f = s.frame();
mouse.x >= f.origin.x
&& mouse.x < f.origin.x + f.size.width
&& mouse.y >= f.origin.y
&& mouse.y < f.origin.y + f.size.height
})
.map(|s| s.retain())
.or_else(|| NSScreen::mainScreen(mtm))
.expect("no screens found")
}

/// This carries out the window configuration for the macos window (only things that are macos specific)
pub(super) fn macos_window_config(handle: &WindowHandle) {
pub(super) fn macos_window_config(handle: &WindowHandle, _position: Position) {
use iced::wgpu::rwh::RawWindowHandle;
use objc2::rc::Retained;
use objc2_app_kit::NSView;
Expand Down
6 changes: 3 additions & 3 deletions src/platform/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
use iced::wgpu::rwh::WindowHandle;

pub use self::cross::default_app_paths;
use crate::app::apps::App;
use crate::{app::apps::App, config::Position};

pub mod cross;
#[cfg(target_os = "macos")]
Expand All @@ -13,9 +13,9 @@ pub fn set_activation_policy_accessory() {
self::macos::set_activation_policy_accessory();
}

pub fn window_config(handle: &WindowHandle) {
pub fn window_config(handle: &WindowHandle, position: Position) {
#[cfg(target_os = "macos")]
self::macos::macos_window_config(handle);
self::macos::macos_window_config(handle, position);
}

pub fn focus_this_app() {
Expand Down
Loading
Loading