Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
81 changes: 81 additions & 0 deletions app/GioView.java
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Matrix;
import android.graphics.PixelFormat;
import android.graphics.Rect;
import android.os.Build;
import android.os.Bundle;
Expand Down Expand Up @@ -55,6 +56,8 @@
import android.view.accessibility.AccessibilityNodeInfo;
import android.view.accessibility.AccessibilityEvent;
import android.view.accessibility.AccessibilityManager;
import android.view.ViewGroup;
import android.widget.FrameLayout;

import java.io.UnsupportedEncodingException;

Expand Down Expand Up @@ -84,9 +87,12 @@ public GioView(Context context, AttributeSet attrs) {
// Late initialization of the Go runtime to wait for a valid context.
Gio.init(context.getApplicationContext());

setZOrderOnTop(true);

// Set background color to transparent to avoid a flickering
// issue on ChromeOS.
setBackgroundColor(Color.argb(0, 0, 0, 0));
getHolder().setFormat(PixelFormat.TRANSPARENT);

ViewConfiguration conf = ViewConfiguration.get(context);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
Expand Down Expand Up @@ -153,6 +159,22 @@ public GioView(Context context, AttributeSet attrs) {
requestUnbufferedDispatch(event);
}

// Check if touch event should be handled by Gio or passed through
// to external views. Only check at the beginning of a trace so drags
// crossing over external regions are not truncated.
if (nhandle != 0) {
int action = event.getActionMasked();
if (action == MotionEvent.ACTION_DOWN || action == MotionEvent.ACTION_POINTER_DOWN) {
int idx = event.getActionIndex();
float x = event.getX(idx);
float y = event.getY(idx);
if (!hitTest(nhandle, x, y)) {
// Event is on an external view, don't consume it.
return false;
}
}
}

dispatchMotionEvent(event);
return true;
}
Expand Down Expand Up @@ -549,6 +571,62 @@ void updateCaret(float m00, float m01, float m02, float m10, float m11, float m1
imm.updateCursorAnchorInfo(this, inf);
}

boolean setEmbedViewPosition(View view, int x, int y, int w, int h, int zOrder) {
ViewGroup parent = (ViewGroup) this.getParent();
boolean isLayoutUpdated = false;

if (zOrder == 0) {
view.setVisibility(View.GONE);
view.requestLayout();
return true;
}

if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
// On API 21+: use Z for draw ordering without mutating the view
// hierarchy, so GioView's SurfaceView surface is never destroyed.
if (view.getVisibility() != View.VISIBLE) {
view.setVisibility(View.VISIBLE);
isLayoutUpdated = true;
}
if (view.getZ() != (float) zOrder) {
// Suppress any outline-based drop shadow the Z value would produce.
view.setOutlineProvider(null);
view.setZ((float) zOrder);
isLayoutUpdated = true;
}
}

ViewGroup.LayoutParams lp = view.getLayoutParams();
if (lp == null || lp.width != w || lp.height != h) {
view.setLayoutParams(new FrameLayout.LayoutParams(w, h));
isLayoutUpdated = true;
}

int oldX = (int) view.getX();
int oldY = (int) view.getY();
if (oldX != x || oldY != y) {
view.setX(x);
view.setY(y);
isLayoutUpdated = true;
}

if (isLayoutUpdated) {
view.invalidate();
view.requestLayout();
this.invalidate();
}

return isLayoutUpdated;
}

public void updateLayout() {
ViewGroup parent = (ViewGroup) this.getParent();
if (parent != null) {
parent.bringChildToFront(this);
parent.forceLayout();
}
}

static private native long onCreateView(GioView view);
static private native void onDestroyView(long handle);
static private native void onStartView(long handle);
Expand Down Expand Up @@ -583,6 +661,9 @@ void updateCaret(float m00, float m01, float m02, float m10, float m11, float m1
static private native int imeToRunes(long handle, int chars);
// imeToUTF16 converts the rune index into Java characters.
static private native int imeToUTF16(long handle, int runes);
// hitTest returns true if the point should be handled by Gio,
// false if it should be passed through to external views.
static private native boolean hitTest(long handle, float x, float y);

private class GioInputConnection implements InputConnection {
private int batchDepth;
Expand Down
4 changes: 3 additions & 1 deletion app/gl_ios.m
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,9 @@ CFTypeRef gio_createGLLayer(void) {
return nil;
}
layer.drawableProperties = @{kEAGLDrawablePropertyColorFormat: kEAGLColorFormatSRGBA8};
layer.opaque = YES;
// Enable transparency for external views.
layer.opaque = NO;
layer.backgroundColor = [UIColor clearColor].CGColor;
layer.anchorPoint = CGPointMake(0, 0);
return CFBridgingRetain(layer);
}
6 changes: 5 additions & 1 deletion app/gl_macos.m
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,11 @@

CALayer *gio_layerFactory(BOOL presentWithTrans) {
@autoreleasepool {
return [CALayer layer];
CALayer *l = [CALayer layer];
// Enable transparency for external views.
l.opaque = NO;
l.backgroundColor = [NSColor clearColor].CGColor;
return l;
}
}

Expand Down
48 changes: 48 additions & 0 deletions app/internal/windows/windows.go
Original file line number Diff line number Diff line change
Expand Up @@ -208,7 +208,10 @@ const (
CFS_CANDIDATEPOS = 0x0040

HWND_TOPMOST = ^(uint32(1) - 1) // -1
HWND_TOP = 0
HWND_BOTTOM = 1

HTTRANSPARENT = ^uintptr(0) // -1, pass through to underlying window
HTCAPTION = 2
HTCLIENT = 1
HTLEFT = 10
Expand All @@ -220,6 +223,15 @@ const (
HTBOTTOMLEFT = 16
HTBOTTOMRIGHT = 17

// Region operations for CombineRgn
RGN_AND = 1
RGN_OR = 2
RGN_XOR = 3
RGN_DIFF = 4
RGN_COPY = 5
RGN_NULL = 1
RGN_ERROR = 0

IDC_APPSTARTING = 32650 // Standard arrow and small hourglass
IDC_ARROW = 32512 // Standard arrow
IDC_CROSS = 32515 // Crosshair
Expand Down Expand Up @@ -259,8 +271,10 @@ const (
SW_SHOWMAXIMIZED = 3
SW_SHOWNORMAL = 1
SW_SHOW = 5
SW_HIDE = 0

SWP_FRAMECHANGED = 0x0020
SWP_NOACTIVATE = 0x0010
SWP_NOMOVE = 0x0002
SWP_NOOWNERZORDER = 0x0200
SWP_NOSIZE = 0x0001
Expand Down Expand Up @@ -378,6 +392,7 @@ const (
WS_THICKFRAME = 0x00040000
WS_MINIMIZEBOX = 0x00020000
WS_MAXIMIZEBOX = 0x00010000
WS_CHILD = 0x40000000

WS_EX_APPWINDOW = 0x00040000
WS_EX_WINDOWEDGE = 0x00000100
Expand Down Expand Up @@ -472,6 +487,7 @@ var (
_SetWindowLong32 = user32.NewProc("SetWindowLongW")
_SetWindowPlacement = user32.NewProc("SetWindowPlacement")
_SetWindowPos = user32.NewProc("SetWindowPos")
_SetWindowRgn = user32.NewProc("SetWindowRgn")
_SetWindowText = user32.NewProc("SetWindowTextW")
_TranslateMessage = user32.NewProc("TranslateMessage")
_UnregisterClass = user32.NewProc("UnregisterClassW")
Expand All @@ -482,6 +498,9 @@ var (

gdi32 = syscall.NewLazySystemDLL("gdi32")
_GetDeviceCaps = gdi32.NewProc("GetDeviceCaps")
_CreateRectRgn = gdi32.NewProc("CreateRectRgn")
_CombineRgn = gdi32.NewProc("CombineRgn")
_DeleteObject = gdi32.NewProc("DeleteObject")

imm32 = syscall.NewLazySystemDLL("imm32")
_ImmGetContext = imm32.NewProc("ImmGetContext")
Expand Down Expand Up @@ -993,3 +1012,32 @@ func (p *WindowPlacement) Set(Left, Top, Right, Bottom int) {
p.rcNormalPosition.Right = int32(Right)
p.rcNormalPosition.Bottom = int32(Bottom)
}

// CreateRectRgn creates a rectangular region.
func CreateRectRgn(left, top, right, bottom int32) syscall.Handle {
r, _, _ := _CreateRectRgn.Call(uintptr(left), uintptr(top), uintptr(right), uintptr(bottom))
return syscall.Handle(r)
}

// CombineRgn combines two regions.
func CombineRgn(dst, src1, src2 syscall.Handle, mode int) int {
r, _, _ := _CombineRgn.Call(uintptr(dst), uintptr(src1), uintptr(src2), uintptr(mode))
return int(r)
}

// DeleteObject deletes a GDI object.
func DeleteObject(h syscall.Handle) bool {
r, _, _ := _DeleteObject.Call(uintptr(h))
return r != 0
}

// SetWindowRgn sets the window region for hit testing.
// Pass 0 as hRgn to reset to the full window rect.
func SetWindowRgn(hwnd syscall.Handle, hRgn syscall.Handle, redraw bool) bool {
var redrawInt uintptr
if redraw {
redrawInt = 1
}
r, _, _ := _SetWindowRgn.Call(uintptr(hwnd), uintptr(hRgn), redrawInt)
return r != 0
}
3 changes: 3 additions & 0 deletions app/metal_ios.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,9 @@ static CFTypeRef getMetalLayer(CFTypeRef viewRef) {
CAMetalLayer *l = (CAMetalLayer *)view.layer;
l.needsDisplayOnBoundsChange = YES;
l.presentsWithTransaction = YES;
// Enable transparency for external views.
l.opaque = NO;
l.backgroundColor = [UIColor clearColor].CGColor;
return CFBridgingRetain(l);
}
}
Expand Down
3 changes: 3 additions & 0 deletions app/metal_macos.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,9 @@ CALayer *gio_layerFactory(BOOL presentWithTrans) {
l.autoresizingMask = kCALayerHeightSizable|kCALayerWidthSizable;
l.needsDisplayOnBoundsChange = YES;
l.presentsWithTransaction = presentWithTrans;
// Enable transparency for external views.
l.opaque = NO;
l.backgroundColor = [NSColor clearColor].CGColor;
return l;
}
}
Expand Down
74 changes: 54 additions & 20 deletions app/os_android.go
Original file line number Diff line number Diff line change
Expand Up @@ -138,6 +138,7 @@ import (

"gioui.org/io/transfer"

"gioui.org/gpu"
"gioui.org/internal/f32color"
"gioui.org/op"

Expand Down Expand Up @@ -176,30 +177,35 @@ type window struct {
focusID input.SemanticID
diffs []input.SemanticID
}

// embedRegions tracks areas for hit testing.
embedRegions gpu.EmbedRegions
}

// gioView hold cached JNI methods for GioView.
var gioView struct {
once sync.Once
getDensity C.jmethodID
getFontScale C.jmethodID
showTextInput C.jmethodID
hideTextInput C.jmethodID
setInputHint C.jmethodID
postFrameCallback C.jmethodID
invalidate C.jmethodID // requests draw, called from UI thread
setCursor C.jmethodID
setOrientation C.jmethodID
setNavigationColor C.jmethodID
setStatusColor C.jmethodID
setFullscreen C.jmethodID
unregister C.jmethodID
sendA11yEvent C.jmethodID
sendA11yChange C.jmethodID
isA11yActive C.jmethodID
restartInput C.jmethodID
updateSelection C.jmethodID
updateCaret C.jmethodID
once sync.Once
getDensity C.jmethodID
getFontScale C.jmethodID
showTextInput C.jmethodID
hideTextInput C.jmethodID
setInputHint C.jmethodID
postFrameCallback C.jmethodID
invalidate C.jmethodID // requests draw, called from UI thread
setCursor C.jmethodID
setOrientation C.jmethodID
setNavigationColor C.jmethodID
setStatusColor C.jmethodID
setFullscreen C.jmethodID
unregister C.jmethodID
sendA11yEvent C.jmethodID
sendA11yChange C.jmethodID
isA11yActive C.jmethodID
restartInput C.jmethodID
updateSelection C.jmethodID
updateCaret C.jmethodID
updateLayout C.jmethodID
setEmbedViewPosition C.jmethodID
}

type pixelInsets struct {
Expand Down Expand Up @@ -486,6 +492,8 @@ func Java_org_gioui_GioView_onCreateView(env *C.JNIEnv, class C.jclass, view C.j
m.restartInput = getMethodID(env, class, "restartInput", "()V")
m.updateSelection = getMethodID(env, class, "updateSelection", "()V")
m.updateCaret = getMethodID(env, class, "updateCaret", "(FFFFFFFFFF)V")
m.updateLayout = getMethodID(env, class, "updateLayout", "()V")
m.setEmbedViewPosition = getMethodID(env, class, "setEmbedViewPosition", "(Landroid/view/View;IIIII)Z")
})
view = C.jni_NewGlobalRef(env, view)
wopts := <-mainWindow.out
Expand Down Expand Up @@ -910,6 +918,23 @@ func (w *window) draw(env *C.JNIEnv, sync bool) {
callVoidMethod(env, w.view, gioView.sendA11yChange, jvalue(w.virtualIDFor(id)))
}
}

currentRegions, lostRegions := w.callbacks.EmbeddedRegions()
var needsUpdate bool
for index, region := range currentRegions.Views {
if ok, _ := callBooleanMethod(env, w.view, gioView.setEmbedViewPosition, jvalue(region.View), jvalue(region.Area.Min.X), jvalue(region.Area.Min.Y), jvalue(region.Area.Dx()), jvalue(region.Area.Dy()), jvalue(index+1)); ok {
needsUpdate = true
}
}
for _, lost := range lostRegions {
if ok, _ := callBooleanMethod(env, w.view, gioView.setEmbedViewPosition, jvalue(lost.View), jvalue(0), jvalue(0), jvalue(0), jvalue(0), jvalue(0)); ok {
needsUpdate = true
}
}
if needsUpdate {
callVoidMethod(env, w.view, gioView.updateLayout)
}
w.embedRegions = currentRegions
}

func runInJVM(jvm *C.JavaVM, f func(env *C.JNIEnv)) {
Expand Down Expand Up @@ -984,6 +1009,15 @@ func Java_org_gioui_GioView_onKeyEvent(env *C.JNIEnv, class C.jclass, handle C.j
}
}

//export Java_org_gioui_GioView_hitTest
func Java_org_gioui_GioView_hitTest(env *C.JNIEnv, class C.jclass, handle C.jlong, x, y C.jfloat) C.jboolean {
w := cgo.Handle(handle).Value().(*window)
if w.embedRegions.Contains(f32.Point{X: float32(x), Y: float32(y)}) {
return C.JNI_FALSE
}
return C.JNI_TRUE
}

//export Java_org_gioui_GioView_onTouchEvent
func Java_org_gioui_GioView_onTouchEvent(env *C.JNIEnv, class C.jclass, handle C.jlong, action, pointerID, tool C.jint, x, y, scrollX, scrollY C.jfloat, jbtns C.jint, t C.jlong) {
w := cgo.Handle(handle).Value().(*window)
Expand Down
Loading