mirror of
https://github.com/ultimatepp/ultimatepp.git
synced 2026-05-15 06:05:58 -06:00
407 lines
9.6 KiB
Text
407 lines
9.6 KiB
Text
#include "CocoMM.h"
|
|
|
|
#ifdef GUI_COCOA
|
|
|
|
#define LLOG(x)
|
|
|
|
@implementation CocoWindow
|
|
|
|
- (void)becomeKeyWindow {
|
|
[super becomeKeyWindow];
|
|
}
|
|
|
|
- (BOOL)canBecomeKeyWindow {
|
|
Upp::GuiLock __;
|
|
return active && ctrl && ctrl->IsEnabled();
|
|
}
|
|
|
|
- (BOOL)canBecomeMainWindow {
|
|
Upp::GuiLock __;
|
|
LLOG("canBecomeMainWindow " << Upp::Name(ctrl) << ", owner " << Upp::Name(ctrl->GetOwner()));
|
|
return active && ctrl && ctrl->IsEnabled() && dynamic_cast<Upp::TopWindow *>(~ctrl) && !ctrl->GetOwner();
|
|
}
|
|
|
|
- (NSMenu *)applicationDockMenu:(NSApplication *)sender {
|
|
Upp::GuiLock __;
|
|
NSMenu *menu = [[[NSMenu alloc] initWithTitle:@"DocTile Menu"] autorelease];
|
|
NSMenuItem *item = [[[NSMenuItem alloc] initWithTitle:@"Hello" action:@selector(hello) keyEquivalent:@"k"] autorelease];
|
|
[menu addItem:item];
|
|
return menu;
|
|
}
|
|
|
|
@end
|
|
|
|
namespace Upp {
|
|
|
|
static Vector<Ptr<Ctrl>> mmtopctrl; // should work without Ptr, but let us be defensive....
|
|
|
|
bool Ctrl::always_use_bundled_icon = false;
|
|
|
|
Ctrl *Ctrl::GetOwner()
|
|
{
|
|
GuiLock __;
|
|
return top && GetTop()->coco ? GetTop()->coco->owner : NULL;
|
|
}
|
|
|
|
Ctrl *Ctrl::GetActiveCtrl()
|
|
{
|
|
GuiLock __;
|
|
for(Ctrl *p : mmtopctrl)
|
|
if(p && p->top && p->GetTop()->coco && p->GetTop()->coco->window.keyWindow)
|
|
return p;
|
|
return lastActive;
|
|
}
|
|
|
|
bool Ctrl::SetWndFocus()
|
|
{
|
|
GuiLock __;
|
|
if(top && GetTop()->coco) {
|
|
[GetTop()->coco->window orderFront:GetTop()->coco->window];
|
|
if([GetTop()->coco->window canBecomeKeyWindow])
|
|
[GetTop()->coco->window makeKeyWindow];
|
|
if(dynamic_cast<TopWindow *>(this) && [GetTop()->coco->window canBecomeMainWindow])
|
|
[GetTop()->coco->window makeMainWindow];
|
|
}
|
|
return true;
|
|
}
|
|
|
|
bool Ctrl::HasWndFocus() const
|
|
{
|
|
GuiLock __;
|
|
return GetActiveCtrl() == this;
|
|
}
|
|
|
|
|
|
void Ctrl::SetWndForeground()
|
|
{
|
|
GuiLock __;
|
|
SetWndFocus();
|
|
}
|
|
|
|
bool Ctrl::IsWndForeground() const
|
|
{
|
|
GuiLock __;
|
|
return HasWndFocus();
|
|
}
|
|
|
|
NSRect DesktopRect(const Rect& r)
|
|
{
|
|
double scalei = 1.0 / Upp::Ctrl::SCL(1);
|
|
return NSMakeRect(scalei * r.left,
|
|
scalei * (Ctrl::GetScreenArea(r.TopLeft()).GetHeight() - r.top - r.GetHeight()),
|
|
scalei * r.GetWidth(), scalei * r.GetHeight());
|
|
}
|
|
|
|
void *Ctrl::GetNSWindow() const
|
|
{
|
|
return top && GetTop()->coco ? GetTop()->coco->window : NULL;
|
|
}
|
|
|
|
void *Ctrl::GetNSView() const
|
|
{
|
|
return top && GetTop()->coco ? GetTop()->coco->view : NULL;
|
|
}
|
|
|
|
void Ctrl::DoCancelPreedit()
|
|
{
|
|
[[NSTextInputContext currentInputContext] discardMarkedText];
|
|
}
|
|
|
|
void Ctrl::Create(Ctrl *owner, dword style, bool active)
|
|
{
|
|
cancel_preedit = DoCancelPreedit; // We really need this just once, but whatever..
|
|
|
|
if(owner)
|
|
owner = owner->GetTopCtrl();
|
|
|
|
SetTop(new Top);
|
|
GetTop()->coco = new CocoTop;
|
|
GetTop()->coco->owner = owner;
|
|
|
|
NSRect frame = DesktopRect(GetRect());
|
|
CocoWindow *window = [[CocoWindow alloc] initWithContentRect:frame styleMask: style
|
|
backing:NSBackingStoreBuffered defer:false];
|
|
GetTop()->coco->window = window;
|
|
if(owner && owner->top && owner->GetTop()->coco)
|
|
[owner->GetTop()->coco->window addChildWindow:window ordered:NSWindowAbove];
|
|
|
|
window->ctrl = this;
|
|
window->active = active;
|
|
window.backgroundColor = [NSColor clearColor];
|
|
|
|
isopen = true;
|
|
|
|
CocoView *view = [[[CocoView alloc] initWithFrame:frame] autorelease];
|
|
view->ctrl = this;
|
|
GetTop()->coco->view = view;
|
|
[window setContentView:view];
|
|
[window setDelegate:view];
|
|
[window setAcceptsMouseMovedEvents:YES];
|
|
[window makeFirstResponder:view];
|
|
[window makeKeyAndOrderFront:window];
|
|
|
|
ONCELOCK {
|
|
[NSApp activateIgnoringOtherApps:YES];
|
|
}
|
|
|
|
MMCtrl::SyncRect(view);
|
|
mmtopctrl.Add(this);
|
|
|
|
RegisterCocoaDropFormats();
|
|
|
|
StateH(OPEN);
|
|
}
|
|
|
|
void Ctrl::WndDestroy()
|
|
{
|
|
LLOG("WndDestroy " << Name());
|
|
if(!top)
|
|
return;
|
|
bool focus = HasFocusDeep();
|
|
Ptr<Ctrl> owner = GetOwner();
|
|
|
|
auto* coco = GetTop()->coco;
|
|
auto* window = coco->window;
|
|
|
|
[window setCollectionBehavior:NSWindowCollectionBehaviorTransient];
|
|
[window close];
|
|
|
|
delete coco;
|
|
DeleteTop();
|
|
|
|
popup = isopen = false;
|
|
int ii = FindIndex(mmtopctrl, this);
|
|
if(ii >= 0)
|
|
mmtopctrl.Remove(ii);
|
|
if(owner && owner->IsOpen() && (focus || !GetFocusCtrl()))
|
|
owner->SetWndFocus();
|
|
}
|
|
|
|
Vector<Ctrl *> Ctrl::GetTopCtrls()
|
|
{
|
|
Vector<Ctrl *> h;
|
|
for(Ctrl *p : mmtopctrl)
|
|
if(p) h.Add(p);
|
|
return h;
|
|
}
|
|
|
|
void WakeUpGuiThread();
|
|
|
|
inline CGRect CGRectDPI(const Upp::Rect& r) {
|
|
double sc = 1.0 / Upp::Ctrl::SCL(1);
|
|
return CGRectMake(sc * r.left, sc * r.top, sc * r.GetWidth(), sc * r.GetHeight());
|
|
}
|
|
|
|
void Ctrl::WndInvalidateRect(const Rect& r)
|
|
{
|
|
GuiLock __;
|
|
if(top) {
|
|
NSRect nsr = (NSRect)CGRectDPI(r.Inflated(10, 10));
|
|
if(IsMainThread())
|
|
[GetTop()->coco->view setNeedsDisplayInRect:nsr];
|
|
else {
|
|
Ptr<Ctrl> ctrl = this;
|
|
PostCallback([=] {
|
|
if(ctrl)
|
|
[ctrl->GetTop()->coco->view setNeedsDisplayInRect:nsr];
|
|
});
|
|
}
|
|
}
|
|
}
|
|
|
|
void Ctrl::WndScrollView(const Rect& r, int dx, int dy)
|
|
{
|
|
GuiLock __;
|
|
LLOG("Scroll View " << r);
|
|
WndInvalidateRect(r);
|
|
}
|
|
|
|
bool Ctrl::IsWndOpen() const {
|
|
GuiLock __;
|
|
// DLOG("IsWndOpen " << Upp::Name(this) << ": " << (bool)top);
|
|
return top;
|
|
}
|
|
|
|
void Ctrl::PopUp(Ctrl *owner, bool savebits, bool activate, bool dropshadow, bool topmost)
|
|
{
|
|
Create(owner ? owner->GetTopCtrl() : GetActiveCtrl(), NSWindowStyleMaskBorderless, 0);
|
|
popup = true;
|
|
if(activate && IsEnabled())
|
|
SetFocus();
|
|
}
|
|
|
|
dword TopWindow::GetMMStyle() const
|
|
{
|
|
dword style = 0;
|
|
if(!frameless)
|
|
style |= NSWindowStyleMaskTitled|NSWindowStyleMaskClosable|NSWindowStyleMaskResizable;
|
|
if(minimizebox)
|
|
style |= NSWindowStyleMaskMiniaturizable;
|
|
// if(maximizebox)
|
|
// style |= ;
|
|
return style;
|
|
}
|
|
|
|
void TopWindow::Open(Ctrl *owner)
|
|
{
|
|
GuiLock __;
|
|
SetupRect(owner);
|
|
if((owner && center == 1) || center == 2)
|
|
SetRect((center == 1 ? owner->GetRect() : owner ? owner->GetWorkArea()
|
|
: GetPrimaryWorkArea())
|
|
.CenterRect(GetRect().GetSize()));
|
|
placefocus = true;
|
|
Create(owner, GetMMStyle(), true);
|
|
SyncCaption();
|
|
SyncSizeHints();
|
|
if(state == MAXIMIZED)
|
|
Maximize();
|
|
if(state == MINIMIZED)
|
|
Minimize();
|
|
// Note: window is activated and PlaceFocus invoked by event, later
|
|
}
|
|
|
|
void TopWindow::Open()
|
|
{
|
|
Open(GetActiveCtrl());
|
|
}
|
|
|
|
void TopWindow::OpenMain()
|
|
{
|
|
Open(NULL);
|
|
}
|
|
|
|
void TopWindow::SyncTitle()
|
|
{
|
|
GuiLock __;
|
|
if(top) {
|
|
LLOG("SyncTitle " << title);
|
|
CFRef<CFStringRef> s = CFStringCreateWithCString(NULL, (const char *)~title.ToString(), kCFStringEncodingUTF8);
|
|
[GetTop()->coco->window setTitle:(NSString *)~s];
|
|
}
|
|
}
|
|
|
|
void Ctrl::SyncAppIcon()
|
|
{
|
|
if(always_use_bundled_icon)
|
|
return;
|
|
|
|
Ctrl *q = GetFocusCtrl();
|
|
if(!q)
|
|
q = lastActive;
|
|
if(q) {
|
|
q = q->GetTopWindow();
|
|
while(q->GetOwner())
|
|
q = q->GetOwner();
|
|
TopWindow *w = dynamic_cast<TopWindow *>(q);
|
|
if(w) {
|
|
Image m = Nvl(w->GetLargeIcon(), w->GetIcon());
|
|
if(!IsNull(m))
|
|
SetNSAppImage(m);
|
|
}
|
|
}
|
|
}
|
|
|
|
void TopWindow::SyncCaption()
|
|
{
|
|
GuiLock __;
|
|
if(top) {
|
|
SyncTitle();
|
|
NSWindow* window = GetTop()->coco->window;
|
|
|
|
NSWindowStyleMask mask = [window styleMask];
|
|
mask = minimizebox ? (mask | NSWindowStyleMaskMiniaturizable)
|
|
: (mask & ~NSWindowStyleMaskMiniaturizable);
|
|
mask = maximizebox ? (mask | NSWindowStyleMaskResizable)
|
|
: (mask & ~NSWindowStyleMaskResizable);
|
|
|
|
[window setStyleMask:mask];
|
|
}
|
|
SyncAppIcon();
|
|
}
|
|
|
|
CGSize MMFrameSize(Size sz, dword style)
|
|
{
|
|
double scale = 1.0 / Upp::Ctrl::SCL(1);
|
|
return [NSWindow frameRectForContentRect:
|
|
(NSRect)CGRectMake(100, 100, scale * sz.cx, scale * sz.cy) styleMask:style].size;
|
|
}
|
|
|
|
void TopWindow::SyncSizeHints()
|
|
{
|
|
GuiLock __;
|
|
if(top) {
|
|
NSWindow *window = GetTop()->coco->window;
|
|
dword style = GetMMStyle();
|
|
Size sz = GetRect().GetSize();
|
|
[window setMinSize:MMFrameSize(sizeable ? GetMinSize() : sz, style)];
|
|
[window setMaxSize:MMFrameSize(sizeable ? GetMaxSize() : sz, style)];
|
|
}
|
|
}
|
|
|
|
Rect Ctrl::GetWndScreenRect() const
|
|
{ // THIS IS NOT NEEDED
|
|
GuiLock __;
|
|
Rect r = GetRect();
|
|
return r;
|
|
}
|
|
|
|
void Ctrl::WndSetPos(const Rect& rect)
|
|
{
|
|
GuiLock __;
|
|
if(top)
|
|
[GetTop()->coco->window setFrame:
|
|
[GetTop()->coco->window frameRectForContentRect:DesktopRect(rect)]
|
|
display:YES];
|
|
}
|
|
|
|
void TopWindow::SerializePlacement(Stream& s, bool reminimize)
|
|
{
|
|
GuiLock __;
|
|
int version = 0;
|
|
s / version;
|
|
Rect rect = GetRect();
|
|
s % rect;
|
|
if(s.IsLoading())
|
|
SetRect(rect);
|
|
}
|
|
|
|
void TopWindow::Maximize(bool effect)
|
|
{
|
|
state = MAXIMIZED;
|
|
if(top && GetTop()->coco && GetTop()->coco->window && !GetTop()->coco->window.zoomed) {
|
|
if(effect)
|
|
[GetTop()->coco->window performZoom:GetTop()->coco->window];
|
|
else
|
|
[GetTop()->coco->window zoom:GetTop()->coco->window];
|
|
}
|
|
}
|
|
|
|
void TopWindow::Minimize(bool effect)
|
|
{
|
|
state = MINIMIZED;
|
|
if(top && GetTop()->coco && GetTop()->coco->window && !GetTop()->coco->window.miniaturized) {
|
|
if(effect)
|
|
[GetTop()->coco->window performMiniaturize:GetTop()->coco->window];
|
|
else
|
|
[GetTop()->coco->window miniaturize:GetTop()->coco->window];
|
|
}
|
|
}
|
|
|
|
void TopWindow::Overlap(bool effect)
|
|
{
|
|
state = OVERLAPPED;
|
|
if(top && GetTop()->coco && GetTop()->coco->window && GetTop()->coco->window.zoomed)
|
|
[GetTop()->coco->window zoom:GetTop()->coco->window];
|
|
if(top && GetTop()->coco && GetTop()->coco->window && GetTop()->coco->window.miniaturized)
|
|
[GetTop()->coco->window deminiaturize:GetTop()->coco->window];
|
|
}
|
|
|
|
bool Ctrl::IsCocoActive() const
|
|
{
|
|
return top && GetTop()->coco && GetTop()->coco->window && GetTop()->coco->window->active;
|
|
}
|
|
|
|
}
|
|
|
|
#endif
|