如何在最顶层的 NSPanel 中获取键盘事件?

Posted

技术标签:

【中文标题】如何在最顶层的 NSPanel 中获取键盘事件?【英文标题】:How do I get keyboard events in the topmost NSPanel? 【发布时间】:2019-07-10 06:31:39 【问题描述】:

我使用 Xamarin 创建了一个应用程序来帮助在线观看电影。它在所有其他窗口的顶部显示字幕。这是使用 NSPanel 完成的,因为这是使其在 MacOS Mojave 上工作的唯一方法。

该应用运行良好。现在我想通过让NSPanel响应键盘事件来改进应用程序,这样我就可以通过键盘来控制应用程序的暂停、播放、后退或前进。

如何在最顶部的NSPanel 中获取键盘事件?

我尝试使用此代码:

NSEvent.AddLocalMonitorForEventsMatchingMask(NSEventMask.KeyDown, KeyboardEventHandler);

private static NSEvent KeyboardEventHandler(NSEvent keyEvent)

    // handle key down events here
    return (keyEvent);

但它仅在应用程序未处于全屏模式时才有效。

完整的SubtitlesViewer-MACOS项目可以在here找到。

下面是创建面板的部分代码:

public override void ViewWillAppear()

    base.ViewWillAppear();
    SetupView();


private void SetupView()
 
    var screenRes = screenResolution();
    int PANEL_HEIGHT = 200;
    subtitlesPanel = new NSPanel
    (
        new CoreGraphics.CGRect(40, 50, screenRes.Width - 80, PANEL_HEIGHT),
        NSWindowStyle.Titled | NSWindowStyle.Closable | NSWindowStyle.Resizable | NSWindowStyle.Miniaturizable | NSWindowStyle.DocModal,
        NSBackingStore.Buffered, true
    )
    
        BackgroundColor = NSColor.FromCalibratedRgba(0, 0, 0, 0.0f),
        ReleasedWhenClosed = true,
        HidesOnDeactivate = false,
        FloatingPanel = true,
        StyleMask = NSWindowStyle.NonactivatingPanel,
        Level = NSWindowLevel.MainMenu - 1,
        IsMovable = true,
        CollectionBehavior = NSWindowCollectionBehavior.CanJoinAllSpaces |
        NSWindowCollectionBehavior.FullScreenAuxiliary
    ;

    subtitlesPanel.OrderFront(null);

    subtitleTextButton = new NSButton(new CoreGraphics.CGRect(40, 0, screenRes.Width - 120, PANEL_HEIGHT-30))
    
        Title = "",
        WantsLayer = true
    ;

    subtitleTextButton.Layer.BackgroundColor = NSColor.Clear.CGColor;

    subtitleTextField = new NSTextField(new CoreGraphics.CGRect(40, 0, screenRes.Width - 120, PANEL_HEIGHT-30))
    
        Alignment = NSTextAlignment.Center
    ;
    subtitleTextField.Cell.Alignment = NSTextAlignment.Center;

    forwardButton = new NSButton(new CoreGraphics.CGRect(0, 0, 40, 30));
    forwardButton.Title = ">>";
    forwardButton.Activated += (object sender, EventArgs e) => 
        subtitlesProvider.Forward();
    ;

    backButton = new NSButton(new CoreGraphics.CGRect(0, 30, 40, 30));
    backButton.Title = "<<";
    backButton.Activated += (object sender, EventArgs e) => 
        subtitlesProvider.Back();
    ;

    startStopButton = new NSButton(new CoreGraphics.CGRect(0, 60, 40, 30));
    startStopButton.Title = "Play";
    startStopButton.Activated += (object sender, EventArgs e) => 
        subtitlesProvider.StartStop(subtitlesProvider.Playing);
    ;

    subtitlesPanel.ContentView.AddSubview(subtitleTextButton, NSWindowOrderingMode.Below, null);
    subtitlesPanel.ContentView.AddSubview(subtitleTextField, NSWindowOrderingMode.Below, null);

    subtitlesPanel.ContentView.AddSubview(forwardButton, NSWindowOrderingMode.Below, null);
    subtitlesPanel.ContentView.AddSubview(backButton, NSWindowOrderingMode.Below, null);
    subtitlesPanel.ContentView.AddSubview(startStopButton, NSWindowOrderingMode.Below, null);

    SetupSubtitlesProvider();

请告知我还应该尝试什么使其工作。

【问题讨论】:

我已经删除了客观的 C 标签,因为这里或 Xamarin 中没有这样的语言,我建议你从文本中删除,除非你做了一些非常不寻常的事情,在这种情况下你应该解释什么。 感谢@IvanIčin 和所有帮助我编辑这篇文章的人。 【参考方案1】:

我在这里找到了解决方案: keyDown not being called

这是我实现的 NSPanelExt 类来处理键。

public class NSPanelExt : NSPanel

    public KeyPressedHandler KeyPressed;
    public delegate void KeyPressedHandler(KeyCodeEventArgs e);

    public NSPanelExt(CGRect contentRect, NSWindowStyle aStyle, NSBackingStore bufferingType, bool deferCreation) : base(contentRect, aStyle, bufferingType, deferCreation)
    
    

    public override bool CanBecomeMainWindow => true;

    public override bool CanBecomeKeyWindow => true;

    public override bool AcceptsFirstResponder()
    
        return true;
    


    public override void KeyDown(NSEvent theEvent)
    
        // this function is never called
        KeyPressed?.Invoke(new KeyCodeEventArgs   Key = GetKeyCode(theEvent.KeyCode) );

    

    private KeyCode GetKeyCode(ushort keyCode)
    
        KeyCode result = KeyCode.Unknown;
        switch (keyCode)
        
            case 123:
                result = KeyCode.Left;
                break;
            case 49:
                result = KeyCode.Space;
                break;
            case 124:
                result = KeyCode.Right;
                break;
            case 53:
                result = KeyCode.Esc;
                break;
        

        return result;
    

我还更新了 ViewController 以保持 NSPanel 始终处于活动状态。

public partial class ViewController : NSViewController
    
        // ...
        private NSButton startStopButton;
        Timer _timer = new Timer();

        private void SetupView()
         
        // ...
    subtitlesPanel.KeyPressed += SubtitlesPanel_KeyPressed;

        // ...   
            IntializeKeepWindowFocusedTimer();
        

        void SubtitlesPanel_KeyPressed(KeyCodeEventArgs e)
        
            switch(e.Key)
            
                case KeyCode.Left:
                    backButton.PerformClick(this);
                    break;
                case KeyCode.Right:
                    forwardButton.PerformClick(this);
                    break;
                case KeyCode.Space:
                    startStopButton.PerformClick(this);
                        break;
                case KeyCode.Esc:
                    _timer.Stop();
                    break;
            
        


        private void IntializeKeepWindowFocusedTimer()
        
            _timer.Interval = 200;  //in milliseconds
            _timer.Elapsed += Timer_Elapsed;;
            _timer.AutoReset = true;
            _timer.Enabled = true;
        

        void Timer_Elapsed(object sender, ElapsedEventArgs e)
        
            NSApplication.SharedApplication.BeginInvokeOnMainThread(() =>
            
                subtitlesPanel.MakeKeyWindow();
                if (SetSubtitleNeeded)
                
                    subtitlesProvider.SetSubTitle(0);
                    startStopButton.Title = "Stop";
                    SetSubtitleNeeded = false;
                    _timer.Interval = 5000;
                
            );
        

        private bool SetSubtitleNeeded = false;

        partial void ClickedButton(NSObject sender)
        
            _timer.Stop();
            var nsUrl = subtitleFileSelector.GetFile();
            if (nsUrl == null)
                return;

            fileName = nsUrl.Path;
            subtitlesProvider.ReadFromFile(fileName);
            SetSubtitleNeeded = true;
            _timer.Start();
        

【讨论】:

以上是关于如何在最顶层的 NSPanel 中获取键盘事件?的主要内容,如果未能解决你的问题,请参考以下文章

如何将 Qt::Key 转换为键盘键名?

javascript如何获取键盘事件按下

在 NSPanel 中初始化控件

WPF用键盘左右键按下获取按钮焦点,离开执行按钮事件

java中如何获取网页中鼠标点击过的事件

Android webview中的js如何监听键盘事件