mirror of https://github.com/adamdruppe/arsd.git
more improvements to input handling and web
This commit is contained in:
parent
2501e6e575
commit
7d81f250fe
183
minigui.d
183
minigui.d
|
@ -1925,6 +1925,18 @@ class Widget : ReflectableProperties {
|
||||||
return StyleInformation(this);
|
return StyleInformation(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int focusableWidgets(scope int delegate(Widget) dg) {
|
||||||
|
foreach(widget; WidgetStream(this)) {
|
||||||
|
if(widget.tabStop && !widget.hidden) {
|
||||||
|
int result = dg(widget);
|
||||||
|
if (result)
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
// FIXME: I kinda want to hide events from implementation widgets
|
// FIXME: I kinda want to hide events from implementation widgets
|
||||||
// so it just catches them all and stops propagation...
|
// so it just catches them all and stops propagation...
|
||||||
// i guess i can do it with a event listener on star.
|
// i guess i can do it with a event listener on star.
|
||||||
|
@ -7084,7 +7096,7 @@ class HorizontalLayout : Layout {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
version(Windows)
|
version(win32_widgets)
|
||||||
private
|
private
|
||||||
extern(Windows)
|
extern(Windows)
|
||||||
LRESULT DoubleBufferWndProc(HWND hwnd, UINT message, WPARAM wparam, LPARAM lparam) nothrow {
|
LRESULT DoubleBufferWndProc(HWND hwnd, UINT message, WPARAM wparam, LPARAM lparam) nothrow {
|
||||||
|
@ -7944,6 +7956,12 @@ class Window : Widget {
|
||||||
Window.newWindowCreated(this);
|
Window.newWindowCreated(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
version(custom_widgets)
|
||||||
|
override void defaultEventHandler_click(ClickEvent event) {
|
||||||
|
if(event.target && event.target.tabStop)
|
||||||
|
event.target.focus();
|
||||||
|
}
|
||||||
|
|
||||||
private static void delegate(Window) newWindowCreated;
|
private static void delegate(Window) newWindowCreated;
|
||||||
|
|
||||||
version(win32_widgets)
|
version(win32_widgets)
|
||||||
|
@ -8082,13 +8100,13 @@ class Window : Widget {
|
||||||
}
|
}
|
||||||
win = new SimpleWindow(width, height, title, OpenGlOptions.no, Resizability.allowResizing, WindowTypes.normal, WindowFlags.dontAutoShow | WindowFlags.managesChildWindowFocus);
|
win = new SimpleWindow(width, height, title, OpenGlOptions.no, Resizability.allowResizing, WindowTypes.normal, WindowFlags.dontAutoShow | WindowFlags.managesChildWindowFocus);
|
||||||
|
|
||||||
/+
|
///+
|
||||||
// for input proxy
|
// for input proxy
|
||||||
auto display = XDisplayConnection.get;
|
auto display = XDisplayConnection.get;
|
||||||
auto inputProxy = XCreateSimpleWindow(display, win.window, -1, -1, 1, 1, 0, 0, 0);
|
auto inputProxy = XCreateSimpleWindow(display, win.window, -1, -1, 1, 1, 0, 0, 0);
|
||||||
XSelectInput(display, inputProxy, EventMask.KeyPressMask | EventMask.KeyReleaseMask);
|
XSelectInput(display, inputProxy, EventMask.KeyPressMask | EventMask.KeyReleaseMask);
|
||||||
XMapWindow(display, inputProxy);
|
XMapWindow(display, inputProxy);
|
||||||
import std.stdio; writefln("input proxy: 0x%0x", inputProxy);
|
//import std.stdio; writefln("input proxy: 0x%0x", inputProxy);
|
||||||
this.inputProxy = new SimpleWindow(inputProxy);
|
this.inputProxy = new SimpleWindow(inputProxy);
|
||||||
|
|
||||||
XEvent lastEvent;
|
XEvent lastEvent;
|
||||||
|
@ -8108,8 +8126,11 @@ class Window : Widget {
|
||||||
if(auto nw = cast(NestedChildWindowWidget) focusedWidget) {
|
if(auto nw = cast(NestedChildWindowWidget) focusedWidget) {
|
||||||
auto thing = nw.focusableWindow();
|
auto thing = nw.focusableWindow();
|
||||||
if(thing && thing.window) {
|
if(thing && thing.window) {
|
||||||
import std.stdio; writeln("sending event ", lastEvent.xkey);
|
lastEvent.xkey.window = thing.window;
|
||||||
|
// import std.stdio; writeln("sending event ", lastEvent.xkey);
|
||||||
|
trapXErrors( {
|
||||||
XSendEvent(XDisplayConnection.get, thing.window, false, 0, &lastEvent);
|
XSendEvent(XDisplayConnection.get, thing.window, false, 0, &lastEvent);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -8121,7 +8142,7 @@ class Window : Widget {
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
// done
|
// done
|
||||||
+/
|
//+/
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@ -8133,13 +8154,7 @@ class Window : Widget {
|
||||||
SimpleWindow inputProxy;
|
SimpleWindow inputProxy;
|
||||||
|
|
||||||
private SimpleWindow setRequestedInputFocus() {
|
private SimpleWindow setRequestedInputFocus() {
|
||||||
// return inputProxy;
|
return inputProxy;
|
||||||
|
|
||||||
if(auto fw = cast(NestedChildWindowWidget) focusedWidget) {
|
|
||||||
// sdpyPrintDebugString("heaven");
|
|
||||||
return fw.focusableWindow;
|
|
||||||
}
|
|
||||||
return win;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// ditto
|
/// ditto
|
||||||
|
@ -8173,14 +8188,15 @@ class Window : Widget {
|
||||||
event.ctrlKey = (ev.modifierState & ModifierState.ctrl) ? true : false;
|
event.ctrlKey = (ev.modifierState & ModifierState.ctrl) ? true : false;
|
||||||
event.dispatch();
|
event.dispatch();
|
||||||
|
|
||||||
return !event.defaultPrevented;
|
return !event.propagationStopped;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// returns true if propagation should continue into nested things.... prolly not a great thing to do.
|
||||||
bool dispatchCharEvent(dchar ch) {
|
bool dispatchCharEvent(dchar ch) {
|
||||||
if(focusedWidget) {
|
if(focusedWidget) {
|
||||||
auto event = new CharEvent(focusedWidget, ch);
|
auto event = new CharEvent(focusedWidget, ch);
|
||||||
event.dispatch();
|
event.dispatch();
|
||||||
return !event.defaultPrevented;
|
return !event.propagationStopped;
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -8343,18 +8359,29 @@ class Window : Widget {
|
||||||
}
|
}
|
||||||
|
|
||||||
static Widget getFirstFocusable(Widget start) {
|
static Widget getFirstFocusable(Widget start) {
|
||||||
if(start.tabStop && !start.hidden)
|
if(start is null)
|
||||||
return start;
|
return null;
|
||||||
|
|
||||||
if(!start.hidden)
|
foreach(widget; &start.focusableWidgets) {
|
||||||
foreach(child; start.children) {
|
return widget;
|
||||||
auto f = getFirstFocusable(child);
|
|
||||||
if(f !is null)
|
|
||||||
return f;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static Widget getLastFocusable(Widget start) {
|
||||||
|
if(start is null)
|
||||||
|
return null;
|
||||||
|
|
||||||
|
Widget last;
|
||||||
|
foreach(widget; &start.focusableWidgets) {
|
||||||
|
last = widget;
|
||||||
|
}
|
||||||
|
|
||||||
|
return last;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
mixin Emits!ClosingEvent;
|
mixin Emits!ClosingEvent;
|
||||||
mixin Emits!ClosedEvent;
|
mixin Emits!ClosedEvent;
|
||||||
}
|
}
|
||||||
|
@ -10457,9 +10484,10 @@ class Menu : Window {
|
||||||
if(!menuParent.parentWindow.win.closed) {
|
if(!menuParent.parentWindow.win.closed) {
|
||||||
if(auto maw = cast(MouseActivatedWidget) menuParent) {
|
if(auto maw = cast(MouseActivatedWidget) menuParent) {
|
||||||
maw.setDynamicState(DynamicState.depressed, false);
|
maw.setDynamicState(DynamicState.depressed, false);
|
||||||
|
maw.setDynamicState(DynamicState.hover, false);
|
||||||
maw.redraw();
|
maw.redraw();
|
||||||
}
|
}
|
||||||
menuParent.parentWindow.win.focus();
|
// menuParent.parentWindow.win.focus();
|
||||||
}
|
}
|
||||||
clickListener.disconnect();
|
clickListener.disconnect();
|
||||||
}
|
}
|
||||||
|
@ -14225,6 +14253,117 @@ interface ReflectableProperties {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private struct Stack(T) {
|
||||||
|
this(int maxSize) {
|
||||||
|
internalLength = 0;
|
||||||
|
arr = initialBuffer[];
|
||||||
|
}
|
||||||
|
|
||||||
|
///.
|
||||||
|
void push(T t) {
|
||||||
|
if(internalLength >= arr.length) {
|
||||||
|
auto oldarr = arr;
|
||||||
|
if(arr.length < 4096)
|
||||||
|
arr = new T[arr.length * 2];
|
||||||
|
else
|
||||||
|
arr = new T[arr.length + 4096];
|
||||||
|
arr[0 .. oldarr.length] = oldarr[];
|
||||||
|
}
|
||||||
|
|
||||||
|
arr[internalLength] = t;
|
||||||
|
internalLength++;
|
||||||
|
}
|
||||||
|
|
||||||
|
///.
|
||||||
|
T pop() {
|
||||||
|
assert(internalLength);
|
||||||
|
internalLength--;
|
||||||
|
return arr[internalLength];
|
||||||
|
}
|
||||||
|
|
||||||
|
///.
|
||||||
|
T peek() {
|
||||||
|
assert(internalLength);
|
||||||
|
return arr[internalLength - 1];
|
||||||
|
}
|
||||||
|
|
||||||
|
///.
|
||||||
|
@property bool empty() {
|
||||||
|
return internalLength ? false : true;
|
||||||
|
}
|
||||||
|
|
||||||
|
///.
|
||||||
|
private T[] arr;
|
||||||
|
private size_t internalLength;
|
||||||
|
private T[64] initialBuffer;
|
||||||
|
// the static array is allocated with this object, so if we have a small stack (which we prolly do; dom trees usually aren't insanely deep),
|
||||||
|
// using this saves us a bunch of trips to the GC. In my last profiling, I got about a 50x improvement in the push()
|
||||||
|
// function thanks to this, and push() was actually one of the slowest individual functions in the code!
|
||||||
|
}
|
||||||
|
|
||||||
|
/// This is the lazy range that walks the tree for you. It tries to go in the lexical order of the source: node, then children from first to last, each recursively.
|
||||||
|
private struct WidgetStream {
|
||||||
|
|
||||||
|
///.
|
||||||
|
@property Widget front() {
|
||||||
|
return current.widget;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Use Widget.tree instead.
|
||||||
|
this(Widget start) {
|
||||||
|
current.widget = start;
|
||||||
|
current.childPosition = -1;
|
||||||
|
isEmpty = false;
|
||||||
|
stack = typeof(stack)(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
Handle it
|
||||||
|
handle its children
|
||||||
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
///.
|
||||||
|
void popFront() {
|
||||||
|
more:
|
||||||
|
if(isEmpty) return;
|
||||||
|
|
||||||
|
// FIXME: the profiler says this function is somewhat slow (noticeable because it can be called a lot of times)
|
||||||
|
|
||||||
|
current.childPosition++;
|
||||||
|
if(current.childPosition >= current.widget.children.length) {
|
||||||
|
if(stack.empty())
|
||||||
|
isEmpty = true;
|
||||||
|
else {
|
||||||
|
current = stack.pop();
|
||||||
|
goto more;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
stack.push(current);
|
||||||
|
current.widget = current.widget.children[current.childPosition];
|
||||||
|
current.childPosition = -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
///.
|
||||||
|
@property bool empty() {
|
||||||
|
return isEmpty;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
|
||||||
|
struct Current {
|
||||||
|
Widget widget;
|
||||||
|
int childPosition;
|
||||||
|
}
|
||||||
|
|
||||||
|
Current current;
|
||||||
|
|
||||||
|
Stack!(Current) stack;
|
||||||
|
|
||||||
|
bool isEmpty;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/+
|
/+
|
||||||
|
|
||||||
|
|
|
@ -360,7 +360,7 @@ class WebViewWidget_CEF : WebViewWidgetBase {
|
||||||
//semaphore = new Semaphore;
|
//semaphore = new Semaphore;
|
||||||
assert(CefApp.active);
|
assert(CefApp.active);
|
||||||
|
|
||||||
this(new MiniguiCefClient(openNewWindow), parent);
|
this(new MiniguiCefClient(openNewWindow), parent, false);
|
||||||
|
|
||||||
cef_window_info_t window_info;
|
cef_window_info_t window_info;
|
||||||
window_info.parent_window = containerWindow.nativeWindowHandle;
|
window_info.parent_window = containerWindow.nativeWindowHandle;
|
||||||
|
@ -398,7 +398,7 @@ class WebViewWidget_CEF : WebViewWidgetBase {
|
||||||
.destroy(this); // but this is ok to do some memory management cleanup
|
.destroy(this); // but this is ok to do some memory management cleanup
|
||||||
}
|
}
|
||||||
|
|
||||||
private this(MiniguiCefClient client, Widget parent) {
|
private this(MiniguiCefClient client, Widget parent, bool isDevTools) {
|
||||||
super(parent);
|
super(parent);
|
||||||
|
|
||||||
this.client = client;
|
this.client = client;
|
||||||
|
@ -407,22 +407,56 @@ class WebViewWidget_CEF : WebViewWidgetBase {
|
||||||
|
|
||||||
mapping[containerWindow.nativeWindowHandle()] = this;
|
mapping[containerWindow.nativeWindowHandle()] = this;
|
||||||
|
|
||||||
|
this.addEventListener(delegate(KeyDownEvent ke) {
|
||||||
this.parentWindow.addEventListener((FocusEvent fe) {
|
if(ke.key == Key.Tab)
|
||||||
if(!browserHandle) return;
|
ke.preventDefault();
|
||||||
//browserHandle.get_host.set_focus(true);
|
|
||||||
|
|
||||||
executeJavascript("if(window.__arsdPreviouslyFocusedNode) window.__arsdPreviouslyFocusedNode.focus(); window.dispatchEvent(new FocusEvent(\"focus\"));");
|
|
||||||
});
|
});
|
||||||
this.parentWindow.addEventListener((BlurEvent be) {
|
|
||||||
|
this.addEventListener((FocusEvent fe) {
|
||||||
if(!browserHandle) return;
|
if(!browserHandle) return;
|
||||||
|
|
||||||
executeJavascript("if(document.activeElement) { window.__arsdPreviouslyFocusedNode = document.activeElement; document.activeElement.blur(); } window.dispatchEvent(new FocusEvent(\"blur\"));");
|
XFocusChangeEvent ev;
|
||||||
|
ev.type = arsd.simpledisplay.EventType.FocusIn;
|
||||||
|
ev.display = XDisplayConnection.get;
|
||||||
|
ev.window = ozone;
|
||||||
|
ev.mode = NotifyModes.NotifyNormal;
|
||||||
|
ev.detail = NotifyDetail.NotifyVirtual;
|
||||||
|
|
||||||
|
trapXErrors( {
|
||||||
|
XSendEvent(XDisplayConnection.get, ozone, false, 0, cast(XEvent*) &ev);
|
||||||
|
});
|
||||||
|
|
||||||
|
// this also works if the message is buggy and it avoids weirdness from raising window etc
|
||||||
|
//executeJavascript("if(window.__arsdPreviouslyFocusedNode) window.__arsdPreviouslyFocusedNode.focus(); window.dispatchEvent(new FocusEvent(\"focus\"));");
|
||||||
|
});
|
||||||
|
this.addEventListener((BlurEvent be) {
|
||||||
|
if(!browserHandle) return;
|
||||||
|
|
||||||
|
XFocusChangeEvent ev;
|
||||||
|
ev.type = arsd.simpledisplay.EventType.FocusOut;
|
||||||
|
ev.display = XDisplayConnection.get;
|
||||||
|
ev.window = ozone;
|
||||||
|
ev.mode = NotifyModes.NotifyNormal;
|
||||||
|
ev.detail = NotifyDetail.NotifyNonlinearVirtual;
|
||||||
|
|
||||||
|
trapXErrors( {
|
||||||
|
XSendEvent(XDisplayConnection.get, ozone, false, 0, cast(XEvent*) &ev);
|
||||||
|
});
|
||||||
|
|
||||||
|
//executeJavascript("if(document.activeElement) { window.__arsdPreviouslyFocusedNode = document.activeElement; document.activeElement.blur(); } window.dispatchEvent(new FocusEvent(\"blur\"));");
|
||||||
});
|
});
|
||||||
|
|
||||||
bool closeAttempted = false;
|
bool closeAttempted = false;
|
||||||
|
|
||||||
|
if(isDevTools)
|
||||||
this.parentWindow.addEventListener((scope ClosingEvent ce) {
|
this.parentWindow.addEventListener((scope ClosingEvent ce) {
|
||||||
|
this.parentWindow.hide();
|
||||||
|
ce.preventDefault();
|
||||||
|
});
|
||||||
|
else
|
||||||
|
this.parentWindow.addEventListener((scope ClosingEvent ce) {
|
||||||
|
if(devTools)
|
||||||
|
devTools.close();
|
||||||
if(!closeAttempted && browserHandle) {
|
if(!closeAttempted && browserHandle) {
|
||||||
browserHandle.get_host.close_browser(true);
|
browserHandle.get_host.close_browser(true);
|
||||||
ce.preventDefault();
|
ce.preventDefault();
|
||||||
|
@ -450,6 +484,7 @@ class WebViewWidget_CEF : WebViewWidgetBase {
|
||||||
}
|
}
|
||||||
|
|
||||||
private NativeWindowHandle browserWindow;
|
private NativeWindowHandle browserWindow;
|
||||||
|
private NativeWindowHandle ozone;
|
||||||
private RC!cef_browser_t browserHandle;
|
private RC!cef_browser_t browserHandle;
|
||||||
|
|
||||||
private static WebViewWidget[NativeWindowHandle] mapping;
|
private static WebViewWidget[NativeWindowHandle] mapping;
|
||||||
|
@ -475,9 +510,37 @@ class WebViewWidget_CEF : WebViewWidgetBase {
|
||||||
browserHandle.get_main_frame.execute_java_script(&c, &u, line);
|
browserHandle.get_main_frame.execute_java_script(&c, &u, line);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private Window devTools;
|
||||||
override void showDevTools() {
|
override void showDevTools() {
|
||||||
if(!browserHandle) return;
|
if(!browserHandle) return;
|
||||||
browserHandle.get_host.show_dev_tools(null /* window info */, client.passable, null /* settings */, null /* inspect element at coordinates */);
|
|
||||||
|
if(devTools is null) {
|
||||||
|
auto host = browserHandle.get_host;
|
||||||
|
|
||||||
|
if(host.has_dev_tools()) {
|
||||||
|
host.close_dev_tools();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
cef_window_info_t windowinfo;
|
||||||
|
version(linux) {
|
||||||
|
auto sw = new Window("DevTools");
|
||||||
|
//sw.win.beingOpenKeepsAppOpen = false;
|
||||||
|
devTools = sw;
|
||||||
|
|
||||||
|
auto wv = new WebViewWidget_CEF(client, sw, true);
|
||||||
|
|
||||||
|
sw.show();
|
||||||
|
|
||||||
|
windowinfo.parent_window = wv.containerWindow.nativeWindowHandle;
|
||||||
|
}
|
||||||
|
host.show_dev_tools(&windowinfo, client.passable, null /* settings */, null /* inspect element at coordinates */);
|
||||||
|
} else {
|
||||||
|
if(devTools.hidden)
|
||||||
|
devTools.show();
|
||||||
|
else
|
||||||
|
devTools.hide();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// FYI the cef browser host also allows things like custom spelling dictionaries and getting navigation entries.
|
// FYI the cef browser host also allows things like custom spelling dictionaries and getting navigation entries.
|
||||||
|
@ -534,7 +597,7 @@ version(cef) {
|
||||||
cef_dictionary_value_t** extra_info,
|
cef_dictionary_value_t** extra_info,
|
||||||
int* no_javascript_access
|
int* no_javascript_access
|
||||||
) {
|
) {
|
||||||
|
sdpyPrintDebugString("on_before_popup");
|
||||||
if(this.client.openNewWindow is null)
|
if(this.client.openNewWindow is null)
|
||||||
return 1; // new windows disabled
|
return 1; // new windows disabled
|
||||||
|
|
||||||
|
@ -551,7 +614,7 @@ version(cef) {
|
||||||
scope WebViewWidget delegate(Widget, BrowserSettings) accept = (parent, passed_settings) {
|
scope WebViewWidget delegate(Widget, BrowserSettings) accept = (parent, passed_settings) {
|
||||||
ret = 0;
|
ret = 0;
|
||||||
if(parent !is null) {
|
if(parent !is null) {
|
||||||
auto widget = new WebViewWidget_CEF(this.client, parent);
|
auto widget = new WebViewWidget_CEF(this.client, parent, false);
|
||||||
(*windowInfo).parent_window = widget.containerWindow.nativeWindowHandle;
|
(*windowInfo).parent_window = widget.containerWindow.nativeWindowHandle;
|
||||||
|
|
||||||
passed_settings.set(browser_settings);
|
passed_settings.set(browser_settings);
|
||||||
|
@ -589,10 +652,13 @@ version(cef) {
|
||||||
import arsd.simpledisplay : Window;
|
import arsd.simpledisplay : Window;
|
||||||
Window root;
|
Window root;
|
||||||
Window parent;
|
Window parent;
|
||||||
|
Window ozone;
|
||||||
uint c = 0;
|
uint c = 0;
|
||||||
auto display = XDisplayConnection.get;
|
auto display = XDisplayConnection.get;
|
||||||
Window* children;
|
Window* children;
|
||||||
XQueryTree(display, handle, &root, &parent, &children, &c);
|
XQueryTree(display, handle, &root, &parent, &children, &c);
|
||||||
|
if(c == 1)
|
||||||
|
ozone = children[0];
|
||||||
XFree(children);
|
XFree(children);
|
||||||
} else static assert(0);
|
} else static assert(0);
|
||||||
|
|
||||||
|
@ -600,8 +666,9 @@ version(cef) {
|
||||||
auto wv = *wvp;
|
auto wv = *wvp;
|
||||||
wv.browserWindow = handle;
|
wv.browserWindow = handle;
|
||||||
wv.browserHandle = RC!cef_browser_t(ptr);
|
wv.browserHandle = RC!cef_browser_t(ptr);
|
||||||
|
wv.ozone = ozone ? ozone : handle;
|
||||||
|
|
||||||
wv.browserWindowWrapped = new SimpleWindow(wv.browserWindow);
|
wv.browserWindowWrapped = new SimpleWindow(wv.ozone);
|
||||||
/+
|
/+
|
||||||
XSelectInput(XDisplayConnection.get, handle, EventMask.FocusChangeMask);
|
XSelectInput(XDisplayConnection.get, handle, EventMask.FocusChangeMask);
|
||||||
|
|
||||||
|
@ -831,17 +898,70 @@ version(cef) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class MiniguiRequestHandler : CEF!cef_request_handler_t {
|
||||||
|
override int on_before_browse(RC!(cef_browser_t), RC!(cef_frame_t), RC!(cef_request_t), int, int) nothrow {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
override int on_open_urlfrom_tab(RC!(cef_browser_t), RC!(cef_frame_t), const(cef_string_utf16_t)*, cef_window_open_disposition_t, int) nothrow {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
override cef_resource_request_handler_t* get_resource_request_handler(RC!(cef_browser_t), RC!(cef_frame_t), RC!(cef_request_t), int, int, const(cef_string_utf16_t)*, int*) nothrow {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
override int get_auth_credentials(RC!(cef_browser_t), const(cef_string_utf16_t)*, int, const(cef_string_utf16_t)*, int, const(cef_string_utf16_t)*, const(cef_string_utf16_t)*, RC!(cef_auth_callback_t)) nothrow {
|
||||||
|
// this is for http basic auth popup.....
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
override int on_quota_request(RC!(cef_browser_t), const(cef_string_utf16_t)*, long, RC!(cef_callback_t)) nothrow {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
override int on_certificate_error(RC!(cef_browser_t), cef_errorcode_t, const(cef_string_utf16_t)*, RC!(cef_sslinfo_t), RC!(cef_callback_t)) nothrow {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
override int on_select_client_certificate(RC!(cef_browser_t), int, const(cef_string_utf16_t)*, int, ulong, cef_x509certificate_t**, RC!(cef_select_client_certificate_callback_t)) nothrow {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
override void on_plugin_crashed(RC!(cef_browser_t), const(cef_string_utf16_t)*) nothrow {
|
||||||
|
|
||||||
|
}
|
||||||
|
override void on_render_view_ready(RC!(cef_browser_t) p) nothrow {
|
||||||
|
|
||||||
|
}
|
||||||
|
override void on_render_process_terminated(RC!(cef_browser_t), cef_termination_status_t) nothrow {
|
||||||
|
|
||||||
|
}
|
||||||
|
override void on_document_available_in_main_frame(RC!(cef_browser_t) browser) nothrow {
|
||||||
|
browser.runOnWebView(delegate(wv) {
|
||||||
|
wv.executeJavascript("console.log('here');");
|
||||||
|
});
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
class MiniguiFocusHandler : CEF!cef_focus_handler_t {
|
class MiniguiFocusHandler : CEF!cef_focus_handler_t {
|
||||||
override void on_take_focus(RC!(cef_browser_t) browser, int next) nothrow {
|
override void on_take_focus(RC!(cef_browser_t) browser, int next) nothrow {
|
||||||
// sdpyPrintDebugString("take");
|
browser.runOnWebView(delegate(wv) {
|
||||||
|
Widget f;
|
||||||
|
if(next) {
|
||||||
|
f = Window.getFirstFocusable(wv.parentWindow);
|
||||||
|
} else {
|
||||||
|
foreach(w; &wv.parentWindow.focusableWidgets) {
|
||||||
|
if(w is wv)
|
||||||
|
break;
|
||||||
|
f = w;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if(f)
|
||||||
|
f.focus();
|
||||||
|
});
|
||||||
}
|
}
|
||||||
override int on_set_focus(RC!(cef_browser_t) browser, cef_focus_source_t source) nothrow {
|
override int on_set_focus(RC!(cef_browser_t) browser, cef_focus_source_t source) nothrow {
|
||||||
/+
|
/+
|
||||||
browser.runOnWebView((ev) {
|
browser.runOnWebView((ev) {
|
||||||
sdpyPrintDebugString("setting");
|
ev.focus(); // even this can steal focus from other parts of my application!
|
||||||
ev.parentWindow.focusedWidget = ev;
|
|
||||||
});
|
});
|
||||||
+/
|
+/
|
||||||
|
//sdpyPrintDebugString("setting");
|
||||||
|
|
||||||
return 1; // otherwise, cancel because this bullshit tends to steal focus from other applications and i never, ever, ever want that to happen.
|
return 1; // otherwise, cancel because this bullshit tends to steal focus from other applications and i never, ever, ever want that to happen.
|
||||||
// seems to happen because of race condition in it getting a focus event and then stealing the focus from the parent
|
// seems to happen because of race condition in it getting a focus event and then stealing the focus from the parent
|
||||||
|
@ -850,7 +970,12 @@ version(cef) {
|
||||||
// it also breaks its own pop up menus and drop down boxes to allow this! wtf
|
// it also breaks its own pop up menus and drop down boxes to allow this! wtf
|
||||||
}
|
}
|
||||||
override void on_got_focus(RC!(cef_browser_t) browser) nothrow {
|
override void on_got_focus(RC!(cef_browser_t) browser) nothrow {
|
||||||
// sdpyPrintDebugString("got");
|
browser.runOnWebView((ev) {
|
||||||
|
// this sometimes steals from the app too but it is relatively acceptable
|
||||||
|
// steals when i mouse in from the side of the window quickly, but still
|
||||||
|
// i want the minigui state to match so i'll allow it
|
||||||
|
ev.focus();
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -865,6 +990,7 @@ version(cef) {
|
||||||
MiniguiDownloadHandler downloadHandler;
|
MiniguiDownloadHandler downloadHandler;
|
||||||
MiniguiKeyboardHandler keyboardHandler;
|
MiniguiKeyboardHandler keyboardHandler;
|
||||||
MiniguiFocusHandler focusHandler;
|
MiniguiFocusHandler focusHandler;
|
||||||
|
MiniguiRequestHandler requestHandler;
|
||||||
this(void delegate(scope OpenNewWindowParams) openNewWindow) {
|
this(void delegate(scope OpenNewWindowParams) openNewWindow) {
|
||||||
this.openNewWindow = openNewWindow;
|
this.openNewWindow = openNewWindow;
|
||||||
lsh = new MiniguiCefLifeSpanHandler(this);
|
lsh = new MiniguiCefLifeSpanHandler(this);
|
||||||
|
@ -874,6 +1000,7 @@ version(cef) {
|
||||||
downloadHandler = new MiniguiDownloadHandler();
|
downloadHandler = new MiniguiDownloadHandler();
|
||||||
keyboardHandler = new MiniguiKeyboardHandler();
|
keyboardHandler = new MiniguiKeyboardHandler();
|
||||||
focusHandler = new MiniguiFocusHandler();
|
focusHandler = new MiniguiFocusHandler();
|
||||||
|
requestHandler = new MiniguiRequestHandler();
|
||||||
}
|
}
|
||||||
|
|
||||||
override cef_audio_handler_t* get_audio_handler() {
|
override cef_audio_handler_t* get_audio_handler() {
|
||||||
|
@ -917,10 +1044,12 @@ version(cef) {
|
||||||
override cef_render_handler_t* get_render_handler() {
|
override cef_render_handler_t* get_render_handler() {
|
||||||
// this thing might work for an off-screen thing
|
// this thing might work for an off-screen thing
|
||||||
// like to an image or to a video stream maybe
|
// like to an image or to a video stream maybe
|
||||||
|
//
|
||||||
|
// might be useful to have it render here then send it over too for remote X sharing a process
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
override cef_request_handler_t* get_request_handler() {
|
override cef_request_handler_t* get_request_handler() {
|
||||||
return null;
|
return requestHandler.returnable;
|
||||||
}
|
}
|
||||||
override int on_process_message_received(RC!cef_browser_t, RC!cef_frame_t, cef_process_id_t, RC!cef_process_message_t) {
|
override int on_process_message_received(RC!cef_browser_t, RC!cef_frame_t, cef_process_id_t, RC!cef_process_message_t) {
|
||||||
return 0; // return 1 if you can actually handle the message
|
return 0; // return 1 if you can actually handle the message
|
||||||
|
|
|
@ -12296,6 +12296,13 @@ version(X11) {
|
||||||
if(width == 0 || height == 0) {
|
if(width == 0 || height == 0) {
|
||||||
XSetClipMask(display, gc, None);
|
XSetClipMask(display, gc, None);
|
||||||
|
|
||||||
|
if(xrenderPicturePainter) {
|
||||||
|
|
||||||
|
XRectangle[1] rects;
|
||||||
|
rects[0] = XRectangle(short.min, short.min, short.max, short.max);
|
||||||
|
XRenderSetPictureClipRectangles(display, xrenderPicturePainter, 0, 0, rects.ptr, cast(int) rects.length);
|
||||||
|
}
|
||||||
|
|
||||||
version(with_xft) {
|
version(with_xft) {
|
||||||
if(xftFont is null || xftDraw is null)
|
if(xftFont is null || xftDraw is null)
|
||||||
return;
|
return;
|
||||||
|
@ -12306,6 +12313,9 @@ version(X11) {
|
||||||
rects[0] = XRectangle(cast(short)(x), cast(short)(y), cast(short) width, cast(short) height);
|
rects[0] = XRectangle(cast(short)(x), cast(short)(y), cast(short) width, cast(short) height);
|
||||||
XSetClipRectangles(XDisplayConnection.get, gc, 0, 0, rects.ptr, 1, 0);
|
XSetClipRectangles(XDisplayConnection.get, gc, 0, 0, rects.ptr, 1, 0);
|
||||||
|
|
||||||
|
if(xrenderPicturePainter)
|
||||||
|
XRenderSetPictureClipRectangles(display, xrenderPicturePainter, 0, 0, rects.ptr, cast(int) rects.length);
|
||||||
|
|
||||||
version(with_xft) {
|
version(with_xft) {
|
||||||
if(xftFont is null || xftDraw is null)
|
if(xftFont is null || xftDraw is null)
|
||||||
return;
|
return;
|
||||||
|
@ -12572,6 +12582,12 @@ version(X11) {
|
||||||
XRenderPictureAttributes attrs;
|
XRenderPictureAttributes attrs;
|
||||||
// FIXME: I can prolly reuse this as long as the pixmap itself is valid.
|
// FIXME: I can prolly reuse this as long as the pixmap itself is valid.
|
||||||
xrenderPicturePainter = XRenderCreatePicture(display, d, Sprite.RGB24, 0, &attrs);
|
xrenderPicturePainter = XRenderCreatePicture(display, d, Sprite.RGB24, 0, &attrs);
|
||||||
|
|
||||||
|
// need to initialize the clip
|
||||||
|
XRectangle[1] rects;
|
||||||
|
rects[0] = XRectangle(cast(short)(_clipRectangle.left), cast(short)(_clipRectangle.top), cast(short) _clipRectangle.width, cast(short) _clipRectangle.height);
|
||||||
|
|
||||||
|
XRenderSetPictureClipRectangles(display, xrenderPicturePainter, 0, 0, rects.ptr, cast(int) rects.length);
|
||||||
}
|
}
|
||||||
|
|
||||||
XRenderComposite(
|
XRenderComposite(
|
||||||
|
@ -13613,6 +13629,35 @@ mixin DynamicLoad!(XRandr, "Xrandr", 2, XRandrLibrarySuccessfullyLoaded) XRandrL
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/++
|
||||||
|
Platform-specific for X11. Traps errors for the duration of `dg`. Avoid calling this from inside a call to this.
|
||||||
|
|
||||||
|
Please note that it returns
|
||||||
|
+/
|
||||||
|
XErrorEvent[] trapXErrors(scope void delegate() dg) {
|
||||||
|
|
||||||
|
static XErrorEvent[] errorBuffer;
|
||||||
|
|
||||||
|
static extern(C) int handler (Display* dpy, XErrorEvent* evt) nothrow {
|
||||||
|
errorBuffer ~= *evt;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto savedErrorHandler = XSetErrorHandler(&handler);
|
||||||
|
|
||||||
|
try {
|
||||||
|
dg();
|
||||||
|
} finally {
|
||||||
|
XSync(XDisplayConnection.get, 0/*False*/);
|
||||||
|
XSetErrorHandler(savedErrorHandler);
|
||||||
|
}
|
||||||
|
|
||||||
|
auto bfr = errorBuffer;
|
||||||
|
errorBuffer = null;
|
||||||
|
|
||||||
|
return bfr;
|
||||||
|
}
|
||||||
|
|
||||||
/// Platform-specific for X11. A singleton class (well, all its methods are actually static... so more like a namespace) wrapping a `Display*`.
|
/// Platform-specific for X11. A singleton class (well, all its methods are actually static... so more like a namespace) wrapping a `Display*`.
|
||||||
class XDisplayConnection {
|
class XDisplayConnection {
|
||||||
private __gshared Display* display;
|
private __gshared Display* display;
|
||||||
|
@ -16986,6 +17031,9 @@ extern(C) {
|
||||||
extern(C) alias XIOErrorHandler = int function (Display* display);
|
extern(C) alias XIOErrorHandler = int function (Display* display);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
extern(C) nothrow
|
||||||
|
alias XErrorHandler = int function(Display*, XErrorEvent*);
|
||||||
|
|
||||||
extern(C) nothrow @nogc {
|
extern(C) nothrow @nogc {
|
||||||
struct Screen{
|
struct Screen{
|
||||||
XExtData *ext_data; /* hook for extension to hang data */
|
XExtData *ext_data; /* hook for extension to hang data */
|
||||||
|
@ -17190,8 +17238,6 @@ struct Visual
|
||||||
byte pad;
|
byte pad;
|
||||||
}
|
}
|
||||||
|
|
||||||
alias XErrorHandler = int function(Display*, XErrorEvent*);
|
|
||||||
|
|
||||||
struct XRectangle {
|
struct XRectangle {
|
||||||
short x;
|
short x;
|
||||||
short y;
|
short y;
|
||||||
|
|
Loading…
Reference in New Issue