覆盖窗口关闭行为

发布于 2024-12-04 05:22:29 字数 810 浏览 1 评论 0原文

我想捕获所有关闭某些特定现有 Cocoa 窗口并添加一些自己的处理程序的尝试(这可能确实会关闭它或执行不同的操作)。

我想到了不同的解决方案来做到这一点。一是:

我想在运行时用自己的关闭小部件替换现有 Cocoa 窗口的窗口关闭按钮,我可以在其中添加一些自己的代码。

现在,我有这段代码:

import objc
_NSThemeCloseWidget = objc.lookUpClass("_NSThemeCloseWidget")

def find_close_widget(window):
    contentView = window.contentView()
    grayFrame = contentView.superview()
    for i in range(len(grayFrame.subviews())):
        v = grayFrame.subviews()[i]
        if isinstance(v, _NSThemeCloseWidget):
            return v, i, grayFrame

class CustomCloseWidget(_NSThemeCloseWidget):
    pass

def replace_close_widget(window, clazz=CustomCloseWidget):
    v, i, grayFrame = find_close_widget(window)
    newv = clazz.alloc().init()
    grayFrame.subviews()[i] = newv

但是,这似乎不太正确。 (它崩溃了。)

I want to catch all tries to close some specific existing Cocoa window and add some own handler (which might indeed really close it or do something different).

I had different solutions in mind to do this. One was:

I want to replace the window close button of an existing Cocoa window at runtime with an own close widget where I can add some own code.

Right now, I have this code:

import objc
_NSThemeCloseWidget = objc.lookUpClass("_NSThemeCloseWidget")

def find_close_widget(window):
    contentView = window.contentView()
    grayFrame = contentView.superview()
    for i in range(len(grayFrame.subviews())):
        v = grayFrame.subviews()[i]
        if isinstance(v, _NSThemeCloseWidget):
            return v, i, grayFrame

class CustomCloseWidget(_NSThemeCloseWidget):
    pass

def replace_close_widget(window, clazz=CustomCloseWidget):
    v, i, grayFrame = find_close_widget(window)
    newv = clazz.alloc().init()
    grayFrame.subviews()[i] = newv

However, this doesn't seem quite right. (It crashes.)

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

时光礼记 2024-12-11 05:22:29

关闭小部件并不是关闭窗口的唯一方法。有一个公共 API 可以获取该小部件,因此您无需仔细检查框架视图的子视图,但无论如何这是错误的路径。

正确的方法是让一个对象成为窗口的委托,并且 干扰窗口关闭 那里。理想情况下,您应该在创建窗口和订购窗口之间设置窗口的委托。

The close widget isn't the only way to close the window. There's a public API to obtain the widget, so you don't need to go rifling through the frame view's subviews, but that's the wrong path anyway.

The right way is to make an object to be the window's delegate, and interfere with the window's closure there. Ideally, you should set the window's delegate in between creating the window and ordering it in.

帅哥哥的热头脑 2024-12-11 05:22:29

我现在要走另一条路。这部分与 Chrome 相关,但它可以很容易地在其他地方采用。我想尽早捕获关闭窗口的几个操作,以避免任何其他清理或其他导致窗口处于奇怪状态的操作。

def check_close_callback(obj):
    # check ...
    return True # or:
    return False

import objc
BrowserWindowController = objc.lookUpClass("BrowserWindowController")

# copied from objc.signature to avoid warning
def my_signature(signature, **kw):
    from objc._objc import selector
    kw['signature'] = signature
    def makeSignature(func):
        return selector(func, **kw)
    return makeSignature

windowWillCloseSig = "c12@0:4@8" # BrowserWindowController.windowWillClose_.signature
commandDispatchSig = "v12@0:4@8"
class BrowserWindowController(objc.Category(BrowserWindowController)):
    @my_signature(windowWillCloseSig)
    def myWindowShouldClose_(self, sender):
        print "myWindowShouldClose", self, sender
        if not check_close_callback(self): return objc.NO
        return self.myWindowShouldClose_(sender) # this is no recursion when we exchanged the methods

    @my_signature(commandDispatchSig)
    def myCommandDispatch_(self, cmd):
        try: print "myCommandDispatch_", self, cmd
        except: pass # like <type 'exceptions.UnicodeEncodeError'>: 'ascii' codec can't encode character u'\u2026' in position 37: ordinal not in range(128)
        if cmd.tag() == 34015: # IDC_CLOSE_TAB
            if not check_close_callback(self): return           
        self.myCommandDispatch_(cmd)

from ctypes import *
capi = pythonapi

# id objc_getClass(const char *name)
capi.objc_getClass.restype = c_void_p
capi.objc_getClass.argtypes = [c_char_p]

# SEL sel_registerName(const char *str)
capi.sel_registerName.restype = c_void_p
capi.sel_registerName.argtypes = [c_char_p]

def capi_get_selector(name):
    return c_void_p(capi.sel_registerName(name))

# Method class_getInstanceMethod(Class aClass, SEL aSelector)
# Will also search superclass for implementations.
capi.class_getInstanceMethod.restype = c_void_p
capi.class_getInstanceMethod.argtypes = [c_void_p, c_void_p]

# void method_exchangeImplementations(Method m1, Method m2)
capi.method_exchangeImplementations.restype = None
capi.method_exchangeImplementations.argtypes = [c_void_p, c_void_p]

def method_exchange(className, origSelName, newSelName):
    clazz = capi.objc_getClass(className)
    origMethod = capi.class_getInstanceMethod(clazz, capi_get_selector(origSelName))
    newMethod = capi.class_getInstanceMethod(clazz, capi_get_selector(newSelName))
    capi.method_exchangeImplementations(origMethod, newMethod)

def hook_into_windowShouldClose():
    method_exchange("BrowserWindowController", "windowShouldClose:", "myWindowShouldClose:")

def hook_into_commandDispatch():
    method_exchange("BrowserWindowController", "commandDispatch:", "myCommandDispatch:")

此代码来自此处这里

I am going another route now. This is partly Chrome related but it can easily be adopted elsewhere. I wanted to catch several actions for closing the window as early as possible to avoid any other cleanups or so which resulted in the window being in a strange state.

def check_close_callback(obj):
    # check ...
    return True # or:
    return False

import objc
BrowserWindowController = objc.lookUpClass("BrowserWindowController")

# copied from objc.signature to avoid warning
def my_signature(signature, **kw):
    from objc._objc import selector
    kw['signature'] = signature
    def makeSignature(func):
        return selector(func, **kw)
    return makeSignature

windowWillCloseSig = "c12@0:4@8" # BrowserWindowController.windowWillClose_.signature
commandDispatchSig = "v12@0:4@8"
class BrowserWindowController(objc.Category(BrowserWindowController)):
    @my_signature(windowWillCloseSig)
    def myWindowShouldClose_(self, sender):
        print "myWindowShouldClose", self, sender
        if not check_close_callback(self): return objc.NO
        return self.myWindowShouldClose_(sender) # this is no recursion when we exchanged the methods

    @my_signature(commandDispatchSig)
    def myCommandDispatch_(self, cmd):
        try: print "myCommandDispatch_", self, cmd
        except: pass # like <type 'exceptions.UnicodeEncodeError'>: 'ascii' codec can't encode character u'\u2026' in position 37: ordinal not in range(128)
        if cmd.tag() == 34015: # IDC_CLOSE_TAB
            if not check_close_callback(self): return           
        self.myCommandDispatch_(cmd)

from ctypes import *
capi = pythonapi

# id objc_getClass(const char *name)
capi.objc_getClass.restype = c_void_p
capi.objc_getClass.argtypes = [c_char_p]

# SEL sel_registerName(const char *str)
capi.sel_registerName.restype = c_void_p
capi.sel_registerName.argtypes = [c_char_p]

def capi_get_selector(name):
    return c_void_p(capi.sel_registerName(name))

# Method class_getInstanceMethod(Class aClass, SEL aSelector)
# Will also search superclass for implementations.
capi.class_getInstanceMethod.restype = c_void_p
capi.class_getInstanceMethod.argtypes = [c_void_p, c_void_p]

# void method_exchangeImplementations(Method m1, Method m2)
capi.method_exchangeImplementations.restype = None
capi.method_exchangeImplementations.argtypes = [c_void_p, c_void_p]

def method_exchange(className, origSelName, newSelName):
    clazz = capi.objc_getClass(className)
    origMethod = capi.class_getInstanceMethod(clazz, capi_get_selector(origSelName))
    newMethod = capi.class_getInstanceMethod(clazz, capi_get_selector(newSelName))
    capi.method_exchangeImplementations(origMethod, newMethod)

def hook_into_windowShouldClose():
    method_exchange("BrowserWindowController", "windowShouldClose:", "myWindowShouldClose:")

def hook_into_commandDispatch():
    method_exchange("BrowserWindowController", "commandDispatch:", "myCommandDispatch:")

This code is from here and here.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文