Lightningbeam/PyGUI-2.5.3/GUI/Cocoa/Window.py

293 lines
11 KiB
Python

#------------------------------------------------------------------------------
#
# Python GUI - Windows - PyObjC version
#
#------------------------------------------------------------------------------
from Foundation import NSRect, NSPoint, NSSize, NSObject
import AppKit
from AppKit import NSWindow, NSScreen, NSTextView, NSMenu
from GUI import export
from GUI import Globals
from GUI.Utils import NSMultiClass, PyGUI_NS_EventHandler, PyGUI_Flipped_NSView
from GUI import application
from GUI import Event
from GUI.GWindows import Window as GWindow
_default_options_for_style = {
'standard':
{'movable': 1, 'closable': 1, 'hidable': 1, 'resizable': 1},
'nonmodal_dialog':
{'movable': 1, 'closable': 0, 'hidable': 1, 'resizable': 0},
'modal_dialog':
{'movable': 1, 'closable': 0, 'hidable': 0, 'resizable': 0},
'alert':
{'movable': 1, 'closable': 0, 'hidable': 0, 'resizable': 0},
'fullscreen':
{'movable': 0, 'closable': 0, 'hidable': 0, 'resizable': 0},
#{'movable': 1, 'closable': 1, 'hidable': 1, 'resizable': 1},
}
#------------------------------------------------------------------------------
class Window(GWindow):
# _ns_window PyGUI_NSWindow
# _ns_style_mask int
def __init__(self, style = 'standard', zoomable = None, **kwds):
# We ignore zoomable, since it's the same as resizable.
self._style = style
options = dict(_default_options_for_style[style])
for option in ['movable', 'closable', 'hidable', 'resizable']:
if option in kwds:
options[option] = kwds.pop(option)
self._ns_style_mask = self._ns_window_style_mask(**options)
if style == 'fullscreen':
ns_rect = NSScreen.mainScreen().frame()
else:
ns_rect = NSRect(NSPoint(0, 0), NSSize(self._default_width, self._default_height))
ns_window = PyGUI_NSWindow.alloc()
ns_window.initWithContentRect_styleMask_backing_defer_(
ns_rect, self._ns_style_mask, AppKit.NSBackingStoreBuffered, True)
ns_content = PyGUI_NS_ContentView.alloc()
ns_content.initWithFrame_(NSRect(NSPoint(0, 0), NSSize(0, 0)))
ns_content.pygui_component = self
ns_window.setContentView_(ns_content)
ns_window.setAcceptsMouseMovedEvents_(True)
ns_window.setDelegate_(ns_window)
ns_window.pygui_component = self
self._ns_window = ns_window
GWindow.__init__(self, style = style, closable = options['closable'],
_ns_view = ns_window.contentView(), _ns_responder = ns_window,
_ns_set_autoresizing_mask = False,
**kwds)
def _ns_window_style_mask(self, movable, closable, hidable, resizable):
if movable or closable or hidable or resizable:
mask = AppKit.NSTitledWindowMask
if closable:
mask |= AppKit.NSClosableWindowMask
if hidable:
mask |= AppKit.NSMiniaturizableWindowMask
if resizable:
mask |= AppKit.NSResizableWindowMask
else:
mask = AppKit.NSBorderlessWindowMask
return mask
def destroy(self):
#print "Window.destroy:", self ###
self.hide()
app = application()
if app._ns_key_window is self:
app._ns_key_window = None
GWindow.destroy(self)
# We can't drop all references to the NSWindow yet, because this method
# can be called from its windowShouldClose: method, and allowing an
# NSWindow to be released while executing one of its own methods seems
# to be a very bad idea (Cocoa hangs). So we hide the NSWindow and store
# a reference to it in a global. It will be released the next time a
# window is closed and the global is re-used.
global _ns_zombie_window
_ns_zombie_window = self._ns_window
self._ns_window.pygui_component = None
#self._ns_window = None
def get_bounds(self):
ns_window = self._ns_window
ns_frame = ns_window.frame()
(l, y), (w, h) = ns_window.contentRectForFrameRect_styleMask_(
ns_frame, self._ns_style_mask)
b = Globals.ns_screen_height - y
result = (l, b - h, l + w, b)
return result
def set_bounds(self, (l, t, r, b)):
y = Globals.ns_screen_height - b
ns_rect = NSRect(NSPoint(l, y), NSSize(r - l, b - t))
ns_window = self._ns_window
ns_frame = ns_window.frameRectForContentRect_styleMask_(
ns_rect, self._ns_style_mask)
ns_window.setFrame_display_(ns_frame, False)
def get_title(self):
return self._ns_window.title()
def set_title(self, v):
self._ns_window.setTitle_(v)
def get_visible(self):
return self._ns_window.isVisible()
def set_visible(self, v):
# At some mysterious time between creating a window and showing
# it for the first time, Cocoa adjusts its position so that it
# doesn't extend above the menu bar. This is a nuisance for
# our fullscreen windows, so we need to readjust the position
# before showing.
if v:
if self._style == 'fullscreen':
self._ns_window.setFrameOrigin_(NSPoint(0, 0))
self._ns_window.orderFront_(None)
else:
self._ns_window.orderOut_(None)
def _show(self):
self.visible = True
self._ns_window.makeKeyWindow()
def get_target(self):
ns_window = self._ns_window
ns_view = ns_window.firstResponder()
while ns_view and ns_view is not ns_window:
component = Globals._ns_view_to_component.get(ns_view)
if component:
return component
ns_view = ns_view.superview()
return self
def center(self):
self._ns_window.center()
def _stagger(self):
key_win = application()._ns_key_window
if key_win:
(x, y), (w, h) = key_win._ns_window.frame()
p = self._ns_window.cascadeTopLeftFromPoint_(NSPoint(x, y + h))
self._ns_window.setFrameTopLeftPoint_(p)
else:
(x, y), (w, h) = NSScreen.mainScreen().visibleFrame()
ns_vis_topleft = NSPoint(x, y + h)
self._ns_window.setFrameTopLeftPoint_(ns_vis_topleft)
def _document_needs_saving(self, state):
self._ns_window.setDocumentEdited_(state)
#------------------------------------------------------------------------------
class PyGUI_NSWindow(NSWindow, PyGUI_NS_EventHandler):
# pygui_component Window
# resize_delta point or None
__metaclass__ = NSMultiClass
__slots__ = ['pygui_component', 'resize_delta', 'pygui_field_editor']
# pygui_component = None
# resize_delta = None
# pygui_field_editor = None
def _ns_event_position(self, ns_event):
return ns_event.locationInWindow()
def windowShouldClose_(self, sender):
# We use this to detect when the Aqua window closing button
# is pressed, and do the closing ourselves.
self.pygui_component.close_cmd()
return False
# The NSWindow is made its own delegate.
def windowWillResize_toSize_(self, ns_win, new_ns_size):
w0, h0 = self.frame().size
w1, h1 = new_ns_size
self.resize_delta = (w1 - w0, h1 - h0)
return new_ns_size
def windowDidResize_(self, notification):
delta = getattr(self, 'resize_delta', None)
if delta:
self.pygui_component._resized(delta)
self.resize_delta = None
def windowDidBecomeKey_(self, notification):
app = application()
app._ns_key_window = self.pygui_component
app._update_menubar()
def windowDidResignKey_(self, notification):
app = application()
app._ns_key_window = None
app._update_menubar()
def windowWillReturnFieldEditor_toObject_(self, ns_window, ns_obj):
# Return special field editor for newline handling in text fields.
#print "Window: Field editor requested for", object.__repr__(ns_obj) ###
#editor = self.pygui_field_editor
#if not editor:
try:
editor = self.pygui_field_editor
except AttributeError:
#print "...creating new field editor" ###
editor = PyGUI_FieldEditor.alloc().initWithFrame_(
NSRect(NSPoint(0, 0), NSSize(0, 0)))
editor.setFieldEditor_(True)
editor.setDrawsBackground_(False)
self.pygui_field_editor = editor
return editor
# Need the following two methods so that a fullscreen window can become
# the main window. Otherwise it can't, because it has no title bar.
def canBecomeKeyWindow(self):
return self.isVisible()
def canBecomeMainWindow(self):
#print "PyGUI_NSWindow.canBecomeMainWindow"
return self.isVisible()
def windowDidBecomeMain_(self, notification):
#print "PyGUI_NSWindow.windowDidBecomeMain_:", self.pygui_component.title ###
comp = self.pygui_component
if comp and comp._style == 'fullscreen':
#print "...hiding menu bar" ###
NSMenu.setMenuBarVisible_(False)
#self.setFrameOrigin_(NSPoint(0, 0))
def windowDidResignMain_(self, notification):
#print "PyGUI_NSWindow.windowDidResignMain_:", self.pygui_component.title ###
comp = self.pygui_component
if comp and comp._style == 'fullscreen':
#print "...showing menu bar" ###
NSMenu.setMenuBarVisible_(True)
#------------------------------------------------------------------------------
class PyGUI_NS_ContentView(PyGUI_Flipped_NSView, PyGUI_NS_EventHandler):
# pygui_component Window
__metaclass__ = NSMultiClass
__slots__ = ['pygui_component']
def acceptsFirstResponder(self):
return False
#------------------------------------------------------------------------------
class PyGUI_FieldEditorBase(NSTextView):
# Special field editor for use by TextFields. Intercepts
# return key events and handles them our own way.
def keyDown_(self, ns_event):
#print "PyGUI_FieldEditorBase.keyDown_ for", self.pygui_component ###
if ns_event.characters() == "\r":
if self.pygui_component._multiline:
self.insertText_("\n")
return
NSTextView.keyDown_(self, ns_event)
#------------------------------------------------------------------------------
class PyGUI_FieldEditor(PyGUI_FieldEditorBase, PyGUI_NS_EventHandler):
__metaclass__ = NSMultiClass
def get_pygui_component(self):
pygui_nstextfield = self.superview().superview()
component = pygui_nstextfield.pygui_component
component._ns_responder = self
return component
pygui_component = property(get_pygui_component)
export(Window)