mirror of https://github.com/buggins/dlangui.git
mac osx cocoa support prototype
This commit is contained in:
parent
6764ce322b
commit
a51abfffc1
|
@ -2,7 +2,7 @@
|
||||||
<Project DefaultTargets="Build" ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
<Project DefaultTargets="Build" ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
|
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
|
||||||
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
|
<Platform Condition=" '$(Platform)' == '' ">x64</Platform>
|
||||||
<ProjectGuid>{1F050D82-5245-4B51-A554-473194EA0DE9}</ProjectGuid>
|
<ProjectGuid>{1F050D82-5245-4B51-A554-473194EA0DE9}</ProjectGuid>
|
||||||
<Compiler>DMD2</Compiler>
|
<Compiler>DMD2</Compiler>
|
||||||
<PreferOneStepBuild>true</PreferOneStepBuild>
|
<PreferOneStepBuild>true</PreferOneStepBuild>
|
||||||
|
@ -26,17 +26,6 @@
|
||||||
</Includes>
|
</Includes>
|
||||||
</Includes>
|
</Includes>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
|
|
||||||
<DebugSymbols>true</DebugSymbols>
|
|
||||||
<OutputPath>bin\Debug</OutputPath>
|
|
||||||
<ObjectsDirectory>obj/Debug</ObjectsDirectory>
|
|
||||||
<LinkinThirdPartyLibraries>false</LinkinThirdPartyLibraries>
|
|
||||||
<UnittestMode>false</UnittestMode>
|
|
||||||
<OutputName>derelictcocoatest-monod-osx</OutputName>
|
|
||||||
<Target>Executable</Target>
|
|
||||||
<Externalconsole>true</Externalconsole>
|
|
||||||
<DebugLevel>0</DebugLevel>
|
|
||||||
</PropertyGroup>
|
|
||||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
|
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
|
||||||
<OutputPath>bin\Release</OutputPath>
|
<OutputPath>bin\Release</OutputPath>
|
||||||
<ObjectsDirectory>obj/Release</ObjectsDirectory>
|
<ObjectsDirectory>obj/Release</ObjectsDirectory>
|
||||||
|
@ -58,6 +47,25 @@
|
||||||
<Externalconsole>true</Externalconsole>
|
<Externalconsole>true</Externalconsole>
|
||||||
<DebugLevel>0</DebugLevel>
|
<DebugLevel>0</DebugLevel>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|x64' ">
|
||||||
|
<DebugSymbols>true</DebugSymbols>
|
||||||
|
<OutputPath>bin\Debug</OutputPath>
|
||||||
|
<VersionIds>
|
||||||
|
<VersionIds>
|
||||||
|
<String>USE_SDL</String>
|
||||||
|
<String>USE_OPENGL</String>
|
||||||
|
<String>USE_FREETYPE</String>
|
||||||
|
<String>EmbedStandardResources</String>
|
||||||
|
</VersionIds>
|
||||||
|
</VersionIds>
|
||||||
|
<ObjectsDirectory>obj/Debug</ObjectsDirectory>
|
||||||
|
<LinkinThirdPartyLibraries>false</LinkinThirdPartyLibraries>
|
||||||
|
<UnittestMode>false</UnittestMode>
|
||||||
|
<OutputName>derelictcocoatest-monod-osx</OutputName>
|
||||||
|
<Target>Executable</Target>
|
||||||
|
<Externalconsole>true</Externalconsole>
|
||||||
|
<DebugLevel>0</DebugLevel>
|
||||||
|
</PropertyGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<Folder Include="src\" />
|
<Folder Include="src\" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
|
@ -1,8 +1,14 @@
|
||||||
module cocoatest;
|
module cocoatest;
|
||||||
|
version(OSX):
|
||||||
|
|
||||||
import derelict.cocoa;
|
import derelict.cocoa;
|
||||||
|
|
||||||
import dlangui.core.logger;
|
import dlangui.core.logger;
|
||||||
|
import dlangui.core.types;
|
||||||
|
import dlangui.core.events;
|
||||||
|
import std.uuid;
|
||||||
|
import core.stdc.stdlib;
|
||||||
|
import std.string;
|
||||||
|
|
||||||
void main(string[] args)
|
void main(string[] args)
|
||||||
{
|
{
|
||||||
|
@ -36,8 +42,650 @@ void main(string[] args)
|
||||||
NSView parentView;
|
NSView parentView;
|
||||||
parentView = window.contentView();
|
parentView = window.contentView();
|
||||||
|
|
||||||
|
Log.i("parentView=", parentView);
|
||||||
|
|
||||||
NSApp.activateIgnoringOtherApps(YES);
|
NSApp.activateIgnoringOtherApps(YES);
|
||||||
|
|
||||||
|
// string uuid = randomUUID().toString();
|
||||||
|
// DlanguiCocoaView.customClassName = "DlanguiCocoaView_" ~ uuid;
|
||||||
|
// DlanguiCocoaView.registerSubclass();
|
||||||
|
//
|
||||||
|
// _view = DlanguiCocoaView.alloc();
|
||||||
|
// _view.initialize(this, width, height);
|
||||||
|
//
|
||||||
|
// parentView.addSubview(_view);
|
||||||
|
|
||||||
|
|
||||||
NSApp.run();
|
NSApp.run();
|
||||||
|
|
||||||
DerelictCocoa.unload();
|
DerelictCocoa.unload();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
interface IWindowListener {
|
||||||
|
void onMouseWheel(int x, int y, int deltaX, int deltaY, MouseState state);
|
||||||
|
void onKeyDown(uint key);
|
||||||
|
void onKeyUp(uint key);
|
||||||
|
void onMouseMove(int x, int y, int deltaX, int deltaY,
|
||||||
|
MouseState mouseState);
|
||||||
|
void onMouseRelease(int x, int y, MouseButton mb, MouseState mouseState);
|
||||||
|
void onMouseClick(int x, int y, MouseButton mb, bool isDoubleClick, MouseState mouseState);
|
||||||
|
void recomputeDirtyAreas();
|
||||||
|
void onResized(int width, int height);
|
||||||
|
void onAnimate(double dt, double time);
|
||||||
|
Rect getDirtyRectangle();
|
||||||
|
}
|
||||||
|
|
||||||
|
struct MouseState {
|
||||||
|
bool leftButtonDown;
|
||||||
|
bool rightButtonDown;
|
||||||
|
bool middleButtonDown;
|
||||||
|
bool ctrlPressed;
|
||||||
|
bool shiftPressed;
|
||||||
|
bool altPressed;
|
||||||
|
}
|
||||||
|
|
||||||
|
enum MouseButton : int {
|
||||||
|
left,
|
||||||
|
right,
|
||||||
|
middle
|
||||||
|
}
|
||||||
|
|
||||||
|
final class CocoaWindow
|
||||||
|
{
|
||||||
|
private:
|
||||||
|
IWindowListener _listener;
|
||||||
|
|
||||||
|
// Stays null in the case of a plugin, but exists for a stand-alone program
|
||||||
|
// For testing purpose.
|
||||||
|
NSWindow _cocoaWindow = null;
|
||||||
|
NSApplication _cocoaApplication;
|
||||||
|
|
||||||
|
NSColorSpace _nsColorSpace;
|
||||||
|
CGColorSpaceRef _cgColorSpaceRef;
|
||||||
|
NSData _imageData;
|
||||||
|
NSString _logFormatStr;
|
||||||
|
|
||||||
|
DPlugCustomView _view = null;
|
||||||
|
|
||||||
|
bool _terminated = false;
|
||||||
|
|
||||||
|
int _lastMouseX, _lastMouseY;
|
||||||
|
bool _firstMouseMove = true;
|
||||||
|
|
||||||
|
int _width;
|
||||||
|
int _height;
|
||||||
|
|
||||||
|
ubyte* _buffer = null;
|
||||||
|
|
||||||
|
uint _timeAtCreationInMs;
|
||||||
|
uint _lastMeasturedTimeInMs;
|
||||||
|
bool _dirtyAreasAreNotYetComputed;
|
||||||
|
|
||||||
|
public:
|
||||||
|
|
||||||
|
this(void* parentWindow, IWindowListener listener, int width, int height)
|
||||||
|
{
|
||||||
|
_listener = listener;
|
||||||
|
|
||||||
|
DerelictCocoa.load();
|
||||||
|
NSApplicationLoad(); // to use Cocoa in Carbon applications
|
||||||
|
bool parentViewExists = parentWindow !is null;
|
||||||
|
NSView parentView;
|
||||||
|
if (!parentViewExists)
|
||||||
|
{
|
||||||
|
// create a NSWindow to hold our NSView
|
||||||
|
_cocoaApplication = NSApplication.sharedApplication;
|
||||||
|
_cocoaApplication.setActivationPolicy(NSApplicationActivationPolicyRegular);
|
||||||
|
|
||||||
|
NSWindow window = NSWindow.alloc();
|
||||||
|
window.initWithContentRect(NSMakeRect(100, 100, width, height),
|
||||||
|
NSBorderlessWindowMask, NSBackingStoreBuffered, NO);
|
||||||
|
window.makeKeyAndOrderFront();
|
||||||
|
|
||||||
|
parentView = window.contentView();
|
||||||
|
|
||||||
|
_cocoaApplication.activateIgnoringOtherApps(YES);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
parentView = NSView(cast(id)parentWindow);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
_width = 0;
|
||||||
|
_height = 0;
|
||||||
|
|
||||||
|
_nsColorSpace = NSColorSpace.sRGBColorSpace();
|
||||||
|
// hopefully not null else the colors will be brighter
|
||||||
|
_cgColorSpaceRef = _nsColorSpace.CGColorSpace();
|
||||||
|
|
||||||
|
_logFormatStr = NSString.stringWith("%@");
|
||||||
|
|
||||||
|
_timeAtCreationInMs = getTimeMs();
|
||||||
|
_lastMeasturedTimeInMs = _timeAtCreationInMs;
|
||||||
|
|
||||||
|
_dirtyAreasAreNotYetComputed = true;
|
||||||
|
|
||||||
|
string uuid = randomUUID().toString();
|
||||||
|
DPlugCustomView.customClassName = "DPlugCustomView_" ~ uuid;
|
||||||
|
DPlugCustomView.registerSubclass();
|
||||||
|
|
||||||
|
_view = DPlugCustomView.alloc();
|
||||||
|
_view.initialize(this, width, height);
|
||||||
|
|
||||||
|
parentView.addSubview(_view);
|
||||||
|
|
||||||
|
if (_cocoaApplication)
|
||||||
|
_cocoaApplication.run();
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
~this()
|
||||||
|
{
|
||||||
|
if (_view)
|
||||||
|
{
|
||||||
|
//debug ensureNotInGC("CocoaWindow");
|
||||||
|
_terminated = true;
|
||||||
|
|
||||||
|
{
|
||||||
|
_view.killTimer();
|
||||||
|
}
|
||||||
|
|
||||||
|
_view.removeFromSuperview();
|
||||||
|
_view.release();
|
||||||
|
_view = DPlugCustomView(null);
|
||||||
|
|
||||||
|
DPlugCustomView.unregisterSubclass();
|
||||||
|
|
||||||
|
if (_buffer != null)
|
||||||
|
{
|
||||||
|
free(_buffer);
|
||||||
|
_buffer = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
DerelictCocoa.unload();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Implements IWindow
|
||||||
|
void waitEventAndDispatch()
|
||||||
|
{
|
||||||
|
assert(false); // not implemented in Cocoa, since we don't have a NSWindow
|
||||||
|
}
|
||||||
|
|
||||||
|
bool terminated()
|
||||||
|
{
|
||||||
|
return _terminated;
|
||||||
|
}
|
||||||
|
|
||||||
|
void debugOutput(string s)
|
||||||
|
{
|
||||||
|
import core.stdc.stdio;
|
||||||
|
fprintf(stderr, toStringz(s));
|
||||||
|
}
|
||||||
|
|
||||||
|
uint getTimeMs()
|
||||||
|
{
|
||||||
|
return cast(uint)(NSDate.timeIntervalSinceReferenceDate() * 1000.0);
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
|
||||||
|
MouseState getMouseState(NSEvent event)
|
||||||
|
{
|
||||||
|
// not working
|
||||||
|
MouseState state;
|
||||||
|
uint pressedMouseButtons = event.pressedMouseButtons();
|
||||||
|
if (pressedMouseButtons & 1)
|
||||||
|
state.leftButtonDown = true;
|
||||||
|
if (pressedMouseButtons & 2)
|
||||||
|
state.rightButtonDown = true;
|
||||||
|
if (pressedMouseButtons & 4)
|
||||||
|
state.middleButtonDown = true;
|
||||||
|
|
||||||
|
NSEventModifierFlags mod = event.modifierFlags();
|
||||||
|
if (mod & NSControlKeyMask)
|
||||||
|
state.ctrlPressed = true;
|
||||||
|
if (mod & NSShiftKeyMask)
|
||||||
|
state.shiftPressed = true;
|
||||||
|
if (mod & NSAlternateKeyMask)
|
||||||
|
state.altPressed = true;
|
||||||
|
|
||||||
|
return state;
|
||||||
|
}
|
||||||
|
|
||||||
|
void handleMouseWheel(NSEvent event)
|
||||||
|
{
|
||||||
|
int deltaX = cast(int)(0.5 + 10 * event.deltaX);
|
||||||
|
int deltaY = cast(int)(0.5 + 10 * event.deltaY);
|
||||||
|
Point mousePos = getMouseXY(_view, event, _height);
|
||||||
|
_listener.onMouseWheel(mousePos.x, mousePos.y, deltaX, deltaY, getMouseState(event));
|
||||||
|
}
|
||||||
|
|
||||||
|
void handleKeyEvent(NSEvent event, bool released)
|
||||||
|
{
|
||||||
|
uint keyCode = event.keyCode();
|
||||||
|
uint key;
|
||||||
|
switch (keyCode)
|
||||||
|
{
|
||||||
|
case kVK_ANSI_Keypad0: key = KeyCode.KEY_0; break;
|
||||||
|
case kVK_ANSI_Keypad1: key = KeyCode.KEY_1; break;
|
||||||
|
case kVK_ANSI_Keypad2: key = KeyCode.KEY_2; break;
|
||||||
|
case kVK_ANSI_Keypad3: key = KeyCode.KEY_3; break;
|
||||||
|
case kVK_ANSI_Keypad4: key = KeyCode.KEY_4; break;
|
||||||
|
case kVK_ANSI_Keypad5: key = KeyCode.KEY_5; break;
|
||||||
|
case kVK_ANSI_Keypad6: key = KeyCode.KEY_6; break;
|
||||||
|
case kVK_ANSI_Keypad7: key = KeyCode.KEY_7; break;
|
||||||
|
case kVK_ANSI_Keypad8: key = KeyCode.KEY_8; break;
|
||||||
|
case kVK_ANSI_Keypad9: key = KeyCode.KEY_9; break;
|
||||||
|
case kVK_Return: key = KeyCode.RETURN; break;
|
||||||
|
case kVK_Escape: key = KeyCode.ESCAPE; break;
|
||||||
|
case kVK_LeftArrow: key = KeyCode.LEFT; break;
|
||||||
|
case kVK_RightArrow: key = KeyCode.RIGHT; break;
|
||||||
|
case kVK_DownArrow: key = KeyCode.DOWN; break;
|
||||||
|
case kVK_UpArrow: key = KeyCode.UP; break;
|
||||||
|
default: key = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (released)
|
||||||
|
_listener.onKeyDown(key);
|
||||||
|
else
|
||||||
|
_listener.onKeyUp(key);
|
||||||
|
}
|
||||||
|
|
||||||
|
void handleMouseMove(NSEvent event)
|
||||||
|
{
|
||||||
|
Point mousePos = getMouseXY(_view, event, _height);
|
||||||
|
|
||||||
|
if (_firstMouseMove)
|
||||||
|
{
|
||||||
|
_firstMouseMove = false;
|
||||||
|
_lastMouseX = mousePos.x;
|
||||||
|
_lastMouseY = mousePos.y;
|
||||||
|
}
|
||||||
|
|
||||||
|
_listener.onMouseMove(mousePos.x, mousePos.y, mousePos.x - _lastMouseX, mousePos.y - _lastMouseY,
|
||||||
|
getMouseState(event));
|
||||||
|
|
||||||
|
_lastMouseX = mousePos.x;
|
||||||
|
_lastMouseY = mousePos.y;
|
||||||
|
}
|
||||||
|
|
||||||
|
void handleMouseClicks(NSEvent event, MouseButton mb, bool released)
|
||||||
|
{
|
||||||
|
Point mousePos = getMouseXY(_view, event, _height);
|
||||||
|
|
||||||
|
if (released)
|
||||||
|
_listener.onMouseRelease(mousePos.x, mousePos.y, mb, getMouseState(event));
|
||||||
|
else
|
||||||
|
{
|
||||||
|
int clickCount = event.clickCount();
|
||||||
|
bool isDoubleClick = clickCount >= 2;
|
||||||
|
_listener.onMouseClick(mousePos.x, mousePos.y, mb, isDoubleClick, getMouseState(event));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
enum scanLineAlignment = 4; // could be anything
|
||||||
|
|
||||||
|
// given a width, how long in bytes should scanlines be
|
||||||
|
int byteStride(int width)
|
||||||
|
{
|
||||||
|
int widthInBytes = width * 4;
|
||||||
|
return (widthInBytes + (scanLineAlignment - 1)) & ~(scanLineAlignment-1);
|
||||||
|
}
|
||||||
|
|
||||||
|
void drawRect(NSRect rect)
|
||||||
|
{
|
||||||
|
NSGraphicsContext nsContext = NSGraphicsContext.currentContext();
|
||||||
|
|
||||||
|
CIContext ciContext = nsContext.getCIContext();
|
||||||
|
|
||||||
|
// update internal buffers in case of startup/resize
|
||||||
|
{
|
||||||
|
NSRect boundsRect = _view.bounds();
|
||||||
|
int width = cast(int)(boundsRect.size.width); // truncating down the dimensions of bounds
|
||||||
|
int height = cast(int)(boundsRect.size.height);
|
||||||
|
updateSizeIfNeeded(width, height);
|
||||||
|
}
|
||||||
|
|
||||||
|
// The first drawRect callback occurs before the timer triggers.
|
||||||
|
// But because recomputeDirtyAreas() wasn't called before there is nothing to draw.
|
||||||
|
// Hence, do it.
|
||||||
|
if (_dirtyAreasAreNotYetComputed)
|
||||||
|
{
|
||||||
|
_dirtyAreasAreNotYetComputed = false;
|
||||||
|
_listener.recomputeDirtyAreas();
|
||||||
|
}
|
||||||
|
|
||||||
|
// draw buffers
|
||||||
|
// ImageRef!RGBA wfb;
|
||||||
|
// wfb.w = _width;
|
||||||
|
// wfb.h = _height;
|
||||||
|
// wfb.pitch = byteStride(_width);
|
||||||
|
// wfb.pixels = cast(RGBA*)_buffer;
|
||||||
|
// _listener.onDraw(wfb, WindowPixelFormat.ARGB8);
|
||||||
|
|
||||||
|
|
||||||
|
size_t sizeNeeded = byteStride(_width) * _height;
|
||||||
|
_imageData = NSData.dataWithBytesNoCopy(_buffer, sizeNeeded, false);
|
||||||
|
|
||||||
|
CIImage image = CIImage.imageWithBitmapData(_imageData,
|
||||||
|
byteStride(_width),
|
||||||
|
CGSize(_width, _height),
|
||||||
|
kCIFormatARGB8,
|
||||||
|
_cgColorSpaceRef);
|
||||||
|
|
||||||
|
ciContext.drawImage(image, rect, rect);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns: true if window size changed.
|
||||||
|
bool updateSizeIfNeeded(int newWidth, int newHeight)
|
||||||
|
{
|
||||||
|
// only do something if the client size has changed
|
||||||
|
if ( (newWidth != _width) || (newHeight != _height) )
|
||||||
|
{
|
||||||
|
// Extends buffer
|
||||||
|
if (_buffer != null)
|
||||||
|
{
|
||||||
|
free(_buffer);
|
||||||
|
_buffer = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t sizeNeeded = byteStride(newWidth) * newHeight;
|
||||||
|
_buffer = cast(ubyte*) malloc(sizeNeeded);
|
||||||
|
_width = newWidth;
|
||||||
|
_height = newHeight;
|
||||||
|
_listener.onResized(_width, _height);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void doAnimation()
|
||||||
|
{
|
||||||
|
uint now = getTimeMs();
|
||||||
|
double dt = (now - _lastMeasturedTimeInMs) * 0.001;
|
||||||
|
double time = (now - _timeAtCreationInMs) * 0.001; // hopefully no plug-in will be open more than 49 days
|
||||||
|
_lastMeasturedTimeInMs = now;
|
||||||
|
_listener.onAnimate(dt, time);
|
||||||
|
}
|
||||||
|
|
||||||
|
void onTimer()
|
||||||
|
{
|
||||||
|
// Deal with animation
|
||||||
|
doAnimation();
|
||||||
|
|
||||||
|
_listener.recomputeDirtyAreas();
|
||||||
|
_dirtyAreasAreNotYetComputed = false;
|
||||||
|
Rect dirtyRect = _listener.getDirtyRectangle();
|
||||||
|
if (!dirtyRect.empty())
|
||||||
|
{
|
||||||
|
|
||||||
|
NSRect boundsRect = _view.bounds();
|
||||||
|
int height = cast(int)(boundsRect.size.height);
|
||||||
|
NSRect r = NSMakeRect(dirtyRect.left,
|
||||||
|
height - dirtyRect.top - dirtyRect.height,
|
||||||
|
dirtyRect.width,
|
||||||
|
dirtyRect.height);
|
||||||
|
_view.setNeedsDisplayInRect(r);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct DPlugCustomView
|
||||||
|
{
|
||||||
|
// This class uses a unique class name for each plugin instance
|
||||||
|
static string customClassName = null;
|
||||||
|
|
||||||
|
NSView parent;
|
||||||
|
alias parent this;
|
||||||
|
|
||||||
|
// create from an id
|
||||||
|
this (id id_)
|
||||||
|
{
|
||||||
|
this._id = id_;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Allocates, but do not init
|
||||||
|
static DPlugCustomView alloc()
|
||||||
|
{
|
||||||
|
alias fun_t = extern(C) id function (id obj, SEL sel);
|
||||||
|
return DPlugCustomView( (cast(fun_t)objc_msgSend)(getClassID(), sel!"alloc") );
|
||||||
|
}
|
||||||
|
|
||||||
|
static Class getClass()
|
||||||
|
{
|
||||||
|
return cast(Class)( getClassID() );
|
||||||
|
}
|
||||||
|
|
||||||
|
static id getClassID()
|
||||||
|
{
|
||||||
|
assert(customClassName !is null);
|
||||||
|
return objc_getClass(customClassName);
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
|
||||||
|
CocoaWindow _window;
|
||||||
|
NSTimer _timer = null;
|
||||||
|
|
||||||
|
void initialize(CocoaWindow window, int width, int height)
|
||||||
|
{
|
||||||
|
// Warning: taking this address is fishy since DPlugCustomView is a struct and thus could be copied
|
||||||
|
// we rely on the fact it won't :|
|
||||||
|
void* thisPointer = cast(void*)(&this);
|
||||||
|
object_setInstanceVariable(_id, "this", thisPointer);
|
||||||
|
|
||||||
|
this._window = window;
|
||||||
|
|
||||||
|
NSRect r = NSRect(NSPoint(0, 0), NSSize(width, height));
|
||||||
|
initWithFrame(r);
|
||||||
|
|
||||||
|
_timer = NSTimer.timerWithTimeInterval(1 / 60.0, this, sel!"onTimer:", null, true);
|
||||||
|
NSRunLoop.currentRunLoop().addTimer(_timer, NSRunLoopCommonModes);
|
||||||
|
}
|
||||||
|
|
||||||
|
static Class clazz;
|
||||||
|
|
||||||
|
static void registerSubclass()
|
||||||
|
{
|
||||||
|
clazz = objc_allocateClassPair(cast(Class) lazyClass!"NSView", toStringz(customClassName), 0);
|
||||||
|
|
||||||
|
class_addMethod(clazz, sel!"keyDown:", cast(IMP) &keyDown, "v@:@");
|
||||||
|
class_addMethod(clazz, sel!"keyUp:", cast(IMP) &keyUp, "v@:@");
|
||||||
|
class_addMethod(clazz, sel!"mouseDown:", cast(IMP) &mouseDown, "v@:@");
|
||||||
|
class_addMethod(clazz, sel!"mouseUp:", cast(IMP) &mouseUp, "v@:@");
|
||||||
|
class_addMethod(clazz, sel!"rightMouseDown:", cast(IMP) &rightMouseDown, "v@:@");
|
||||||
|
class_addMethod(clazz, sel!"rightMouseUp:", cast(IMP) &rightMouseUp, "v@:@");
|
||||||
|
class_addMethod(clazz, sel!"otherMouseDown:", cast(IMP) &otherMouseDown, "v@:@");
|
||||||
|
class_addMethod(clazz, sel!"otherMouseUp:", cast(IMP) &otherMouseUp, "v@:@");
|
||||||
|
class_addMethod(clazz, sel!"mouseMoved:", cast(IMP) &mouseMoved, "v@:@");
|
||||||
|
class_addMethod(clazz, sel!"mouseDragged:", cast(IMP) &mouseMoved, "v@:@");
|
||||||
|
class_addMethod(clazz, sel!"rightMouseDragged:", cast(IMP) &mouseMoved, "v@:@");
|
||||||
|
class_addMethod(clazz, sel!"otherMouseDragged:", cast(IMP) &mouseMoved, "v@:@");
|
||||||
|
class_addMethod(clazz, sel!"acceptsFirstResponder", cast(IMP) &acceptsFirstResponder, "b@:");
|
||||||
|
class_addMethod(clazz, sel!"isOpaque", cast(IMP) &isOpaque, "b@:");
|
||||||
|
class_addMethod(clazz, sel!"acceptsFirstMouse:", cast(IMP) &acceptsFirstMouse, "b@:@");
|
||||||
|
class_addMethod(clazz, sel!"viewDidMoveToWindow", cast(IMP) &viewDidMoveToWindow, "v@:");
|
||||||
|
class_addMethod(clazz, sel!"drawRect:", cast(IMP) &drawRect, "v@:" ~ encode!NSRect);
|
||||||
|
class_addMethod(clazz, sel!"onTimer:", cast(IMP) &onTimer, "v@:@");
|
||||||
|
|
||||||
|
// This ~ is to avoid a strange DMD ICE. Didn't succeed in isolating it.
|
||||||
|
class_addMethod(clazz, sel!("scroll" ~ "Wheel:") , cast(IMP) &scrollWheel, "v@:@");
|
||||||
|
|
||||||
|
// very important: add an instance variable for the this pointer so that the D object can be
|
||||||
|
// retrieved from an id
|
||||||
|
class_addIvar(clazz, "this", (void*).sizeof, (void*).sizeof == 4 ? 2 : 3, "^v");
|
||||||
|
|
||||||
|
objc_registerClassPair(clazz);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void unregisterSubclass()
|
||||||
|
{
|
||||||
|
// For some reason the class need to continue to exist, so we leak it
|
||||||
|
// objc_disposeClassPair(clazz);
|
||||||
|
// TODO: remove this crap
|
||||||
|
}
|
||||||
|
|
||||||
|
void killTimer()
|
||||||
|
{
|
||||||
|
if (_timer)
|
||||||
|
{
|
||||||
|
_timer.invalidate();
|
||||||
|
_timer = NSTimer(null);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
DPlugCustomView getInstance(id anId)
|
||||||
|
{
|
||||||
|
// strange thing: object_getInstanceVariable definition is odd (void**)
|
||||||
|
// and only works for pointer-sized values says SO
|
||||||
|
void* thisPointer = null;
|
||||||
|
Ivar var = object_getInstanceVariable(anId, "this", &thisPointer);
|
||||||
|
assert(var !is null);
|
||||||
|
assert(thisPointer !is null);
|
||||||
|
return *cast(DPlugCustomView*)thisPointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
Point getMouseXY(NSView view, NSEvent event, int windowHeight)
|
||||||
|
{
|
||||||
|
NSPoint mouseLocation = event.locationInWindow();
|
||||||
|
mouseLocation = view.convertPoint(mouseLocation, NSView(null));
|
||||||
|
int px = cast(int)(mouseLocation.x) - 2;
|
||||||
|
int py = windowHeight - cast(int)(mouseLocation.y) - 3;
|
||||||
|
return Point(px, py);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Overridden function gets called with an id, instead of the self pointer.
|
||||||
|
// So we have to get back the D class object address.
|
||||||
|
// Big thanks to Mike Ash (@macdev)
|
||||||
|
extern(C)
|
||||||
|
{
|
||||||
|
void keyDown(id self, SEL selector, id event)
|
||||||
|
{
|
||||||
|
//FPControl fpctrl;
|
||||||
|
//fpctrl.initialize();
|
||||||
|
DPlugCustomView view = getInstance(self);
|
||||||
|
view._window.handleKeyEvent(NSEvent(event), false);
|
||||||
|
}
|
||||||
|
|
||||||
|
void keyUp(id self, SEL selector, id event)
|
||||||
|
{
|
||||||
|
//FPControl fpctrl;
|
||||||
|
//fpctrl.initialize();
|
||||||
|
DPlugCustomView view = getInstance(self);
|
||||||
|
view._window.handleKeyEvent(NSEvent(event), true);
|
||||||
|
}
|
||||||
|
|
||||||
|
void mouseDown(id self, SEL selector, id event)
|
||||||
|
{
|
||||||
|
//FPControl fpctrl;
|
||||||
|
//fpctrl.initialize();
|
||||||
|
DPlugCustomView view = getInstance(self);
|
||||||
|
view._window.handleMouseClicks(NSEvent(event), MouseButton.left, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
void mouseUp(id self, SEL selector, id event)
|
||||||
|
{
|
||||||
|
//FPControl fpctrl;
|
||||||
|
//fpctrl.initialize();
|
||||||
|
DPlugCustomView view = getInstance(self);
|
||||||
|
view._window.handleMouseClicks(NSEvent(event), MouseButton.left, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
void rightMouseDown(id self, SEL selector, id event)
|
||||||
|
{
|
||||||
|
//FPControl fpctrl;
|
||||||
|
//fpctrl.initialize();
|
||||||
|
DPlugCustomView view = getInstance(self);
|
||||||
|
view._window.handleMouseClicks(NSEvent(event), MouseButton.right, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
void rightMouseUp(id self, SEL selector, id event)
|
||||||
|
{
|
||||||
|
//FPControl fpctrl;
|
||||||
|
//fpctrl.initialize();
|
||||||
|
DPlugCustomView view = getInstance(self);
|
||||||
|
view._window.handleMouseClicks(NSEvent(event), MouseButton.right, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
void otherMouseDown(id self, SEL selector, id event)
|
||||||
|
{
|
||||||
|
//FPControl fpctrl;
|
||||||
|
//fpctrl.initialize();
|
||||||
|
DPlugCustomView view = getInstance(self);
|
||||||
|
auto nsEvent = NSEvent(event);
|
||||||
|
if (nsEvent.buttonNumber == 2)
|
||||||
|
view._window.handleMouseClicks(nsEvent, MouseButton.middle, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
void otherMouseUp(id self, SEL selector, id event)
|
||||||
|
{
|
||||||
|
//FPControl fpctrl;
|
||||||
|
//fpctrl.initialize();
|
||||||
|
DPlugCustomView view = getInstance(self);
|
||||||
|
auto nsEvent = NSEvent(event);
|
||||||
|
if (nsEvent.buttonNumber == 2)
|
||||||
|
view._window.handleMouseClicks(nsEvent, MouseButton.middle, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
void mouseMoved(id self, SEL selector, id event)
|
||||||
|
{
|
||||||
|
//FPControl fpctrl;
|
||||||
|
//fpctrl.initialize();
|
||||||
|
DPlugCustomView view = getInstance(self);
|
||||||
|
view._window.handleMouseMove(NSEvent(event));
|
||||||
|
}
|
||||||
|
|
||||||
|
void scrollWheel(id self, SEL selector, id event)
|
||||||
|
{
|
||||||
|
//FPControl fpctrl;
|
||||||
|
//fpctrl.initialize();
|
||||||
|
DPlugCustomView view = getInstance(self);
|
||||||
|
view._window.handleMouseWheel(NSEvent(event));
|
||||||
|
}
|
||||||
|
|
||||||
|
bool acceptsFirstResponder(id self, SEL selector)
|
||||||
|
{
|
||||||
|
return YES;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool acceptsFirstMouse(id self, SEL selector, id pEvent)
|
||||||
|
{
|
||||||
|
return YES;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool isOpaque(id self, SEL selector)
|
||||||
|
{
|
||||||
|
return YES;
|
||||||
|
}
|
||||||
|
|
||||||
|
void viewDidMoveToWindow(id self, SEL selector)
|
||||||
|
{
|
||||||
|
DPlugCustomView view = getInstance(self);
|
||||||
|
NSWindow parentWindow = view.window();
|
||||||
|
if (parentWindow)
|
||||||
|
{
|
||||||
|
parentWindow.makeFirstResponder(view);
|
||||||
|
parentWindow.setAcceptsMouseMovedEvents(true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void drawRect(id self, SEL selector, NSRect rect)
|
||||||
|
{
|
||||||
|
//FPControl fpctrl;
|
||||||
|
//fpctrl.initialize();
|
||||||
|
DPlugCustomView view = getInstance(self);
|
||||||
|
view._window.drawRect(rect);
|
||||||
|
}
|
||||||
|
|
||||||
|
void onTimer(id self, SEL selector, id timer)
|
||||||
|
{
|
||||||
|
//FPControl fpctrl;
|
||||||
|
//fpctrl.initialize();
|
||||||
|
DPlugCustomView view = getInstance(self);
|
||||||
|
view._window.onTimer();
|
||||||
|
}
|
||||||
|
}
|
|
@ -38,14 +38,13 @@
|
||||||
</VersionIds>
|
</VersionIds>
|
||||||
<ObjectsDirectory>obj/Debug</ObjectsDirectory>
|
<ObjectsDirectory>obj/Debug</ObjectsDirectory>
|
||||||
<LinkinThirdPartyLibraries>true</LinkinThirdPartyLibraries>
|
<LinkinThirdPartyLibraries>true</LinkinThirdPartyLibraries>
|
||||||
<ExtraCompilerArguments>-Jviews -Jviews/res -Jviews/res/i18n -Jviews/res/mdpi -Jviews/res/hdpi
|
<ExtraCompilerArguments>-Jviews -Jviews/res -Jviews/res/i18n -Jviews/res/mdpi -Jviews/res/hdpi</ExtraCompilerArguments>
|
||||||
-v</ExtraCompilerArguments>
|
|
||||||
<UnittestMode>false</UnittestMode>
|
<UnittestMode>false</UnittestMode>
|
||||||
<OutputName>example1-monod-osx</OutputName>
|
<OutputName>example1-monod-osx</OutputName>
|
||||||
<Target>Executable</Target>
|
<Target>Executable</Target>
|
||||||
<Externalconsole>true</Externalconsole>
|
<Externalconsole>true</Externalconsole>
|
||||||
<DebugLevel>0</DebugLevel>
|
<DebugLevel>0</DebugLevel>
|
||||||
<ExtraLinkerArguments>-v</ExtraLinkerArguments>
|
<ConsolePause>false</ConsolePause>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|x64' ">
|
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|x64' ">
|
||||||
<OutputPath>bin\Release</OutputPath>
|
<OutputPath>bin\Release</OutputPath>
|
||||||
|
|
|
@ -1521,22 +1521,18 @@ version (Windows) {
|
||||||
ft.registerFont("/Library/Fonts/Arial Bold.ttf", FontFamily.SansSerif, "Arial", false, FontWeight.Bold);
|
ft.registerFont("/Library/Fonts/Arial Bold.ttf", FontFamily.SansSerif, "Arial", false, FontWeight.Bold);
|
||||||
ft.registerFont("/Library/Fonts/Arial Italic.ttf", FontFamily.SansSerif, "Arial", true, FontWeight.Normal);
|
ft.registerFont("/Library/Fonts/Arial Italic.ttf", FontFamily.SansSerif, "Arial", true, FontWeight.Normal);
|
||||||
ft.registerFont("/Library/Fonts/Arial Bold Italic.ttf", FontFamily.SansSerif, "Arial", true, FontWeight.Bold);
|
ft.registerFont("/Library/Fonts/Arial Bold Italic.ttf", FontFamily.SansSerif, "Arial", true, FontWeight.Bold);
|
||||||
ft.registerFont("/Library/Fonts/Arial Narrow.ttf", FontFamily.SansSerif, "Arial Narrow", false, FontWeight.Normal);
|
//ft.registerFont("/Library/Fonts/Arial Narrow.ttf", FontFamily.SansSerif, "Arial Narrow", false, FontWeight.Normal);
|
||||||
ft.registerFont("/Library/Fonts/Arial Narrow Bold.ttf", FontFamily.SansSerif, "Arial Narrow", false, FontWeight.Bold);
|
//ft.registerFont("/Library/Fonts/Arial Narrow Bold.ttf", FontFamily.SansSerif, "Arial Narrow", false, FontWeight.Bold);
|
||||||
ft.registerFont("/Library/Fonts/Arial Narrow Italic.ttf", FontFamily.SansSerif, "Arial Narrow", true, FontWeight.Normal);
|
//ft.registerFont("/Library/Fonts/Arial Narrow Italic.ttf", FontFamily.SansSerif, "Arial Narrow", true, FontWeight.Normal);
|
||||||
ft.registerFont("/Library/Fonts/Arial Narrow Bold Italic.ttf", FontFamily.SansSerif, "Arial Narrow", true, FontWeight.Bold);
|
//ft.registerFont("/Library/Fonts/Arial Narrow Bold Italic.ttf", FontFamily.SansSerif, "Arial Narrow", true, FontWeight.Bold);
|
||||||
ft.registerFont("/Library/Fonts/Courier New.ttf", FontFamily.MonoSpace, "Courier New", false, FontWeight.Normal);
|
ft.registerFont("/Library/Fonts/Courier New.ttf", FontFamily.MonoSpace, "Courier New", false, FontWeight.Normal);
|
||||||
ft.registerFont("/Library/Fonts/Courier New Bold.ttf", FontFamily.MonoSpace, "Courier New", false, FontWeight.Bold);
|
ft.registerFont("/Library/Fonts/Courier New Bold.ttf", FontFamily.MonoSpace, "Courier New", false, FontWeight.Bold);
|
||||||
ft.registerFont("/Library/Fonts/Courier New Italic.ttf", FontFamily.MonoSpace, "Courier New", true, FontWeight.Normal);
|
ft.registerFont("/Library/Fonts/Courier New Italic.ttf", FontFamily.MonoSpace, "Courier New", true, FontWeight.Normal);
|
||||||
ft.registerFont("/Library/Fonts/Courier New Bold Italic.ttf", FontFamily.MonoSpace, "Courier New", true, FontWeight.Bold);
|
ft.registerFont("/Library/Fonts/Courier New Bold Italic.ttf", FontFamily.MonoSpace, "Courier New", true, FontWeight.Bold);
|
||||||
ft.registerFont("/Library/Fonts/Georgia.ttf", FontFamily.SansSerif, "Georgia", false, FontWeight.Normal);
|
ft.registerFont("/Library/Fonts/Georgia.ttf", FontFamily.Serif, "Georgia", false, FontWeight.Normal);
|
||||||
ft.registerFont("/Library/Fonts/Georgia Bold.ttf", FontFamily.SansSerif, "Georgia", false, FontWeight.Bold);
|
ft.registerFont("/Library/Fonts/Georgia Bold.ttf", FontFamily.Serif, "Georgia", false, FontWeight.Bold);
|
||||||
ft.registerFont("/Library/Fonts/Georgia Italic.ttf", FontFamily.SansSerif, "Georgia", true, FontWeight.Normal);
|
ft.registerFont("/Library/Fonts/Georgia Italic.ttf", FontFamily.Serif, "Georgia", true, FontWeight.Normal);
|
||||||
ft.registerFont("/Library/Fonts/Georgia Bold Italic.ttf", FontFamily.SansSerif, "Georgia", true, FontWeight.Bold);
|
ft.registerFont("/Library/Fonts/Georgia Bold Italic.ttf", FontFamily.Serif, "Georgia", true, FontWeight.Bold);
|
||||||
ft.registerFont("/Library/Fonts/Georgia.ttf", FontFamily.SansSerif, "Georgia", false, FontWeight.Normal);
|
|
||||||
ft.registerFont("/Library/Fonts/Georgia Bold.ttf", FontFamily.SansSerif, "Georgia", false, FontWeight.Bold);
|
|
||||||
ft.registerFont("/Library/Fonts/Georgia Italic.ttf", FontFamily.SansSerif, "Georgia", true, FontWeight.Normal);
|
|
||||||
ft.registerFont("/Library/Fonts/Georgia Bold Italic.ttf", FontFamily.SansSerif, "Georgia", true, FontWeight.Bold);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -827,8 +827,8 @@ class Theme : Style {
|
||||||
_fontSize = 9 | SIZE_IN_POINTS_FLAG; // TODO: from settings or screen properties / DPI
|
_fontSize = 9 | SIZE_IN_POINTS_FLAG; // TODO: from settings or screen properties / DPI
|
||||||
_fontStyle = FONT_STYLE_NORMAL;
|
_fontStyle = FONT_STYLE_NORMAL;
|
||||||
_fontWeight = 400;
|
_fontWeight = 400;
|
||||||
//_fontFace = "Arial"; // TODO: from settings
|
_fontFace = "Arial"; // TODO: from settings
|
||||||
_fontFace = "Verdana"; // TODO: from settings
|
//_fontFace = "Verdana"; // TODO: from settings
|
||||||
_fontFamily = FontFamily.SansSerif;
|
_fontFamily = FontFamily.SansSerif;
|
||||||
_minHeight = 0;
|
_minHeight = 0;
|
||||||
_minWidth = 0;
|
_minWidth = 0;
|
||||||
|
|
Loading…
Reference in New Issue