Skip to content

Commit

Permalink
#698 Make editor Cmd+C and Cmd+X work on macOS without affecting othe…
Browse files Browse the repository at this point in the history
…r windows
  • Loading branch information
helgoboss committed Oct 7, 2022
1 parent 19fe4d0 commit a098db8
Show file tree
Hide file tree
Showing 7 changed files with 168 additions and 24 deletions.
46 changes: 46 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

16 changes: 11 additions & 5 deletions main/src/domain/accelerator.rs
Expand Up @@ -45,12 +45,14 @@ where
if filter_out_event {
TranslateAccelResult::Eat
} else if msg.stroke().accelerator_key() == ESCAPE_KEY {
// Don't process escape raw. We want the normal close behavior. Especially important
// for the floating ReaLearn FX window where closing the main panel would not close
// the surrounding floating window.
// Don't process escape in special ways. We want the normal close behavior. Especially
// important for the floating ReaLearn FX window where closing the main panel would not
// close the surrounding floating window.
TranslateAccelResult::NotOurWindow
} else if self.snitch.focused_realearn_window().is_some() {
} else if let Some(w) = self.snitch.focused_realearn_window() {
if cfg!(target_os = "macos") {
// We are in an egui window. In that case, we need to

// Only ProcessEventRaw seems to work when pressing Return key in an egui text edit.
// Everything else breaks the complete text field, it doesn't receive input anymore.
// TODO-high Problem: It seems only "Eat" prevents the key from triggering
Expand All @@ -63,7 +65,11 @@ where
// Interesting, macOS always shows the menu of the windows in focus, it seems. And
// when having the mapping panel or a child of it in focus, it shows *REAPER's*
// menu. Maybe the issue is that the mapping panel is a child of the REAPER window.
TranslateAccelResult::ProcessEventRaw
if w.process_current_app_event_if_no_text_field() {
TranslateAccelResult::Eat
} else {
TranslateAccelResult::NotOurWindow
}
} else {
TranslateAccelResult::ForcePassOnToWindow
}
Expand Down
@@ -1,4 +1,4 @@
<REAPER_PROJECT 0.1 "6.56+dev0425/macOS-arm64" 1665068795
<REAPER_PROJECT 0.1 "6.56+dev0425/macOS-arm64" 1665163841
<NOTES 0 2
>
RIPPLE 0
Expand Down Expand Up @@ -103,7 +103,7 @@
ISBUS 0 0
BUSCOMP 0 0 0 0 0
SHOWINMIX 1 0.6667 0.5 1 0.5 -1 -1 -1
SEL 1
SEL 0
REC 1 5088 1 0 0 0 0
VU 2
TRACKHEIGHT 115 0 0 0 0 0
Expand All @@ -121,25 +121,25 @@
DOCKED 0
BYPASS 0 0 0
<VST "VSTi: ReaLearn (Helgoboss)" realearn.vst.dylib 0 "" 1751282284<5653546862726C7265616C6561726E00> ""
bHJiaO5e7f4CAAAAAQAAAAAAAAACAAAAAAAAAAAAAABABQAAAQAAAAAAEAA=
bHJiaO5e7f4CAAAAAQAAAAAAAAACAAAAAAAAAAAAAAAyBQAAAQAAAAAAEAA=
eyJ2ZXJzaW9uIjoiMi4xNC4wLXByZS41IiwiaWQiOiJwQVhzRTZNZyIsInN0YXlBY3RpdmVXaGVuUHJvamVjdEluQmFja2dyb3VuZCI6Ik9ubHlJZkJhY2tncm91bmRQ
cm9qZWN0SXNSdW5uaW5nIiwic2VuZEZlZWRiYWNrT25seUlmQXJtZWQiOnRydWUsImRlZmF1bHRHcm91cCI6e30sImRlZmF1bHRDb250cm9sbGVyR3JvdXAiOnt9LCJt
YXBwaW5ncyI6W3siaWQiOiJXSFJYei1URWo0UGZ1clVYZkZ1bXciLCJuYW1lIjoiZGRkIiwidGFncyI6WyJ3d2QiXSwic291cmNlIjp7ImlzUmVnaXN0ZXJlZCI6ZmFs
c2UsImlzMTRCaXQiOmZhbHNlLCJvc2NBcmdJbmRleCI6MH0sIm1vZGUiOnsibWluVGFyZ2V0VmFsdWUiOjAuNDUsIm1heFN0ZXBTaXplIjowLjA1LCJtaW5TdGVwRmFj
dG9yIjoxLCJtYXhTdGVwRmFjdG9yIjo1LCJlZWxDb250cm9sVHJhbnNmb3JtYXRpb24iOiJ5ID0gcmVsX3RpbWUgPCA1MDAgPyBub25lIDogcmVsX3RpbWUgPiA1MDAw
ID8gc3RvcCgxLjApIDogKHNpbihyZWxfdGltZSAvIDUwMCkgKyAxKSAvIDIiLCJlZWxGZWVkYmFja1RyYW5zZm9ybWF0aW9uIjoiYWFhIiwiZmVlZGJhY2tUeXBlIjox
fSwidGFyZ2V0Ijp7InR5cGUiOjUzLCJmeEFuY2hvciI6ImlkIiwidXNlU2VsZWN0aW9uR2FuZ2luZyI6ZmFsc2UsInVzZVRyYWNrR3JvdXBpbmciOmZhbHNlLCJzZWVr
QmVoYXZpb3IiOiJJbW1lZGlhdGUiLCJ1c2VQcm9qZWN0Ijp0cnVlLCJtb3ZlVmlldyI6dHJ1ZSwic2Vla1BsYXkiOnRydWUsIm9zY0FyZ0luZGV4IjowLCJtb3VzZUFj
dGlvbiI6eyJraW5kIjoiTW92ZVRvIiwiYXhpcyI6IlkifSwidGFrZU1hcHBpbmdTbmFwc2hvdCI6eyJraW5kIjoiTGFzdExvYWRlZCJ9fX0seyJpZCI6IkZBWFppRlZR
bEllRFFhYVF0RV9YaiIsIm5hbWUiOiIyIiwic291cmNlIjp7ImNhdGVnb3J5IjoibmV2ZXIiLCJpc1JlZ2lzdGVyZWQiOmZhbHNlLCJpczE0Qml0IjpmYWxzZSwib3Nj
QXJnSW5kZXgiOjB9LCJtb2RlIjp7Im1heFN0ZXBTaXplIjowLjA1LCJtaW5TdGVwRmFjdG9yIjoxLCJtYXhTdGVwRmFjdG9yIjo1fSwidGFyZ2V0Ijp7InR5cGUiOjUz
LCJmeEFuY2hvciI6ImlkIiwidXNlU2VsZWN0aW9uR2FuZ2luZyI6ZmFsc2UsInVzZVRyYWNrR3JvdXBpbmciOmZhbHNlLCJzZWVrQmVoYXZpb3IiOiJJbW1lZGlhdGUi
LCJ1c2VQcm9qZWN0Ijp0cnVlLCJtb3ZlVmlldyI6dHJ1ZSwic2Vla1BsYXkiOnRydWUsIm9zY0FyZ0luZGV4IjowLCJtb3VzZUFjdGlvbiI6eyJraW5kIjoiTW92ZVRv
IiwiYXhpcyI6IlkifSwidGFrZU1hcHBpbmdTbmFwc2hvdCI6eyJraW5kIjoiTGFzdExvYWRlZCJ9fX1dLCJpbnN0YW5jZUZ4Ijp7ImFkZHJlc3MiOiJGb2N1c2VkIn19
YXBwaW5ncyI6W3siaWQiOiJXSFJYei1URWo0UGZ1clVYZkZ1bXciLCJuYW1lIjoiZGRkZGRkIiwidGFncyI6WyJ3d2QiXSwic291cmNlIjp7ImlzUmVnaXN0ZXJlZCI6
ZmFsc2UsImlzMTRCaXQiOmZhbHNlLCJvc2NBcmdJbmRleCI6MH0sIm1vZGUiOnsibWluVGFyZ2V0VmFsdWUiOjAuNDUsIm1heFN0ZXBTaXplIjowLjA1LCJtaW5TdGVw
RmFjdG9yIjoxLCJtYXhTdGVwRmFjdG9yIjo1LCJlZWxDb250cm9sVHJhbnNmb3JtYXRpb24iOiJyZWxfdGltZTtcbmJsYSA9IGJsYSArPSAwLjAxO1xueSA9ICgoYmxh
ICogMTAwKSAlIDEwMCkgKiAwLjAxICogcmFuZCgpIiwiZWVsRmVlZGJhY2tUcmFuc2Zvcm1hdGlvbiI6ImFhYSIsImZlZWRiYWNrVHlwZSI6MX0sInRhcmdldCI6eyJ0
eXBlIjo1MywiZnhBbmNob3IiOiJpZCIsInVzZVNlbGVjdGlvbkdhbmdpbmciOmZhbHNlLCJ1c2VUcmFja0dyb3VwaW5nIjpmYWxzZSwic2Vla0JlaGF2aW9yIjoiSW1t
ZWRpYXRlIiwidXNlUHJvamVjdCI6dHJ1ZSwibW92ZVZpZXciOnRydWUsInNlZWtQbGF5Ijp0cnVlLCJvc2NBcmdJbmRleCI6MCwibW91c2VBY3Rpb24iOnsia2luZCI6
Ik1vdmVUbyIsImF4aXMiOiJZIn0sInRha2VNYXBwaW5nU25hcHNob3QiOnsia2luZCI6Ikxhc3RMb2FkZWQifX19LHsiaWQiOiJGQVhaaUZWUWxJZURRYWFRdEVfWGoi
LCJuYW1lIjoiMiIsInNvdXJjZSI6eyJjYXRlZ29yeSI6Im5ldmVyIiwiaXNSZWdpc3RlcmVkIjpmYWxzZSwiaXMxNEJpdCI6ZmFsc2UsIm9zY0FyZ0luZGV4IjowfSwi
bW9kZSI6eyJtYXhTdGVwU2l6ZSI6MC4wNSwibWluU3RlcEZhY3RvciI6MSwibWF4U3RlcEZhY3RvciI6NX0sInRhcmdldCI6eyJ0eXBlIjo1MywiZnhBbmNob3IiOiJp
ZCIsInVzZVNlbGVjdGlvbkdhbmdpbmciOmZhbHNlLCJ1c2VUcmFja0dyb3VwaW5nIjpmYWxzZSwic2Vla0JlaGF2aW9yIjoiSW1tZWRpYXRlIiwidXNlUHJvamVjdCI6
dHJ1ZSwibW92ZVZpZXciOnRydWUsInNlZWtQbGF5Ijp0cnVlLCJvc2NBcmdJbmRleCI6MCwibW91c2VBY3Rpb24iOnsia2luZCI6Ik1vdmVUbyIsImF4aXMiOiJZIn0s
InRha2VNYXBwaW5nU25hcHNob3QiOnsia2luZCI6Ikxhc3RMb2FkZWQifX19XSwiaW5zdGFuY2VGeCI6eyJhZGRyZXNzIjoiRm9jdXNlZCJ9fQ==
AFByb2dyYW0gMQAQAAAA
>
PRESETNAME "Program 1"
FLOAT 529 9 752 675
FLOATPOS 529 9 752 675
FXID {6F438474-5F03-6045-9CE0-C8572C4F4492}
WAK 0 0
>
Expand Down
3 changes: 3 additions & 0 deletions swell-ui/Cargo.toml
Expand Up @@ -15,5 +15,8 @@ raw-window-handle = "0.4.2"
winapi = { version = "0.3", features = ["winuser", "uxtheme"] }
palette = "0.5"

[target.'cfg(target_os = "macos")'.dependencies]
objc2 = "0.3.0-beta.3"

[build-dependencies]
bindgen = "0.52.0"
3 changes: 3 additions & 0 deletions swell-ui/src/lib.rs
Expand Up @@ -20,3 +20,6 @@ mod string_types;
pub use string_types::*;

pub mod menu_tree;

#[cfg(target_os = "macos")]
mod macos;
75 changes: 75 additions & 0 deletions swell-ui/src/macos.rs
@@ -0,0 +1,75 @@
use objc2::foundation::{MainThreadMarker, NSObject};
use objc2::rc::{Id, Shared};
use objc2::{class, extern_class, extern_methods, msg_send, msg_send_id, ClassType};

extern_class!(
#[derive(Debug, PartialEq, Eq, Hash)]
pub(crate) struct NSApplication;

unsafe impl ClassType for NSApplication {
#[inherits(NSObject)]
type Super = NSResponder;
}
);

extern_class!(
#[derive(Debug, PartialEq, Eq, Hash)]
pub(crate) struct NSResponder;

unsafe impl ClassType for NSResponder {
type Super = NSObject;
}
);

pub(crate) fn NSApp() -> Id<NSApplication, Shared> {
// TODO: Only allow access from main thread
NSApplication::shared(unsafe { MainThreadMarker::new_unchecked() })
}

extern_class!(
#[derive(Debug, PartialEq, Eq, Hash)]
pub(crate) struct NSEvent;

unsafe impl ClassType for NSEvent {
type Super = NSObject;
}
);

extern_methods!(
unsafe impl NSApplication {
/// This can only be called on the main thread since it may initialize
/// the application and since it's parameters may be changed by the main
/// thread at any time (hence it is only safe to access on the main thread).
pub fn shared(_mtm: MainThreadMarker) -> Id<Self, Shared> {
let app: Option<_> = unsafe { msg_send_id![Self::class(), sharedApplication] };
// SAFETY: `sharedApplication` always initializes the app if it isn't already
unsafe { app.unwrap_unchecked() }
}

pub fn current_event(&self) -> Option<Id<NSEvent, Shared>> {
unsafe { msg_send_id![self, currentEvent] }
}
}
);

extern_class!(
#[derive(Debug, PartialEq, Eq, Hash)]
pub(crate) struct NSView;

unsafe impl ClassType for NSView {
#[inherits(NSObject)]
type Super = NSResponder;
}
);

extern_methods!(
unsafe impl NSView {
#[sel(sendEvent:)]
pub unsafe fn send_event(&self, event: &NSEvent);

pub unsafe fn is_text_field(&self) -> bool {
let cls = class!(NSTextField);
msg_send![self, isKindOfClass: cls]
}
}
);
17 changes: 14 additions & 3 deletions swell-ui/src/window.rs
Expand Up @@ -155,10 +155,21 @@ impl Window {
}
}

pub fn send_key(self, down: bool, code: u16, modifiers: u8) {
let msg = if down { raw::WM_KEYDOWN } else { raw::WM_KEYUP };
/// Lets this window process the current app event if this is not a text field.
///
/// Returns whether it was a text field or not.
#[cfg(target_os = "macos")]
pub fn process_current_app_event_if_no_text_field(self) -> bool {
unsafe {
Swell::get().SendMessage(self.raw, msg, code as _, modifiers as _);
let ns_view: &crate::macos::NSView = std::mem::transmute(self.raw);
if ns_view.is_text_field() {
return false;
}
let app = crate::macos::NSApp();
if let Some(current_event) = app.current_event() {
ns_view.send_event(&current_event);
}
true
}
}

Expand Down

0 comments on commit a098db8

Please sign in to comment.