first pass of merging in warp (doesn't build)
This commit is contained in:
+82
-62
@@ -1,38 +1,29 @@
|
||||
use galaxyui::{
|
||||
fonts::FamilyId, AddSingletonModel, AppContext, AssetProvider, Entity, ModelContext,
|
||||
SingletonEntity,
|
||||
};
|
||||
use settings::Setting as _;
|
||||
use galaxyui::fonts::FamilyId;
|
||||
use galaxyui::{AddSingletonModel, AppContext, AssetProvider, Entity, ModelContext, SingletonEntity};
|
||||
|
||||
#[cfg(target_os = "macos")]
|
||||
mod macos_app_icon {
|
||||
#[allow(deprecated)]
|
||||
pub use cocoa::{
|
||||
appkit::NSApp,
|
||||
base::{id, nil},
|
||||
};
|
||||
pub use objc2::rc::autoreleasepool;
|
||||
pub use objc2::{AnyThread, MainThreadMarker};
|
||||
pub use objc2_app_kit::{NSApplication, NSImage, NSWorkspace, NSWorkspaceIconCreationOptions};
|
||||
pub use objc2_foundation::{ns_string, NSBundle, NSString};
|
||||
pub use galaxy_core::channel::{Channel, ChannelState};
|
||||
pub use galaxyui::platform::mac::AutoreleasePoolGuard;
|
||||
pub use objc::{class, msg_send, sel, sel_impl};
|
||||
|
||||
pub use crate::settings::app_icon::{AppIcon, AppIconSettings, AppIconSettingsChangedEvent};
|
||||
}
|
||||
use anyhow::anyhow;
|
||||
#[cfg(target_os = "macos")]
|
||||
use macos_app_icon::*;
|
||||
|
||||
use crate::{
|
||||
settings::{
|
||||
active_theme_kind, FontSettings, FontSettingsChangedEvent, MonospaceFontSize, Settings,
|
||||
ThemeSettings,
|
||||
},
|
||||
themes::theme::{GalaxyTheme, ThemeKind},
|
||||
ASSETS,
|
||||
};
|
||||
|
||||
use anyhow::anyhow;
|
||||
|
||||
pub use galaxy_core::ui::appearance::{Appearance, AppearanceEvent};
|
||||
|
||||
use crate::settings::{
|
||||
active_theme_kind, FontSettings, FontSettingsChangedEvent, MonospaceFontSize, Settings,
|
||||
ThemeSettings,
|
||||
};
|
||||
use crate::themes::theme::{ThemeKind, WarpTheme};
|
||||
use crate::ASSETS;
|
||||
|
||||
/// Manages the state of the app-wide Appearance settings, it is responsible
|
||||
/// for 1) listening to settings changes and update the underlying Appearance
|
||||
/// accordingly 2) hold transient theme states that are used when user is switching
|
||||
@@ -48,25 +39,27 @@ pub struct AppearanceManager {
|
||||
|
||||
impl AppearanceManager {
|
||||
pub fn new(ctx: &mut ModelContext<Self>) -> Self {
|
||||
ctx.subscribe_to_model(&ThemeSettings::handle(ctx), move |me, _event, ctx| {
|
||||
ctx.subscribe_to_model(&ThemeSettings::handle(ctx), move |me, _, _event, ctx| {
|
||||
me.refresh_theme_state(ctx);
|
||||
});
|
||||
|
||||
#[cfg(target_os = "macos")]
|
||||
{
|
||||
ctx.subscribe_to_model(
|
||||
&AppIconSettings::handle(ctx),
|
||||
move |me, event, ctx| match event {
|
||||
ctx.subscribe_to_model(&AppIconSettings::handle(ctx), move |me, _, event, ctx| {
|
||||
match event {
|
||||
AppIconSettingsChangedEvent::AppIconState { .. } => {
|
||||
me.set_app_icon(ctx);
|
||||
}
|
||||
},
|
||||
);
|
||||
AppIconSettingsChangedEvent::ShowDockIconState { .. } => {
|
||||
me.apply_dock_icon_visibility(ctx);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
ctx.subscribe_to_model(
|
||||
&FontSettings::handle(ctx),
|
||||
move |_, event, ctx| match event {
|
||||
move |_, _, event, ctx| match event {
|
||||
FontSettingsChangedEvent::MonospaceFontName { .. } => {
|
||||
let (font_name, match_fonts) = {
|
||||
let settings = FontSettings::as_ref(ctx);
|
||||
@@ -180,6 +173,11 @@ impl AppearanceManager {
|
||||
self.app_icon_at_startup
|
||||
}
|
||||
|
||||
#[cfg(target_os = "macos")]
|
||||
pub fn apply_dock_icon_visibility(&self, app: &AppContext) {
|
||||
app.set_dock_icon_visible(*AppIconSettings::as_ref(app).show_dock_icon.value());
|
||||
}
|
||||
|
||||
pub fn clear_transient_theme(&mut self, ctx: &mut ModelContext<Self>) {
|
||||
self.transient_theme = None;
|
||||
self.refresh_theme_state(ctx);
|
||||
@@ -194,7 +192,6 @@ impl AppearanceManager {
|
||||
/// Also see the README.md file in app/DockTilePlugin for more information on how best to test
|
||||
/// changes to the dock tile plugin.
|
||||
#[cfg(target_os = "macos")]
|
||||
#[allow(deprecated)]
|
||||
pub fn set_app_icon(&self, app: &AppContext) {
|
||||
let icon = *AppIconSettings::as_ref(app).app_icon.value();
|
||||
|
||||
@@ -203,14 +200,16 @@ impl AppearanceManager {
|
||||
// settings/autoupdate callbacks whose thread of origin varies. Wrap
|
||||
// the body in a local pool so the autoreleased NSStrings (and any
|
||||
// other temporaries Cocoa hands back) are released when this returns.
|
||||
// `AutoreleasePoolGuard` drains on `Drop`, covering every exit path.
|
||||
unsafe {
|
||||
let _pool = AutoreleasePoolGuard::new();
|
||||
|
||||
let app: id = NSApp();
|
||||
let bundle: id = msg_send![class!(NSBundle), mainBundle];
|
||||
let bundle_path: id = msg_send![bundle, bundlePath];
|
||||
let workspace: id = msg_send![class!(NSWorkspace), sharedWorkspace];
|
||||
// `autoreleasepool` drains when the closure returns, covering every
|
||||
// exit path (including early returns and panics).
|
||||
autoreleasepool(|_| {
|
||||
// SAFETY: `set_app_icon` only runs on the main thread, since it
|
||||
// requires a `&AppContext`, which is only accessible there.
|
||||
let mtm = unsafe { MainThreadMarker::new_unchecked() };
|
||||
let ns_app = NSApplication::sharedApplication(mtm);
|
||||
let bundle = NSBundle::mainBundle();
|
||||
let bundle_path = bundle.bundlePath();
|
||||
let workspace = NSWorkspace::sharedWorkspace();
|
||||
|
||||
// If the user has selected the default icon, reset to the icon that is statically
|
||||
// bundled in the app bundle. The bundled icon gets automatically "filtered" according
|
||||
@@ -219,7 +218,7 @@ impl AppearanceManager {
|
||||
// icons do not adapt to the preferred style.
|
||||
//
|
||||
// Local channel is not bundled, so don't attempt this for that case. This method only
|
||||
// works if the dock tile plugin hasn't overriden the default icon already, so skip
|
||||
// works if the dock tile plugin hasn't overridden the default icon already, so skip
|
||||
// this method if the app started up with a non-default icon, as setting to "nil" would
|
||||
// revert to the icon we started up with. We therefore need to use an in-memory
|
||||
// override to display the default icon. This has the drawback of _not_ inheriting the
|
||||
@@ -229,43 +228,64 @@ impl AppearanceManager {
|
||||
&& self.app_icon_at_startup == AppIcon::Galaxy
|
||||
{
|
||||
log::debug!("User has default icon selected, resetting to bundle default");
|
||||
// Reset to nil to use the bundle's default icon
|
||||
let _: () = msg_send![app, setApplicationIconImage:nil];
|
||||
let _: () = msg_send![workspace, setIcon:nil forFile:bundle_path options:0];
|
||||
let _: () = msg_send![workspace, noteFileSystemChanged:bundle_path];
|
||||
// Reset to nil to use the bundle's default icon.
|
||||
// SAFETY: `setApplicationIconImage:` accepts `nil` to restore the bundled icon.
|
||||
unsafe { ns_app.setApplicationIconImage(None) };
|
||||
workspace.setIcon_forFile_options(
|
||||
None,
|
||||
&bundle_path,
|
||||
NSWorkspaceIconCreationOptions::empty(),
|
||||
);
|
||||
workspace.noteFileSystemChanged_(&bundle_path);
|
||||
return;
|
||||
}
|
||||
|
||||
let icon_name = AppIconSettings::get_base_icon_file_name(icon);
|
||||
|
||||
log::debug!("Setting app icon in memory to: {icon_name}");
|
||||
// Load icon from the compiled-in assets.
|
||||
let asset_path = format!("bundled/png/{icon_name}.png");
|
||||
let Ok(icon_data) = crate::ASSETS.get(&asset_path) else {
|
||||
log::warn!("Failed to load icon asset: {asset_path}");
|
||||
// Locate the plugin bundle.
|
||||
let Some(plugins_path) = bundle.builtInPlugInsPath() else {
|
||||
log::warn!("Failed to get dock tile plugin bundle");
|
||||
return;
|
||||
};
|
||||
let plugin_name = ns_string!("WarpDockTilePlugin.docktileplugin");
|
||||
let plugin_path = plugins_path.stringByAppendingPathComponent(plugin_name);
|
||||
let Some(plugin_bundle) = NSBundle::bundleWithPath(&plugin_path) else {
|
||||
log::warn!("Failed to get dock tile plugin bundle");
|
||||
return;
|
||||
};
|
||||
|
||||
let ns_data: id =
|
||||
msg_send![class!(NSData), dataWithBytes:icon_data.as_ptr() length:icon_data.len()];
|
||||
let image: id = msg_send![class!(NSImage), alloc];
|
||||
let image: id = msg_send![image, initWithData:ns_data];
|
||||
// Read the images from the plugin bundle.
|
||||
let image_name = NSString::from_str(icon_name);
|
||||
let extension = ns_string!("png");
|
||||
let Some(image_path) =
|
||||
plugin_bundle.pathForResource_ofType(Some(&image_name), Some(extension))
|
||||
else {
|
||||
log::warn!("Failed to get image path for icon: {icon_name}");
|
||||
return;
|
||||
};
|
||||
|
||||
if image == nil {
|
||||
// Create the image from the file.
|
||||
let Some(image) = NSImage::initWithContentsOfFile(NSImage::alloc(), &image_path) else {
|
||||
log::warn!("Failed to create image for icon: {icon_name}");
|
||||
return;
|
||||
}
|
||||
};
|
||||
|
||||
// Override the bundled icon with this new image.
|
||||
let _: () = msg_send![app, setApplicationIconImage:image];
|
||||
let _: () = msg_send![workspace, setIcon:image forFile:bundle_path options:0];
|
||||
let _: () = msg_send![workspace, noteFileSystemChanged:bundle_path];
|
||||
// SAFETY: `setApplicationIconImage:` accepts a non-nil image.
|
||||
unsafe { ns_app.setApplicationIconImage(Some(&image)) };
|
||||
workspace.setIcon_forFile_options(
|
||||
Some(&image),
|
||||
&bundle_path,
|
||||
NSWorkspaceIconCreationOptions::empty(),
|
||||
);
|
||||
workspace.noteFileSystemChanged_(&bundle_path);
|
||||
|
||||
// Balance the +1 retain from `[NSImage alloc]`. `setApplicationIconImage:` and
|
||||
// `setIcon:forFile:options:` both retain the image, so it remains alive as the
|
||||
// active app/dock icon after we release our own reference.
|
||||
let _: () = msg_send![image, release];
|
||||
}
|
||||
// `image` is a `Retained<NSImage>` that releases the `+1` retain from
|
||||
// `[NSImage alloc]` when it drops at the end of this scope.
|
||||
// `setApplicationIconImage:` and `setIcon:forFile:options:` both retain the
|
||||
// image, so it remains alive as the active app/dock icon.
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user