mirror of https://github.com/adamdruppe/arsd.git
catchup
This commit is contained in:
parent
25906483bd
commit
0b8ddc2470
39
bmp.d
39
bmp.d
|
@ -1,16 +1,40 @@
|
|||
import core.stdc.stdio;
|
||||
module arsd.bmp;
|
||||
|
||||
import arsd.color;
|
||||
|
||||
MemoryImage readBmp(string filename) {
|
||||
import core.stdc.stdio;
|
||||
|
||||
FILE* fp = fopen((filename ~ "\0").ptr, "rb".ptr);
|
||||
if(fp is null)
|
||||
throw new Exception("can't open save file");
|
||||
scope(exit) fclose(fp);
|
||||
|
||||
uint read4() { uint what; fread(&what, 4, 1, fp); return what; }
|
||||
ushort read2(){ ushort what; fread(&what, 2, 1, fp); return what; }
|
||||
ubyte read1() { return cast(ubyte) fgetc(fp); }
|
||||
void specialFread(void* tgt, size_t size) {
|
||||
fread(tgt, size, 1, fp);
|
||||
}
|
||||
|
||||
return readBmpIndirect(&specialFread);
|
||||
}
|
||||
|
||||
MemoryImage readBmp(in ubyte[] data) {
|
||||
const(ubyte)[] current = data;
|
||||
void specialFread(void* tgt, size_t size) {
|
||||
while(size) {
|
||||
*cast(ubyte*)(tgt) = current[0];
|
||||
current = current[1 .. $];
|
||||
tgt++;
|
||||
size--;
|
||||
}
|
||||
}
|
||||
|
||||
return readBmpIndirect(&specialFread);
|
||||
}
|
||||
|
||||
MemoryImage readBmpIndirect(void delegate(void*, size_t) fread) {
|
||||
uint read4() { uint what; fread(&what, 4); return what; }
|
||||
ushort read2(){ ushort what; fread(&what, 2); return what; }
|
||||
ubyte read1(){ ubyte what; fread(&what, 1); return what; }
|
||||
|
||||
void require1(ubyte t, size_t line = __LINE__) {
|
||||
if(read1() != t)
|
||||
|
@ -234,6 +258,7 @@ MemoryImage readBmp(string filename) {
|
|||
}
|
||||
|
||||
void writeBmp(MemoryImage img, string filename) {
|
||||
import core.stdc.stdio;
|
||||
FILE* fp = fopen((filename ~ "\0").ptr, "wb".ptr);
|
||||
if(fp is null)
|
||||
throw new Exception("can't open save file");
|
||||
|
@ -351,13 +376,15 @@ void writeBmp(MemoryImage img, string filename) {
|
|||
}
|
||||
}
|
||||
|
||||
/*
|
||||
/+
|
||||
void main() {
|
||||
import simpledisplay;
|
||||
//import std.file;
|
||||
//auto img = readBmp(cast(ubyte[]) std.file.read("/home/me/test2.bmp"));
|
||||
auto img = readBmp("/home/me/test2.bmp");
|
||||
import std.stdio;
|
||||
writeln((cast(Object)img).toString());
|
||||
displayImage(Image.fromMemoryImage(img));
|
||||
//img.writeBmp("/home/me/test2.bmp");
|
||||
}
|
||||
*/
|
||||
+/
|
||||
|
|
21
jsvar.d
21
jsvar.d
|
@ -116,7 +116,7 @@ void main() {
|
|||
}
|
||||
|
||||
// the WrappedNativeObject is disgusting
|
||||
// but works.
|
||||
// but works. sort of.
|
||||
/*
|
||||
Foop foop2;
|
||||
|
||||
|
@ -169,6 +169,18 @@ void main() {
|
|||
a;
|
||||
}, globals));
|
||||
|
||||
/*
|
||||
globals.minigui = json!q{};
|
||||
import arsd.minigui;
|
||||
globals.minigui.createWindow = {
|
||||
var v;
|
||||
auto mw = new MainWindow();
|
||||
v._object = new OpaqueNativeObject!(MainWindow)(mw);
|
||||
v.loop = { mw.loop(); };
|
||||
return v;
|
||||
};
|
||||
*/
|
||||
|
||||
repl(globals);
|
||||
|
||||
writeln("BACK IN D!");
|
||||
|
@ -1146,6 +1158,7 @@ struct var {
|
|||
}
|
||||
}
|
||||
|
||||
// this doesn't really work
|
||||
class WrappedNativeObject(T, bool wrapData = true) : PrototypeObject {
|
||||
T nativeObject;
|
||||
|
||||
|
@ -1246,9 +1259,9 @@ class OpaqueNativeObject(T) : PrototypeObject {
|
|||
this.item = t;
|
||||
}
|
||||
|
||||
override string toString() const {
|
||||
return item.toString();
|
||||
}
|
||||
//override string toString() const {
|
||||
//return item.toString();
|
||||
//}
|
||||
|
||||
override OpaqueNativeObject!T copy() {
|
||||
auto n = new OpaqueNativeObject!T(item);
|
||||
|
|
574
minigui.d
574
minigui.d
|
@ -1,22 +1,136 @@
|
|||
// http://msdn.microsoft.com/en-us/library/windows/desktop/bb775498%28v=vs.85%29.aspx
|
||||
|
||||
/// FOR BEST RESULTS: be sure to link with the appropriate subsystem command
|
||||
/// -L/SUBSYSTEM:WINDOWS:5.0
|
||||
/// otherwise you'll get a console and other visual bugs.
|
||||
module arsd.minigui;
|
||||
|
||||
/*
|
||||
STILL NEEDED:
|
||||
* combo box. (this is diff than select because you can free-form edit too. more like a lineedit with autoselect)
|
||||
* slider
|
||||
* listbox
|
||||
* spinner
|
||||
* label?
|
||||
* rich text
|
||||
*/
|
||||
|
||||
abstract class ComboboxBase : Widget {
|
||||
// if the user can enter arbitrary data, we want to use 2 == CBS_DROPDOWN
|
||||
// or to always show the list, we want CBS_SIMPLE == 1
|
||||
version(win32_widgets)
|
||||
this(uint style, Widget parent = null) {
|
||||
super(parent);
|
||||
parentWindow = parent.parentWindow;
|
||||
createWin32Window(this, "ComboBox", null, style);
|
||||
}
|
||||
|
||||
private string[] options;
|
||||
private int selection = -1;
|
||||
|
||||
void addOption(string s) {
|
||||
options ~= s;
|
||||
version(win32_widgets)
|
||||
SendMessageA(hwnd, 323 /*CB_ADDSTRING*/, 0, cast(LPARAM) toStringzInternal(s));
|
||||
}
|
||||
|
||||
void setSelection(int idx) {
|
||||
selection = idx;
|
||||
version(win32_widgets)
|
||||
SendMessageA(hwnd, 334 /*CB_SETCURSEL*/, idx, 0);
|
||||
}
|
||||
|
||||
version(win32_widgets)
|
||||
override void handleWmCommand(ushort cmd, ushort id) {
|
||||
selection = SendMessageA(hwnd, 327 /* CB_GETCURSEL */, 0, 0);
|
||||
auto event = new Event("changed", this);
|
||||
event.dispatch();
|
||||
}
|
||||
}
|
||||
|
||||
class DropDownSelection : ComboboxBase {
|
||||
this(Widget parent = null) {
|
||||
version(win32_widgets)
|
||||
super(3 /* CBS_DROPDOWNLIST */, parent);
|
||||
}
|
||||
}
|
||||
|
||||
class FreeEntrySelection : ComboboxBase {
|
||||
this(Widget parent = null) {
|
||||
version(win32_widgets)
|
||||
super(2 /* CBS_DROPDOWN */, parent);
|
||||
}
|
||||
}
|
||||
|
||||
class ComboBox : ComboboxBase {
|
||||
this(Widget parent = null) {
|
||||
version(win32_widgets)
|
||||
super(1 /* CBS_SIMPLE */, parent);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/+
|
||||
class Spinner : Widget {
|
||||
version(win32_widgets)
|
||||
this(Widget parent = null) {
|
||||
super(parent);
|
||||
parentWindow = parent.parentWindow;
|
||||
auto hlayout = new HorizontalLayout(this);
|
||||
lineEdit = new LineEdit(hlayout);
|
||||
upDownControl = new UpDownControl(hlayout);
|
||||
}
|
||||
|
||||
LineEdit lineEdit;
|
||||
UpDownControl upDownControl;
|
||||
}
|
||||
|
||||
class UpDownControl : Widget {
|
||||
version(win32_widgets)
|
||||
this(Widget parent = null) {
|
||||
super(parent);
|
||||
parentWindow = parent.parentWindow;
|
||||
createWin32Window(this, "msctls_updown32", null, 4/*UDS_ALIGNRIGHT*/| 2 /* UDS_SETBUDDYINT */ | 16 /* UDS_AUTOBUDDY */ | 32 /* UDS_ARROWKEYS */);
|
||||
}
|
||||
|
||||
override int minHeight() { return Window.lineHeight; }
|
||||
override int maxHeight() { return Window.lineHeight * 3/2; }
|
||||
|
||||
override int minWidth() { return Window.lineHeight * 3/2; }
|
||||
override int maxWidth() { return Window.lineHeight * 3/2; }
|
||||
}
|
||||
+/
|
||||
|
||||
class DataView : Widget {
|
||||
// this is the omnibus data viewer
|
||||
// the internal data layout is something like:
|
||||
// string[string][] but also each node can have parents
|
||||
}
|
||||
|
||||
|
||||
// http://msdn.microsoft.com/en-us/library/windows/desktop/bb775491(v=vs.85).aspx#PROGRESS_CLASS
|
||||
|
||||
// http://svn.dsource.org/projects/bindings/trunk/win32/commctrl.d
|
||||
|
||||
// FIXME: menus should prolly capture the mouse. ugh i kno.
|
||||
|
||||
import simpledisplay;
|
||||
public import simpledisplay;
|
||||
|
||||
// this is a hack to call the original window procedure on native win32 widgets if our event listener thing prevents default.
|
||||
private bool lastDefaultPrevented;
|
||||
|
||||
version(Windows) {
|
||||
// use native widgets when available unless specifically asked otherwise
|
||||
version(custom_widgets) {}
|
||||
else {
|
||||
version(custom_widgets) {
|
||||
enum bool UsingCustomWidgets = true;
|
||||
} else {
|
||||
version = win32_widgets;
|
||||
enum bool UsingCustomWidgets = false;
|
||||
}
|
||||
// and native theming when needed
|
||||
version = win32_theming;
|
||||
//version = win32_theming;
|
||||
} else {
|
||||
enum bool UsingCustomWidgets = true;
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -31,11 +145,30 @@ version(Windows) {
|
|||
connect(paste, &textEdit.insertTextAtCarat);
|
||||
|
||||
would be nice.
|
||||
|
||||
|
||||
|
||||
I kinda want an omnibus dataview that combines list, tree,
|
||||
and table - it can be switched dynamically between them.
|
||||
|
||||
Flattening policy: only show top level, show recursive, show grouped
|
||||
List styles: plain list (e.g. <ul>), tiles (some details next to it), icons (like Windows explorer)
|
||||
|
||||
Single select, multi select, organization, drag+drop
|
||||
*/
|
||||
|
||||
enum windowBackgroundColor = Color(190, 190, 190);
|
||||
static if(UsingSimpledisplayX11)
|
||||
enum windowBackgroundColor = Color(220, 220, 220);
|
||||
|
||||
private const(char)* toStringzInternal(string s) { return (s ~ '\0').ptr; }
|
||||
private const(wchar)* toWstringzInternal(in char[] s) {
|
||||
wchar[] str;
|
||||
str.reserve(s.length + 1);
|
||||
foreach(dchar ch; s)
|
||||
str ~= ch;
|
||||
str ~= '\0';
|
||||
return str.ptr;
|
||||
}
|
||||
|
||||
class Action {
|
||||
version(win32_widgets) {
|
||||
|
@ -75,6 +208,7 @@ class Action {
|
|||
basic clipboard
|
||||
|
||||
* radio box
|
||||
splitter
|
||||
toggle buttons (optionally mutually exclusive, like in Paint)
|
||||
label, rich text display, multi line plain text (selectable)
|
||||
* fieldset
|
||||
|
@ -131,16 +265,46 @@ enum LinePreference {
|
|||
}
|
||||
*/
|
||||
|
||||
mixin template Padding(string code) {
|
||||
override int paddingLeft() { return mixin(code);}
|
||||
override int paddingRight() { return mixin(code);}
|
||||
override int paddingTop() { return mixin(code);}
|
||||
override int paddingBottom() { return mixin(code);}
|
||||
}
|
||||
|
||||
mixin template Margin(string code) {
|
||||
override int marginLeft() { return mixin(code);}
|
||||
override int marginRight() { return mixin(code);}
|
||||
override int marginTop() { return mixin(code);}
|
||||
override int marginBottom() { return mixin(code);}
|
||||
}
|
||||
|
||||
|
||||
mixin template LayoutInfo() {
|
||||
int minWidth() { return 0; }
|
||||
int minHeight() { return 0; }
|
||||
int minHeight() {
|
||||
// default widgets have a vertical layout, therefore the minimum height is the sum of the contents
|
||||
int sum = 0;
|
||||
foreach(child; children) {
|
||||
sum += child.minHeight();
|
||||
sum += child.marginTop();
|
||||
}
|
||||
|
||||
return sum;
|
||||
}
|
||||
int maxWidth() { return int.max; }
|
||||
int maxHeight() { return int.max; }
|
||||
int widthStretchiness() { return 1; }
|
||||
int heightStretchiness() { return 1; }
|
||||
|
||||
int margin() { return 0; }
|
||||
int padding() { return 0; }
|
||||
int marginLeft() { return 0; }
|
||||
int marginRight() { return 0; }
|
||||
int marginTop() { return 0; }
|
||||
int marginBottom() { return 0; }
|
||||
int paddingLeft() { return 0; }
|
||||
int paddingRight() { return 0; }
|
||||
int paddingTop() { return 0; }
|
||||
int paddingBottom() { return 0; }
|
||||
//LinePreference linePreference() { return LinePreference.PreferOwnLine; }
|
||||
|
||||
void recomputeChildLayout() {
|
||||
|
@ -155,12 +319,24 @@ void recomputeChildLayout(string relevantMeasure)(Widget parent) {
|
|||
|
||||
if(parent.children.length == 0)
|
||||
return;
|
||||
|
||||
enum firstThingy = relevantMeasure == "height" ? "Top" : "Left";
|
||||
enum secondThingy = relevantMeasure == "height" ? "Bottom" : "Right";
|
||||
|
||||
// my own width and height should already be set by the caller of this function...
|
||||
int spaceRemaining = mixin("parent." ~ relevantMeasure) - parent.padding() * 2;
|
||||
int spaceRemaining = mixin("parent." ~ relevantMeasure) -
|
||||
mixin("parent.padding"~firstThingy~"()") -
|
||||
mixin("parent.padding"~secondThingy~"()");
|
||||
|
||||
int stretchinessSum;
|
||||
int lastMargin = 0;
|
||||
foreach(child; parent.children) {
|
||||
static if(calcingV) {
|
||||
child.width = parent.width - child.margin() * 2 - parent.padding() * 2; // block element style
|
||||
child.width = parent.width -
|
||||
mixin("child.margin"~firstThingy~"()") -
|
||||
mixin("child.margin"~secondThingy~"()") -
|
||||
mixin("parent.padding"~firstThingy~"()") -
|
||||
mixin("parent.padding"~secondThingy~"()");
|
||||
if(child.width < 0)
|
||||
child.width = 0;
|
||||
if(child.width > child.maxWidth())
|
||||
|
@ -169,20 +345,32 @@ void recomputeChildLayout(string relevantMeasure)(Widget parent) {
|
|||
} else {
|
||||
if(child.height < 0)
|
||||
child.height = 0;
|
||||
child.height = parent.height - child.margin() * 2 - parent.padding() * 2;
|
||||
child.height = parent.height -
|
||||
mixin("child.margin"~firstThingy~"()") -
|
||||
mixin("child.margin"~secondThingy~"()") -
|
||||
mixin("parent.padding"~firstThingy~"()") -
|
||||
mixin("parent.padding"~secondThingy~"()");
|
||||
if(child.height > child.maxHeight())
|
||||
child.height = child.maxHeight();
|
||||
child.width = child.minWidth();
|
||||
}
|
||||
|
||||
spaceRemaining -= mixin("child." ~ relevantMeasure);
|
||||
|
||||
int thisMargin = mymax(lastMargin, mixin("child.margin"~firstThingy~"()"));
|
||||
auto margin = mixin("child.margin" ~ secondThingy ~ "()");
|
||||
lastMargin = margin;
|
||||
spaceRemaining -= thisMargin + margin;
|
||||
stretchinessSum += mixin("child." ~ relevantMeasure ~ "Stretchiness()");
|
||||
}
|
||||
|
||||
while(stretchinessSum) {
|
||||
|
||||
while(spaceRemaining > 0 && stretchinessSum) {
|
||||
//import std.stdio; writeln("str ", stretchinessSum);
|
||||
auto spacePerChild = spaceRemaining / stretchinessSum;
|
||||
if(spacePerChild == 0)
|
||||
if(spacePerChild <= 0)
|
||||
break;
|
||||
int previousSpaceRemaining = spaceRemaining;
|
||||
stretchinessSum = 0;
|
||||
foreach(child; parent.children) {
|
||||
static if(calcingV)
|
||||
|
@ -200,35 +388,42 @@ void recomputeChildLayout(string relevantMeasure)(Widget parent) {
|
|||
mixin("child." ~ relevantMeasure) += spaceAdjustment;
|
||||
spaceRemaining -= spaceAdjustment;
|
||||
if(mixin("child." ~ relevantMeasure) > maximum) {
|
||||
auto diff = maximum - mixin("child." ~ relevantMeasure);
|
||||
auto diff = mixin("child." ~ relevantMeasure) - maximum;
|
||||
mixin("child." ~ relevantMeasure) -= diff;
|
||||
spaceRemaining += diff;
|
||||
} else if(mixin("child." ~ relevantMeasure) < maximum) {
|
||||
stretchinessSum += mixin("child." ~ relevantMeasure ~ "Stretchiness()");
|
||||
}
|
||||
|
||||
spaceRemaining -= child.margin();
|
||||
}
|
||||
|
||||
if(spaceRemaining == previousSpaceRemaining)
|
||||
break; // apparently nothing more we can do
|
||||
}
|
||||
|
||||
int currentPos = parent.padding();
|
||||
lastMargin = 0;
|
||||
int currentPos = mixin("parent.padding"~firstThingy~"()");
|
||||
foreach(child; parent.children) {
|
||||
currentPos += child.margin();
|
||||
auto margin = mixin("child.margin" ~ secondThingy ~ "()");
|
||||
int thisMargin = mymax(lastMargin, mixin("child.margin"~firstThingy~"()"));
|
||||
currentPos += thisMargin;
|
||||
static if(calcingV) {
|
||||
child.x = parent.padding() + child.margin();
|
||||
child.x = parent.paddingLeft() + child.marginLeft();
|
||||
child.y = currentPos;
|
||||
} else {
|
||||
child.x = currentPos;
|
||||
child.y = parent.padding() + child.margin();
|
||||
child.y = parent.paddingTop() + child.marginTop();
|
||||
|
||||
}
|
||||
currentPos += mixin("child." ~ relevantMeasure);
|
||||
currentPos += child.margin();
|
||||
currentPos += margin;
|
||||
lastMargin = margin;
|
||||
|
||||
child.recomputeChildLayout();
|
||||
}
|
||||
}
|
||||
|
||||
int mymax(int a, int b) { return a > b ? a : b; }
|
||||
|
||||
/+
|
||||
mixin template StyleInfo(string windowType) {
|
||||
version(win32_theming)
|
||||
|
@ -244,7 +439,7 @@ mixin template StyleInfo(string windowType) {
|
|||
+/
|
||||
|
||||
// OK so we need to make getting at the native window stuff possible in simpledisplay.d
|
||||
// and here, it must be integratable with the layout, the event system, and not be painted over.
|
||||
// and here, it must be integrable with the layout, the event system, and not be painted over.
|
||||
version(win32_widgets) {
|
||||
extern(Windows)
|
||||
int HookedWndProc(HWND hWnd, UINT iMessage, WPARAM wParam, LPARAM lParam) nothrow {
|
||||
|
@ -276,6 +471,8 @@ version(win32_widgets) {
|
|||
else
|
||||
phwnd = p.parentWindow.win.impl.hwnd;
|
||||
|
||||
assert(phwnd !is null);
|
||||
|
||||
style |= WS_VISIBLE | WS_CHILD;
|
||||
p.hwnd = CreateWindow(toStringzInternal(className), toStringzInternal(windowText), style,
|
||||
CW_USEDEFAULT, CW_USEDEFAULT, 100, 100,
|
||||
|
@ -309,6 +506,16 @@ class Widget {
|
|||
mixin EventStuff!();
|
||||
mixin LayoutInfo!();
|
||||
|
||||
static if(UsingSimpledisplayX11) {
|
||||
// see: http://tronche.com/gui/x/xlib/appendix/b/
|
||||
protected Cursor cursor;
|
||||
|
||||
// maybe I can do something similar cross platform
|
||||
}
|
||||
|
||||
version(win32_widgets)
|
||||
void handleWmCommand(ushort cmd, ushort id) {}
|
||||
|
||||
string statusTip;
|
||||
// string toolTip;
|
||||
// string helpText;
|
||||
|
@ -404,7 +611,7 @@ class Widget {
|
|||
else {
|
||||
assert(position < children.length);
|
||||
children.length = children.length + 1;
|
||||
for(int i = children.length - 1; i > position; i--)
|
||||
for(int i = cast(int) children.length - 1; i > position; i--)
|
||||
children[i] = children[i - 1];
|
||||
children[position] = w;
|
||||
}
|
||||
|
@ -472,23 +679,65 @@ class VerticalLayout : Widget {
|
|||
// intentionally blank - widget's default is vertical layout right now
|
||||
}
|
||||
class HorizontalLayout : Widget {
|
||||
this(Widget parent = null) { super(parent); }
|
||||
override void recomputeChildLayout() {
|
||||
.recomputeChildLayout!"width"(this);
|
||||
}
|
||||
|
||||
override int minHeight() {
|
||||
int largest = 0;
|
||||
int margins = 0;
|
||||
int lastMargin = 0;
|
||||
foreach(child; children) {
|
||||
auto mh = child.minHeight();
|
||||
if(mh > largest)
|
||||
largest = mh;
|
||||
margins += mymax(lastMargin, child.marginTop());
|
||||
lastMargin = child.marginBottom();
|
||||
}
|
||||
return largest + margins;
|
||||
}
|
||||
|
||||
override int maxHeight() {
|
||||
int largest = 0;
|
||||
int margins = 0;
|
||||
int lastMargin = 0;
|
||||
foreach(child; children) {
|
||||
auto mh = child.maxHeight();
|
||||
if(mh > largest)
|
||||
largest = mh;
|
||||
margins += mymax(lastMargin, child.marginTop());
|
||||
lastMargin = child.marginBottom();
|
||||
}
|
||||
return largest + margins;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
class Window : Widget {
|
||||
int mouseCaptureCount = 0;
|
||||
Widget mouseCapturedBy;
|
||||
void captureMouse(Widget byWhom) {
|
||||
assert(mouseCapturedBy is null || byWhom is mouseCapturedBy);
|
||||
mouseCaptureCount++;
|
||||
mouseCapturedBy = byWhom;
|
||||
}
|
||||
void releaseMouseCapture() {
|
||||
mouseCaptureCount--;
|
||||
mouseCapturedBy = null;
|
||||
}
|
||||
|
||||
static int lineHeight;
|
||||
|
||||
Widget focusedWidget;
|
||||
|
||||
SimpleWindow win;
|
||||
this(int width = 500, int height = 500) {
|
||||
this(int width = 500, int height = 500, string title = null) {
|
||||
super(null);
|
||||
|
||||
win = new SimpleWindow(width, height);
|
||||
win = new SimpleWindow(width, height, title, OpenGlOptions.no, Resizablity.allowResizing);
|
||||
this.width = win.width;
|
||||
this.height = win.height;
|
||||
this.parentWindow = this;
|
||||
|
@ -511,6 +760,24 @@ class Window : Widget {
|
|||
lineHeight = painter.fontHeight() * 5 / 4;
|
||||
}
|
||||
|
||||
version(win32_widgets) {
|
||||
this.paint = (ScreenPainter painter) {
|
||||
/*
|
||||
RECT rect;
|
||||
rect.right = this.width;
|
||||
rect.bottom = this.height;
|
||||
DrawThemeBackground(theme, painter.impl.hdc, 4, 1, &rect, null);
|
||||
*/
|
||||
// 3dface is used as window backgrounds by Windows too, so that's why I'm using it here
|
||||
auto b = SelectObject(painter.impl.hdc, GetSysColorBrush(COLOR_3DFACE));
|
||||
auto p = SelectObject(painter.impl.hdc, GetStockObject(NULL_PEN));
|
||||
// since the pen is null, to fill the whole space, we need the +1 on both.
|
||||
Rectangle(painter.impl.hdc, 0, 0, this.width + 1, this.height + 1);
|
||||
SelectObject(painter.impl.hdc, p);
|
||||
SelectObject(painter.impl.hdc, b);
|
||||
};
|
||||
}
|
||||
else
|
||||
this.paint = (ScreenPainter painter) {
|
||||
painter.fillColor = windowBackgroundColor;
|
||||
painter.drawRectangle(Point(0, 0), this.width, this.height);
|
||||
|
@ -545,6 +812,11 @@ class Window : Widget {
|
|||
override bool dispatchMouseEvent(MouseEvent ev) {
|
||||
auto ele = widgetAtPoint(this, ev.x, ev.y);
|
||||
|
||||
if(mouseCapturedBy !is null) {
|
||||
if(ele !is mouseCapturedBy && !mouseCapturedBy.isAParentOf(ele))
|
||||
ele = this;
|
||||
}
|
||||
|
||||
if(ev.type == 1) {
|
||||
mouseLastDownOn = ele;
|
||||
auto event = new Event("mousedown", ele);
|
||||
|
@ -558,6 +830,7 @@ class Window : Widget {
|
|||
event = new Event("click", ele);
|
||||
event.clientX = ev.x;
|
||||
event.clientY = ev.y;
|
||||
event.button = ev.button;
|
||||
event.dispatch();
|
||||
}
|
||||
} else if(ev.type == 0) {
|
||||
|
@ -573,6 +846,9 @@ class Window : Widget {
|
|||
event = new Event("mouseenter", ele);
|
||||
event.relatedTarget = mouseLastOver;
|
||||
event.sendDirectly();
|
||||
|
||||
static if(UsingSimpledisplayX11)
|
||||
XDefineCursor(XDisplayConnection.get(), ele.parentWindow.win.impl.window, ele.cursor);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -625,7 +901,7 @@ class MainWindow : Window {
|
|||
if(this.statusBar !is null && event.target.statusTip.length)
|
||||
this.statusBar.parts[0].content = event.target.statusTip;
|
||||
else if(this.statusBar !is null && _this.statusTip.length)
|
||||
this.statusBar.parts[0].content = _this.statusTip ~ " " ~ event.target.toString();
|
||||
this.statusBar.parts[0].content = _this.statusTip; // ~ " " ~ event.target.toString();
|
||||
};
|
||||
|
||||
version(win32_widgets)
|
||||
|
@ -648,10 +924,9 @@ class MainWindow : Window {
|
|||
event.dispatch();
|
||||
*/
|
||||
} else {
|
||||
auto buttonHandle = cast(HWND) lParam;
|
||||
if(auto widget = buttonHandle in Widget.nativeMapping) {
|
||||
auto event = new Event("triggered", *widget);
|
||||
event.dispatch();
|
||||
auto handle = cast(HWND) lParam;
|
||||
if(auto widgetp = handle in Widget.nativeMapping) {
|
||||
(*widgetp).handleWmCommand(HIWORD(wParam), LOWORD(wParam));
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
@ -718,7 +993,15 @@ class MainWindow : Window {
|
|||
Each button ought to correspond to a menu item.
|
||||
*/
|
||||
class ToolBar : Widget {
|
||||
override int maxHeight() { return 40; }
|
||||
version(win32_widgets) {
|
||||
private const int idealHeight;
|
||||
override int minHeight() { return idealHeight; }
|
||||
override int maxHeight() { return idealHeight; }
|
||||
} else {
|
||||
override int minHeight() { return Window.lineHeight * 3/2; }
|
||||
override int maxHeight() { return Window.lineHeight * 3/2; }
|
||||
}
|
||||
override int heightStretchiness() { return 0; }
|
||||
|
||||
version(win32_widgets)
|
||||
HIMAGELIST imageList;
|
||||
|
@ -743,10 +1026,22 @@ class ToolBar : Widget {
|
|||
|
||||
// FIXME: I_IMAGENONE is if here is no icon
|
||||
foreach(action; actions)
|
||||
buttons ~= TBBUTTON(MAKELONG(action.iconId, 0), action.id, TBSTATE_ENABLED, 0, 0, 0, cast(int) toStringzInternal(action.label));
|
||||
buttons ~= TBBUTTON(MAKELONG(cast(ushort)(action.iconId ? (action.iconId - 1) : -2 /* I_IMAGENONE */), 0), action.id, TBSTATE_ENABLED, 0, 0, 0, cast(int) toStringzInternal(action.label));
|
||||
|
||||
SendMessageA(hwnd, TB_BUTTONSTRUCTSIZE, cast(WPARAM)TBBUTTON.sizeof, 0);
|
||||
SendMessageA(hwnd, TB_ADDBUTTONSA, cast(WPARAM) buttons.length, cast(LPARAM)buttons.ptr);
|
||||
|
||||
/* this seems to make it a vertical toolbar on windows xp... don't actually want that
|
||||
SIZE size;
|
||||
SendMessageA(hwnd, TB_GETIDEALSIZE, true, cast(LPARAM) &size);
|
||||
idealHeight = size.cy;
|
||||
*/
|
||||
|
||||
RECT rect;
|
||||
GetWindowRect(hwnd, &rect);
|
||||
idealHeight = rect.bottom - rect.top + 10; // the +10 is a hack since the size right now doesn't look right on a real Windows XP box
|
||||
|
||||
assert(idealHeight);
|
||||
} else {
|
||||
foreach(action; actions)
|
||||
addChild(new ToolButton(action));
|
||||
|
@ -773,11 +1068,15 @@ class ToolButton : Button {
|
|||
};
|
||||
|
||||
paint = (ScreenPainter painter) {
|
||||
painter.outlineColor = Color.black;
|
||||
painter.outlineColor = windowBackgroundColor;
|
||||
if(isHovering) {
|
||||
painter.fillColor = Color.transparent;
|
||||
painter.drawRectangle(Point(0, 0), width, height);
|
||||
painter.fillColor = lighten(windowBackgroundColor, 0.8);
|
||||
} else {
|
||||
painter.fillColor = windowBackgroundColor;
|
||||
}
|
||||
|
||||
painter.drawRectangle(Point(1, 1), width, height);
|
||||
painter.outlineColor = Color.black;
|
||||
painter.drawText(Point(0, 0), action.label, Point(width, height), TextAlignment.Center | TextAlignment.VerticalCenter);
|
||||
};
|
||||
}
|
||||
|
@ -786,7 +1085,6 @@ class ToolButton : Button {
|
|||
Action action;
|
||||
|
||||
override int maxWidth() { return 40; }
|
||||
override int minWidth() { return 40; }
|
||||
}
|
||||
|
||||
|
||||
|
@ -842,8 +1140,8 @@ class MenuBar : Widget {
|
|||
.recomputeChildLayout!"width"(this);
|
||||
}
|
||||
|
||||
override int maxHeight() { return Window.lineHeight; }
|
||||
override int minHeight() { return Window.lineHeight; }
|
||||
override int maxHeight() { return Window.lineHeight + 4; }
|
||||
override int minHeight() { return Window.lineHeight + 4; }
|
||||
|
||||
}
|
||||
|
||||
|
@ -863,7 +1161,7 @@ class StatusBar : Widget {
|
|||
@disable this();
|
||||
this(StatusBar owner) { this.owner = owner; }
|
||||
//@disable this(this);
|
||||
@property int length() { return owner.partsArray.length; }
|
||||
@property int length() { return cast(int) owner.partsArray.length; }
|
||||
private StatusBar owner;
|
||||
private this(StatusBar owner, Part[] parts) {
|
||||
this.owner.partsArray = parts;
|
||||
|
@ -878,7 +1176,7 @@ class StatusBar : Widget {
|
|||
Part opOpAssign(string op : "~" )(Part p) {
|
||||
assert(owner.partsArray.length < 255);
|
||||
p.owner = this.owner;
|
||||
p.idx = owner.partsArray.length;
|
||||
p.idx = cast(int) owner.partsArray.length;
|
||||
owner.partsArray ~= p;
|
||||
version(win32_widgets) {
|
||||
int[256] pos;
|
||||
|
@ -937,6 +1235,11 @@ class StatusBar : Widget {
|
|||
version(win32_widgets) {
|
||||
parentWindow = parent.parentWindow;
|
||||
createWin32Window(this, "msctls_statusbar32", "D rox", 0);
|
||||
|
||||
RECT rect;
|
||||
GetWindowRect(hwnd, &rect);
|
||||
idealHeight = rect.bottom - rect.top;
|
||||
assert(idealHeight);
|
||||
} else {
|
||||
this.paint = (ScreenPainter painter) {
|
||||
painter.outlineColor = Color.black;
|
||||
|
@ -951,8 +1254,14 @@ class StatusBar : Widget {
|
|||
}
|
||||
}
|
||||
|
||||
override int maxHeight() { return Window.lineHeight; }
|
||||
override int minHeight() { return Window.lineHeight; }
|
||||
version(win32_widgets) {
|
||||
private const int idealHeight;
|
||||
override int maxHeight() { return idealHeight; }
|
||||
override int minHeight() { return idealHeight; }
|
||||
} else {
|
||||
override int maxHeight() { return Window.lineHeight + 4; }
|
||||
override int minHeight() { return Window.lineHeight + 4; }
|
||||
}
|
||||
}
|
||||
|
||||
/// Displays an in-progress indicator without known values
|
||||
|
@ -1038,11 +1347,17 @@ class ProgressBar : Widget {
|
|||
}
|
||||
|
||||
class Fieldset : Widget {
|
||||
override int padding() { return 8; }
|
||||
override int margin() { return 4; }
|
||||
version(win32_widgets)
|
||||
override int paddingTop() { return Window.lineHeight; }
|
||||
else
|
||||
override int paddingTop() { return Window.lineHeight / 2 + 2; }
|
||||
override int paddingBottom() { return 6; }
|
||||
override int paddingLeft() { return 6; }
|
||||
override int paddingRight() { return 6; }
|
||||
mixin Margin!q{ Window.lineHeight / 2 + 2 };
|
||||
|
||||
string legend;
|
||||
/*
|
||||
|
||||
version(win32_widgets)
|
||||
this(string legend, Widget parent = null) {
|
||||
super(parent);
|
||||
|
@ -1051,23 +1366,43 @@ class Fieldset : Widget {
|
|||
createWin32Window(this, "button", legend, BS_GROUPBOX);
|
||||
}
|
||||
else
|
||||
*/
|
||||
this(string legend, Widget parent = null) {
|
||||
super(parent);
|
||||
this.legend = legend;
|
||||
parentWindow = parent.parentWindow;
|
||||
this.paint = (ScreenPainter painter) {
|
||||
painter.fillColor = Color(220, 220, 220);
|
||||
painter.outlineColor = Color.black;
|
||||
painter.fillColor = Color.transparent;
|
||||
painter.pen = Pen(Color.black, 1);
|
||||
painter.drawRectangle(Point(0, 0), width, height);
|
||||
|
||||
auto tx = painter.textSize(legend);
|
||||
painter.outlineColor = Color.transparent;
|
||||
|
||||
static if(UsingSimpledisplayX11) {
|
||||
painter.fillColor = windowBackgroundColor;
|
||||
painter.drawRectangle(Point(8, -tx.height/2), tx.width, tx.height);
|
||||
} else version(Windows) {
|
||||
auto b = SelectObject(painter.impl.hdc, GetSysColorBrush(COLOR_3DFACE));
|
||||
painter.drawRectangle(Point(8, -tx.height/2), tx.width, tx.height);
|
||||
SelectObject(painter.impl.hdc, b);
|
||||
} else static assert(0);
|
||||
painter.outlineColor = Color.black;
|
||||
painter.drawText(Point(8, -tx.height / 2), legend);
|
||||
};
|
||||
}
|
||||
|
||||
override int maxHeight() {
|
||||
auto m = padding * 2;
|
||||
foreach(child; children)
|
||||
auto m = paddingTop() + paddingBottom();
|
||||
foreach(child; children) {
|
||||
m += child.maxHeight();
|
||||
return m;
|
||||
m += child.marginBottom();
|
||||
m += child.marginTop();
|
||||
}
|
||||
return m + 6;
|
||||
}
|
||||
|
||||
override int minHeight() {
|
||||
return super.minHeight() + Window.lineHeight + 4;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1081,23 +1416,25 @@ class Menu : Widget {
|
|||
parentWindow.redraw();
|
||||
|
||||
parentWindow.removeEventListener("mousedown", &remove);
|
||||
parentWindow.releaseMouseCapture();
|
||||
}
|
||||
|
||||
version(win32_widgets) {} else
|
||||
void popup(Widget parent) {
|
||||
assert(parentWindow !is null);
|
||||
auto pos = getChildPositionRelativeToParentOrigin(parent);
|
||||
this.x = pos[0];
|
||||
this.y = pos[1] + parent.height;
|
||||
this.width = parent.width;
|
||||
this.width = 150;
|
||||
if(this.children.length)
|
||||
this.height = this.children.length * this.children[0].maxHeight();
|
||||
this.height = cast(int) this.children.length * this.children[0].maxHeight();
|
||||
else
|
||||
this.height = 4;
|
||||
this.recomputeChildLayout();
|
||||
|
||||
this.paint = (ScreenPainter painter) {
|
||||
painter.outlineColor = Color.black;
|
||||
painter.fillColor = Color(190, 190, 190);
|
||||
painter.fillColor = lighten(windowBackgroundColor, 0.8);
|
||||
painter.drawRectangle(Point(0, 0), width, height);
|
||||
};
|
||||
|
||||
|
@ -1109,6 +1446,8 @@ class Menu : Widget {
|
|||
ev.stopPropagation();
|
||||
};
|
||||
|
||||
parentWindow.captureMouse(this);
|
||||
|
||||
foreach(child; children)
|
||||
child.parentWindow = this.parentWindow;
|
||||
|
||||
|
@ -1157,10 +1496,14 @@ class MenuItem : MouseActivatedWidget {
|
|||
Action action;
|
||||
string label;
|
||||
|
||||
override int maxHeight() { return Window.lineHeight; }
|
||||
override int minWidth() { return Window.lineHeight * label.length; }
|
||||
override int maxWidth() { return Window.lineHeight / 2 * label.length; }
|
||||
this(string lbl, Window parent = null) {
|
||||
override int maxHeight() { return Window.lineHeight + 4; }
|
||||
override int minWidth() { return Window.lineHeight * cast(int) label.length + 8; }
|
||||
override int maxWidth() {
|
||||
if(cast(MenuBar) parent)
|
||||
return Window.lineHeight / 2 * cast(int) label.length + 8;
|
||||
return int.max;
|
||||
}
|
||||
this(string lbl, Widget parent = null) {
|
||||
super(parent);
|
||||
label = lbl;
|
||||
version(win32_widgets) {} else
|
||||
|
@ -1169,11 +1512,11 @@ class MenuItem : MouseActivatedWidget {
|
|||
painter.outlineColor = Color.blue;
|
||||
else
|
||||
painter.outlineColor = Color.black;
|
||||
painter.drawText(Point(0, 0), label, Point(width, height), TextAlignment.Center);
|
||||
painter.drawText(Point(cast(MenuBar) this.parent ? 4 : 20, 2), label, Point(width, height), TextAlignment.Left);
|
||||
};
|
||||
}
|
||||
|
||||
this(Action action, Window parent = null) {
|
||||
this(Action action, Widget parent = null) {
|
||||
assert(action !is null);
|
||||
this(action.label);
|
||||
this.action = action;
|
||||
|
@ -1182,6 +1525,9 @@ class MenuItem : MouseActivatedWidget {
|
|||
//event.dispatch();
|
||||
foreach(handler; action.triggered)
|
||||
handler();
|
||||
|
||||
if(auto pmenu = cast(Menu) this.parent)
|
||||
pmenu.remove();
|
||||
};
|
||||
}
|
||||
}
|
||||
|
@ -1249,6 +1595,7 @@ class Checkbox : MouseActivatedWidget {
|
|||
|
||||
override int maxHeight() { return 16; }
|
||||
override int minHeight() { return 16; }
|
||||
mixin Margin!"4";
|
||||
|
||||
version(win32_widgets)
|
||||
this(string label, Widget parent = null) {
|
||||
|
@ -1288,6 +1635,14 @@ class Checkbox : MouseActivatedWidget {
|
|||
}
|
||||
}
|
||||
|
||||
class VerticalSpacer : Widget {
|
||||
override int maxHeight() { return 20; }
|
||||
override int minHeight() { return 20; }
|
||||
this(Widget parent = null) {
|
||||
super(parent);
|
||||
}
|
||||
}
|
||||
|
||||
class MutuallyExclusiveGroup {
|
||||
MouseActivatedWidget[] members;
|
||||
|
||||
|
@ -1322,7 +1677,7 @@ class Radiobox : MouseActivatedWidget {
|
|||
this(string label, Widget parent = null) {
|
||||
super(parent);
|
||||
height = 16;
|
||||
width = height + 4 + label.length * 16;
|
||||
width = height + 4 + cast(int) label.length * 16;
|
||||
|
||||
this.paint = (ScreenPainter painter) {
|
||||
painter.outlineColor = Color.black;
|
||||
|
@ -1359,6 +1714,12 @@ class Button : MouseActivatedWidget {
|
|||
Color hoverBgColor;
|
||||
Color depressedBgColor;
|
||||
|
||||
version(win32_widgets)
|
||||
override void handleWmCommand(ushort cmd, ushort id) {
|
||||
auto event = new Event("triggered", this);
|
||||
event.dispatch();
|
||||
}
|
||||
|
||||
version(win32_widgets) {} else
|
||||
Color currentButtonColor() {
|
||||
if(isHovering) {
|
||||
|
@ -1408,6 +1769,8 @@ class Button : MouseActivatedWidget {
|
|||
painter.drawText(Point(0, 0), label, Point(width, height), TextAlignment.Center | TextAlignment.VerticalCenter);
|
||||
};
|
||||
}
|
||||
|
||||
override int minHeight() { return Window.lineHeight; }
|
||||
}
|
||||
|
||||
int[2] getChildPositionRelativeToParentOrigin(Widget c) nothrow {
|
||||
|
@ -1435,8 +1798,18 @@ int[2] getChildPositionRelativeToParentHwnd(Widget c) nothrow {
|
|||
return [x, y];
|
||||
}
|
||||
|
||||
class LineEdit : Widget {
|
||||
version(win32_widgets)
|
||||
this(Widget parent = null) {
|
||||
super(parent);
|
||||
parentWindow = parent.parentWindow;
|
||||
createWin32Window(this, "edit", "",
|
||||
WS_BORDER|WS_HSCROLL|ES_AUTOHSCROLL);
|
||||
}
|
||||
}
|
||||
|
||||
class TextEdit : Widget {
|
||||
override int minHeight() { return Window.lineHeight; }
|
||||
override int heightStretchiness() { return 3; }
|
||||
override int widthStretchiness() { return 3; }
|
||||
|
||||
|
@ -1468,6 +1841,8 @@ class TextEdit : Widget {
|
|||
redraw();
|
||||
};
|
||||
|
||||
static if(UsingSimpledisplayX11)
|
||||
cursor = XCreateFontCursor(XDisplayConnection.get(), 152 /* XC_xterm, a text input thingy */);
|
||||
//super();
|
||||
}
|
||||
|
||||
|
@ -1493,10 +1868,10 @@ class MessageBox : Window {
|
|||
this(string message) {
|
||||
super(300, 100);
|
||||
|
||||
auto superPaint = this.paint;
|
||||
this.paint = (ScreenPainter painter) {
|
||||
painter.fillColor = Color(192, 192, 192);
|
||||
painter.drawRectangle(Point(0, 0), this.width, this.height);
|
||||
|
||||
if(superPaint)
|
||||
superPaint(painter);
|
||||
painter.outlineColor = Color.black;
|
||||
painter.drawText(Point(0, 0), message, Point(width, height / 2), TextAlignment.Center | TextAlignment.VerticalCenter);
|
||||
};
|
||||
|
@ -1725,6 +2100,39 @@ Widget widgetAtPoint(Widget starting, int x, int y) {
|
|||
return starting;
|
||||
}
|
||||
|
||||
version(win32_theming) {
|
||||
import std.c.windows.windows;
|
||||
|
||||
alias HANDLE HTHEME;
|
||||
|
||||
// Since dmd doesn't offer uxtheme.lib, I'll load the dll at runtime instead
|
||||
HMODULE uxtheme;
|
||||
static this() {
|
||||
uxtheme = LoadLibraryA("uxtheme.dll");
|
||||
if(uxtheme) {
|
||||
DrawThemeBackground = cast(typeof(DrawThemeBackground)) GetProcAddress(uxtheme, "DrawThemeBackground");
|
||||
OpenThemeData = cast(typeof(OpenThemeData)) GetProcAddress(uxtheme, "OpenThemeData");
|
||||
CloseThemeData = cast(typeof(CloseThemeData)) GetProcAddress(uxtheme, "CloseThemeData");
|
||||
GetThemeSysColorBrush = cast(typeof(GetThemeSysColorBrush)) GetProcAddress(uxtheme, "CloseThemeData");
|
||||
}
|
||||
}
|
||||
|
||||
// everything from here is just win32 headers copy pasta
|
||||
private:
|
||||
extern(Windows):
|
||||
|
||||
HRESULT function(HTHEME, HDC, int, int, in RECT*, in RECT*) DrawThemeBackground;
|
||||
HTHEME function(HWND, LPCWSTR) OpenThemeData;
|
||||
HRESULT function(HTHEME) CloseThemeData;
|
||||
HBRUSH function(HTHEME, int) GetThemeSysColorBrush;
|
||||
|
||||
HMODULE LoadLibraryA(LPCSTR);
|
||||
BOOL FreeLibrary(HMODULE);
|
||||
FARPROC GetProcAddress(HMODULE, LPCSTR);
|
||||
// pragma(lib, "uxtheme");
|
||||
|
||||
BOOL GetClassInfoA(HINSTANCE, LPCSTR, WNDCLASS*);
|
||||
}
|
||||
|
||||
version(win32_widgets) {
|
||||
import std.c.windows.windows;
|
||||
|
@ -1737,7 +2145,7 @@ version(win32_widgets) {
|
|||
// http://msdn.microsoft.com/en-us/library/windows/desktop/bb775507(v=vs.85).aspx
|
||||
INITCOMMONCONTROLSEX ic;
|
||||
ic.dwSize = cast(DWORD) ic.sizeof;
|
||||
ic.dwICC = ICC_WIN95_CLASSES | ICC_BAR_CLASSES | ICC_PROGRESS_CLASS | ICC_COOL_CLASSES;
|
||||
ic.dwICC = ICC_UPDOWN_CLASS | ICC_WIN95_CLASSES | ICC_BAR_CLASSES | ICC_PROGRESS_CLASS | ICC_COOL_CLASSES | ICC_STANDARD_CLASSES;
|
||||
InitCommonControlsEx(&ic);
|
||||
}
|
||||
|
||||
|
@ -1808,9 +2216,15 @@ struct TBBUTTON {
|
|||
|
||||
enum {
|
||||
TB_ADDBUTTONSA = WM_USER + 20,
|
||||
TB_INSERTBUTTONA = WM_USER + 21
|
||||
TB_INSERTBUTTONA = WM_USER + 21,
|
||||
TB_GETIDEALSIZE = WM_USER + 99,
|
||||
}
|
||||
|
||||
struct SIZE {
|
||||
LONG cx;
|
||||
LONG cy;
|
||||
}
|
||||
|
||||
|
||||
enum {
|
||||
TBSTATE_CHECKED = 1,
|
||||
|
@ -1946,7 +2360,8 @@ enum {
|
|||
ICC_WIN95_CLASSES = 255,
|
||||
ICC_DATE_CLASSES = 256,
|
||||
ICC_USEREX_CLASSES = 512,
|
||||
ICC_COOL_CLASSES = 1024
|
||||
ICC_COOL_CLASSES = 1024,
|
||||
ICC_STANDARD_CLASSES = 0x00004000,
|
||||
}
|
||||
|
||||
enum WM_USER = 1024;
|
||||
|
@ -1955,15 +2370,24 @@ enum {
|
|||
|
||||
|
||||
|
||||
version(win32_widgets)
|
||||
enum GenericIcons : ushort {
|
||||
New = STD_FILENEW,
|
||||
Open = STD_FILEOPEN,
|
||||
Save = STD_FILESAVE,
|
||||
}
|
||||
else
|
||||
enum GenericIcons : ushort {
|
||||
New, Open, Save
|
||||
None,
|
||||
// these happen to match the win32 std icons numerically if you just subtract one from the value
|
||||
Cut,
|
||||
Copy,
|
||||
Paste,
|
||||
Undo,
|
||||
Redo,
|
||||
Delete,
|
||||
New,
|
||||
Open,
|
||||
Save,
|
||||
PrintPreview,
|
||||
Properties,
|
||||
Help,
|
||||
Find,
|
||||
Replace,
|
||||
Print,
|
||||
}
|
||||
|
||||
/*
|
||||
|
|
14
oauth.d
14
oauth.d
|
@ -234,6 +234,20 @@ OAuthParams twitter(string apiKey, string apiSecret) {
|
|||
return params;
|
||||
}
|
||||
|
||||
OAuthParams tumblr(string apiKey, string apiSecret) {
|
||||
OAuthParams params;
|
||||
|
||||
params.apiKey = apiKey;
|
||||
params.apiSecret = apiSecret;
|
||||
|
||||
params.baseUrl = "http://www.tumblr.com";
|
||||
params.requestTokenPath = "/oauth/request_token";
|
||||
params.authorizePath = "/oauth/authorize";
|
||||
params.accessTokenPath = "/oauth/access_token";
|
||||
|
||||
return params;
|
||||
}
|
||||
|
||||
OAuthParams linkedIn(string apiKey, string apiSecret) {
|
||||
OAuthParams params;
|
||||
|
||||
|
|
12
png.d
12
png.d
|
@ -26,7 +26,15 @@ import simpledisplay;
|
|||
|
||||
import std.file;
|
||||
void main(string[] args) {
|
||||
auto img = imageFromPng(readPng(cast(ubyte[]) read(args[1])));
|
||||
// older api, the individual functions give you more control if you need it
|
||||
//auto img = imageFromPng(readPng(cast(ubyte[]) read(args[1])));
|
||||
|
||||
// newer api, simpler but less control
|
||||
auto img = readPng(args[1]);
|
||||
|
||||
// displayImage is from simpledisplay and just pops up a window to show the image
|
||||
// simpledisplay's Images are a little different than MemoryImages that this loads,
|
||||
// but conversion is easy
|
||||
displayImage(Image.fromMemoryImage(img));
|
||||
}
|
||||
*/
|
||||
|
@ -452,7 +460,7 @@ PngHeader getHeaderFromFile(string filename) {
|
|||
return getHeader(png);
|
||||
}
|
||||
|
||||
PNG* readPng(ubyte[] data) {
|
||||
PNG* readPng(in ubyte[] data) {
|
||||
auto p = new PNG;
|
||||
|
||||
p.length = data.length;
|
||||
|
|
151
simpledisplay.d
151
simpledisplay.d
|
@ -1,5 +1,8 @@
|
|||
module simpledisplay;
|
||||
|
||||
// FIXME: SIGINT handler is necessary to clean up shared memory handles upon ctrl+c
|
||||
|
||||
|
||||
// Note: if you are using Image on X, you might want to do:
|
||||
/*
|
||||
static if(UsingSimpledisplayX11) {
|
||||
|
@ -10,7 +13,7 @@ module simpledisplay;
|
|||
}
|
||||
|
||||
If the shared memory extension is available though, simpledisplay uses it
|
||||
for a significant speed boost whenever you draw Images.
|
||||
for a significant speed boost whenever you draw large Images.
|
||||
*/
|
||||
|
||||
// CHANGE FROM LAST VERSION: the window background is no longer fixed, so you might want to fill the screen with a particular color before drawing.
|
||||
|
@ -214,17 +217,17 @@ enum RasterOp {
|
|||
|
||||
// being phobos-free keeps the size WAY down
|
||||
private const(char)* toStringz(string s) { return (s ~ '\0').ptr; }
|
||||
private string[] split(string a, char c) {
|
||||
private string[] split(in void[] a, char c) {
|
||||
string[] ret;
|
||||
size_t previous = 0;
|
||||
foreach(i, char ch; a) {
|
||||
foreach(i, char ch; cast(ubyte[]) a) {
|
||||
if(ch == c) {
|
||||
ret ~= a[previous .. i];
|
||||
ret ~= cast(string) a[previous .. i];
|
||||
previous = i + 1;
|
||||
}
|
||||
}
|
||||
if(previous != a.length)
|
||||
ret ~= a[previous .. $];
|
||||
ret ~= cast(string) a[previous .. $];
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
@ -292,6 +295,35 @@ enum TextAlignment : uint {
|
|||
|
||||
public import arsd.color; // no longer stand alone... :-( but i need a common type for this to work with images easily.
|
||||
|
||||
version(X11)
|
||||
enum ModifierState : uint {
|
||||
shift = 1,
|
||||
capsLock = 2,
|
||||
ctrl = 4,
|
||||
alt = 8,
|
||||
numLock = 16,
|
||||
windows = 64,
|
||||
|
||||
// these aren't available on Windows for key events, so don't use them for that unless your app is X only.
|
||||
leftButtonDown = 256,
|
||||
middleButtonDown = 512,
|
||||
rightButtonDown = 1024,
|
||||
}
|
||||
else version(Windows)
|
||||
enum ModifierState : uint {
|
||||
shift = 4,
|
||||
ctrl = 8,
|
||||
|
||||
alt = 256,
|
||||
windows = 512,
|
||||
capsLock = 1024,
|
||||
numLock = 2048,
|
||||
|
||||
leftButtonDown = 1,
|
||||
middleButtonDown = 16,
|
||||
rightButtonDown = 2,
|
||||
}
|
||||
|
||||
struct KeyEvent {
|
||||
/// see table below. Always use the symbolic names, even for ASCII characters, since the actual numbers vary across platforms.
|
||||
Key key;
|
||||
|
@ -300,11 +332,7 @@ struct KeyEvent {
|
|||
|
||||
dchar character;
|
||||
|
||||
// state:
|
||||
// 1 == shift
|
||||
// 8 == alt
|
||||
// 4 == ctrl
|
||||
uint modifierState;
|
||||
uint modifierState; /// see enum ModifierState
|
||||
|
||||
SimpleWindow window;
|
||||
}
|
||||
|
@ -698,7 +726,7 @@ version(X11) {
|
|||
// FIXME: mouse move should be distinct from presses+releases, so we can avoid subscribing to those events in X unnecessarily
|
||||
/// Listen for this on your event listeners if you are interested in mouse
|
||||
struct MouseEvent {
|
||||
int type; // movement, press, release, double click
|
||||
MouseEventType type; // movement, press, release, double click
|
||||
|
||||
int x;
|
||||
int y;
|
||||
|
@ -706,8 +734,8 @@ struct MouseEvent {
|
|||
int dx;
|
||||
int dy;
|
||||
|
||||
int button;
|
||||
int buttonFlags;
|
||||
MouseButton button;
|
||||
int modifierState;
|
||||
|
||||
SimpleWindow window;
|
||||
}
|
||||
|
@ -1153,7 +1181,7 @@ class Sprite {
|
|||
version(X11) {
|
||||
auto display = XDisplayConnection.get();
|
||||
handle = XCreatePixmap(display, cast(Drawable) win.window, width, height, 24);
|
||||
if(i.xshmAvailable)
|
||||
if(i.usingXshm)
|
||||
XShmPutImage(display, cast(Drawable) handle, DefaultGC(display, DefaultScreen(display)), i.handle, 0, 0, 0, 0, i.width, i.height, false);
|
||||
else
|
||||
XPutImage(display, cast(Drawable) handle, DefaultGC(display, DefaultScreen(display)), i.handle, 0, 0, 0, 0, i.width, i.height);
|
||||
|
@ -1391,7 +1419,7 @@ class SimpleWindow {
|
|||
} else
|
||||
version(X11) {
|
||||
if(!destroyed) {
|
||||
if(i.xshmAvailable)
|
||||
if(i.usingXshm)
|
||||
XShmPutImage(display, cast(Drawable) window, gc, i.handle, 0, 0, 0, 0, i.width, i.height, false);
|
||||
else
|
||||
XPutImage(display, cast(Drawable) window, gc, i.handle, 0, 0, 0, 0, i.width, i.height);
|
||||
|
@ -1845,7 +1873,7 @@ version(Windows) {
|
|||
mouse.x = LOWORD(lParam) + offsetX;
|
||||
mouse.y = HIWORD(lParam) + offsetY;
|
||||
wind.mdx(mouse);
|
||||
mouse.buttonFlags = wParam;
|
||||
mouse.modifierState = wParam;
|
||||
mouse.window = wind;
|
||||
|
||||
if(wind.handleMouseEvent)
|
||||
|
@ -1866,7 +1894,19 @@ version(Windows) {
|
|||
ev.pressed = msg == WM_KEYDOWN;
|
||||
// FIXME
|
||||
// ev.hardwareCode
|
||||
// ev.modifierState =
|
||||
|
||||
if(GetKeyState(Key.Shift)&0x8000 || GetKeyState(Key.Shift_r)&0x8000)
|
||||
ev.modifierState |= ModifierState.shift;
|
||||
if(GetKeyState(Key.Alt)&0x8000 || GetKeyState(Key.Alt_r)&0x8000)
|
||||
ev.modifierState |= ModifierState.alt;
|
||||
if(GetKeyState(Key.Ctrl)&0x8000 || GetKeyState(Key.Ctrl_r)&0x8000)
|
||||
ev.modifierState |= ModifierState.ctrl;
|
||||
if(GetKeyState(Key.Windows)&0x8000 || GetKeyState(Key.Windows_r)&0x8000)
|
||||
ev.modifierState |= ModifierState.windows;
|
||||
if(GetKeyState(Key.NumLock))
|
||||
ev.modifierState |= ModifierState.numLock;
|
||||
if(GetKeyState(Key.CapsLock))
|
||||
ev.modifierState |= ModifierState.capsLock;
|
||||
|
||||
/+
|
||||
// we always want to send the character too, so let's convert it
|
||||
|
@ -1886,45 +1926,45 @@ version(Windows) {
|
|||
wind.handleKeyEvent(ev);
|
||||
break;
|
||||
case 0x020a /*WM_MOUSEWHEEL*/:
|
||||
mouse.type = 1;
|
||||
mouse.button = (HIWORD(wParam) > 120) ? 16 : 8;
|
||||
mouse.type = cast(MouseEventType) 1;
|
||||
mouse.button = cast(MouseButton) ((HIWORD(wParam) > 120) ? 16 : 8);
|
||||
mouseEvent();
|
||||
break;
|
||||
case WM_MOUSEMOVE:
|
||||
mouse.type = 0;
|
||||
mouse.type = cast(MouseEventType) 0;
|
||||
mouseEvent();
|
||||
break;
|
||||
case WM_LBUTTONDOWN:
|
||||
case WM_LBUTTONDBLCLK:
|
||||
mouse.type = 1;
|
||||
mouse.button = 1;
|
||||
mouse.type = cast(MouseEventType) 1;
|
||||
mouse.button = cast(MouseButton) 1;
|
||||
mouseEvent();
|
||||
break;
|
||||
case WM_LBUTTONUP:
|
||||
mouse.type = 2;
|
||||
mouse.button = 1;
|
||||
mouse.type = cast(MouseEventType) 2;
|
||||
mouse.button =cast(MouseButton) 1;
|
||||
mouseEvent();
|
||||
break;
|
||||
case WM_RBUTTONDOWN:
|
||||
case WM_RBUTTONDBLCLK:
|
||||
mouse.type = 1;
|
||||
mouse.button = 2;
|
||||
mouse.type = cast(MouseEventType) 1;
|
||||
mouse.button =cast(MouseButton) 2;
|
||||
mouseEvent();
|
||||
break;
|
||||
case WM_RBUTTONUP:
|
||||
mouse.type = 2;
|
||||
mouse.button = 2;
|
||||
mouse.type = cast(MouseEventType) 2;
|
||||
mouse.button =cast(MouseButton) 2;
|
||||
mouseEvent();
|
||||
break;
|
||||
case WM_MBUTTONDOWN:
|
||||
case WM_MBUTTONDBLCLK:
|
||||
mouse.type = 1;
|
||||
mouse.button = 4;
|
||||
mouse.type = cast(MouseEventType) 1;
|
||||
mouse.button = cast(MouseButton) 4;
|
||||
mouseEvent();
|
||||
break;
|
||||
case WM_MBUTTONUP:
|
||||
mouse.type = 2;
|
||||
mouse.button = 4;
|
||||
mouse.type = cast(MouseEventType) 2;
|
||||
mouse.button = cast(MouseButton) 4;
|
||||
mouseEvent();
|
||||
break;
|
||||
default: return 1;
|
||||
|
@ -2310,7 +2350,7 @@ version(X11) {
|
|||
|
||||
void drawImage(int x, int y, Image i, int ix, int iy, int w, int h) {
|
||||
// source x, source y
|
||||
if(i.xshmAvailable)
|
||||
if(i.usingXshm)
|
||||
XShmPutImage(display, d, gc, i.handle, ix, iy, x, y, w, h, false);
|
||||
else
|
||||
XPutImage(display, d, gc, i.handle, ix, iy, x, y, w, h);
|
||||
|
@ -2346,10 +2386,12 @@ version(X11) {
|
|||
|
||||
void drawText(in int x, in int y, in int x2, in int y2, in string originalText, in uint alignment) {
|
||||
// FIXME: we should actually draw unicode.. but until then, I'm going to strip out multibyte chars
|
||||
string text;
|
||||
immutable(ubyte)[] text;
|
||||
// the first 256 unicode codepoints are the same as ascii and latin-1, which is what X expects, so we can keep all those
|
||||
// then strip the rest so there isn't garbage
|
||||
foreach(dchar ch; originalText)
|
||||
if(ch < 128)
|
||||
text ~= ch;
|
||||
if(ch < 256)
|
||||
text ~= cast(ubyte) ch;
|
||||
if(text.length == 0)
|
||||
return;
|
||||
|
||||
|
@ -2526,12 +2568,17 @@ version(X11) {
|
|||
return _xshmAvailable;
|
||||
}
|
||||
|
||||
bool usingXshm;
|
||||
|
||||
void createImage(int width, int height) {
|
||||
auto display = XDisplayConnection.get();
|
||||
assert(display !is null);
|
||||
auto screen = DefaultScreen(display);
|
||||
|
||||
if(xshmAvailable) {
|
||||
// it will only use shared memory for somewhat largish images,
|
||||
// since otherwise we risk wasting shared memory handles on a lot of little ones
|
||||
if(xshmAvailable && width > 100 && height > 100) {
|
||||
usingXshm = true;
|
||||
handle = XShmCreateImage(
|
||||
display,
|
||||
DefaultVisual(display, screen),
|
||||
|
@ -2544,7 +2591,8 @@ version(X11) {
|
|||
|
||||
assert(handle.bytes_per_line == 4 * width);
|
||||
shminfo.shmid = shmget(IPC_PRIVATE, handle.bytes_per_line * height, IPC_CREAT | 511 /* 0777 */);
|
||||
assert(shminfo.shmid >= 0);
|
||||
//import std.conv; import core.stdc.errno;
|
||||
assert(shminfo.shmid >= 0);//, to!string(errno));
|
||||
handle.data = shminfo.shmaddr = rawData = cast(ubyte*) shmat(shminfo.shmid, null, 0);
|
||||
assert(rawData != cast(ubyte*) -1);
|
||||
shminfo.readOnly = 0;
|
||||
|
@ -2570,11 +2618,12 @@ version(X11) {
|
|||
void dispose() {
|
||||
// note: this calls free(rawData) for us
|
||||
if(handle) {
|
||||
if(xshmAvailable)
|
||||
if(usingXshm)
|
||||
XShmDetach(XDisplayConnection.get(), &shminfo);
|
||||
XDestroyImage(handle);
|
||||
if(xshmAvailable)
|
||||
if(usingXshm) {
|
||||
shmdt(shminfo.shmaddr);
|
||||
}
|
||||
handle = null;
|
||||
}
|
||||
}
|
||||
|
@ -2900,10 +2949,10 @@ version(X11) {
|
|||
MouseEvent mouse;
|
||||
auto event = e.xmotion;
|
||||
|
||||
mouse.type = 0;
|
||||
mouse.type = MouseEventType.motion;
|
||||
mouse.x = event.x;
|
||||
mouse.y = event.y;
|
||||
mouse.buttonFlags = event.state;
|
||||
mouse.modifierState = event.state;
|
||||
|
||||
if(auto win = e.xmotion.window in SimpleWindow.nativeMapping) {
|
||||
(*win).mdx(mouse);
|
||||
|
@ -2920,23 +2969,23 @@ version(X11) {
|
|||
MouseEvent mouse;
|
||||
auto event = e.xbutton;
|
||||
|
||||
mouse.type = e.type == EventType.ButtonPress ? 1 : 2;
|
||||
mouse.type = cast(MouseEventType) (e.type == EventType.ButtonPress ? 1 : 2);
|
||||
mouse.x = event.x;
|
||||
mouse.y = event.y;
|
||||
|
||||
switch(event.button) {
|
||||
case 1: mouse.button = 1; break; // left
|
||||
case 2: mouse.button = 4; break; // middle
|
||||
case 3: mouse.button = 2; break; // right
|
||||
case 4: mouse.button = 8; break; // scroll up
|
||||
case 5: mouse.button = 16; break; // scroll down
|
||||
case 1: mouse.button = MouseButton.left; break; // left
|
||||
case 2: mouse.button = MouseButton.middle; break; // middle
|
||||
case 3: mouse.button = MouseButton.right; break; // right
|
||||
case 4: mouse.button = MouseButton.wheelUp; break; // scroll up
|
||||
case 5: mouse.button = MouseButton.wheelDown; break; // scroll down
|
||||
default:
|
||||
}
|
||||
|
||||
// FIXME: double check this
|
||||
mouse.buttonFlags = event.state;
|
||||
mouse.modifierState = event.state;
|
||||
|
||||
//mouse.buttonFlags = event.detail;
|
||||
//mouse.modifierState = event.detail;
|
||||
|
||||
if(auto win = e.xbutton.window in SimpleWindow.nativeMapping) {
|
||||
(*win).mdx(mouse);
|
||||
|
@ -3197,6 +3246,8 @@ nothrow:
|
|||
HBRUSH GetSysColorBrush(int nIndex);
|
||||
DWORD GetSysColor(int nIndex);
|
||||
|
||||
SHORT GetKeyState(int nVirtKey);
|
||||
|
||||
int SetROP2(HDC, int);
|
||||
enum R2_XORPEN = 7;
|
||||
enum R2_COPYPEN = 13;
|
||||
|
|
|
@ -593,6 +593,8 @@ http://msdn.microsoft.com/en-us/library/windows/desktop/ms683193%28v=vs.85%29.as
|
|||
size.X = cast(short) GetSystemMetrics(SM_CXMIN);
|
||||
size.Y = cast(short) GetSystemMetrics(SM_CYMIN);
|
||||
*/
|
||||
|
||||
// FIXME: this sucks, maybe i should just revert it. but there shouldn't be scrollbars in cellular mode
|
||||
size.X = 80;
|
||||
size.Y = 24;
|
||||
SetConsoleScreenBufferSize(hConsole, size);
|
||||
|
|
Loading…
Reference in New Issue