From a48126cec795c08244fa83cbfe71b734c58fae2f Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Fri, 22 Aug 2014 11:29:44 -0400 Subject: [PATCH] Added Area.OpenTextFieldAt() and implemented it on GTK+. --- redo/area.go | 16 ++++++++- redo/area_unix.go | 74 +++++++++++++++++++++++++++++++++++++++++ redo/yz_repaint_test.go | 11 +++++- 3 files changed, 99 insertions(+), 2 deletions(-) diff --git a/redo/area.go b/redo/area.go index 829fc33..cd7f3e1 100644 --- a/redo/area.go +++ b/redo/area.go @@ -27,7 +27,7 @@ import ( // Character processing methods differ across operating // systems; trying ot recreate these yourself is only going // to lead to trouble. -// [FOR FUTURE PLANNING Use TextArea instead, providing a TextAreaHandler.] +// If you absolutely need to enter text somehow, use OpenTextFieldAt() and its related methods. type Area interface { Control @@ -44,6 +44,20 @@ type Area interface { // RepaintAll marks the entirety of the Area as needing to be redrawn. RepaintAll() + + // OpenTextFieldAt opens a TextField with the top-left corner at the given coordinates of the Area. + // It panics if the coordinates fall outside the Area. + // Any text previously in the TextField is removed. + // The TextField receives the input focus so the user can type things; when the TextField loses the input focus, it hides itself and signals the event set by OnTextFieldDismissed. + // TODO escape key + OpenTextFieldAt(x int, y int) + + // TextFieldText and TextFieldSetText get and set the OpenTextFieldAt TextField's text, respectively. + TextFieldText() string + SetTextFieldText(text string) + + // OnTextFieldDismissed is an event that is fired when the OpenTextFieldAt TextField is dismissed. + OnTextFieldDismissed(f func()) } type areabase struct { diff --git a/redo/area_unix.go b/redo/area_unix.go index 7cee97b..ed52beb 100644 --- a/redo/area_unix.go +++ b/redo/area_unix.go @@ -11,6 +11,8 @@ import ( ) // #include "gtk_unix.h" +// extern gboolean our_area_get_child_position_callback(GtkOverlay *, GtkWidget *, GdkRectangle *, gpointer); +// extern gboolean our_area_textfield_focus_out_event_callback(GtkWidget *, GdkEvent *, gpointer); // extern gboolean our_area_draw_callback(GtkWidget *, cairo_t *, gpointer); // extern gboolean our_area_button_press_event_callback(GtkWidget *, GdkEvent *, gpointer); // extern gboolean our_area_button_release_event_callback(GtkWidget *, GdkEvent *, gpointer); @@ -36,6 +38,12 @@ type area struct { scroller *scroller clickCounter *clickCounter + + textfieldw *C.GtkWidget + textfield *C.GtkEntry + textfieldx int + textfieldy int + textfielddone *event } func newArea(ab *areabase) Area { @@ -46,12 +54,16 @@ func newArea(ab *areabase) Area { C.GDK_BUTTON_PRESS_MASK|C.GDK_BUTTON_RELEASE_MASK|C.GDK_POINTER_MOTION_MASK|C.GDK_BUTTON_MOTION_MASK|C.GDK_ENTER_NOTIFY_MASK|C.GDK_LEAVE_NOTIFY_MASK) // and we need to allow focusing on a GtkDrawingArea to enable keyboard events C.gtk_widget_set_can_focus(widget, C.TRUE) + textfieldw := C.gtk_entry_new() a := &area{ areabase: ab, _widget: widget, drawingarea: (*C.GtkDrawingArea)(unsafe.Pointer(widget)), scroller: newScroller(widget, false, false, true), // not natively scrollable; no border; have an overlay for OpenTextFieldAt() clickCounter: new(clickCounter), + textfieldw: textfieldw, + textfield: (*C.GtkEntry)(unsafe.Pointer(textfieldw)), + textfielddone: newEvent(), } for _, c := range areaCallbacks { g_signal_connect( @@ -61,6 +73,19 @@ func newArea(ab *areabase) Area { C.gpointer(unsafe.Pointer(a))) } a.SetSize(a.width, a.height) + C.gtk_overlay_add_overlay(a.scroller.overlay, a.textfieldw) + g_signal_connect( + C.gpointer(unsafe.Pointer(a.scroller.overlay)), + "get-child-position", + area_get_child_position_callback, + C.gpointer(unsafe.Pointer(a))) + g_signal_connect( + C.gpointer(unsafe.Pointer(a.textfield)), + "focus-out-event", + area_textfield_focus_out_event_callback, + C.gpointer(unsafe.Pointer(a))) + // the widget shows up initially + C.gtk_widget_hide(a.textfieldw) return a } @@ -83,6 +108,55 @@ func (a *area) RepaintAll() { C.gtk_widget_queue_draw(a._widget) } +func (a *area) OpenTextFieldAt(x, y int) { + if x < 0 || x >= a.width || y < 0 || y >= a.height { + panic(fmt.Errorf("point (%d,%d) outside Area in Area.OpenTextFieldAt()", x, y)) + } + a.textfieldx = x + a.textfieldy = y + a.SetTextFieldText("") + C.gtk_widget_show_all(a.textfieldw) + C.gtk_widget_grab_focus(a.textfieldw) +} + +func (a *area) TextFieldText() string { + return fromgstr(C.gtk_entry_get_text(a.textfield)) +} + +func (a *area) SetTextFieldText(text string) { + ctext := togstr(text) + defer freegstr(ctext) + C.gtk_entry_set_text(a.textfield, ctext) +} + +func (a *area) OnTextFieldDismissed(f func()) { + a.textfielddone.set(f) +} + +//export our_area_get_child_position_callback +func our_area_get_child_position_callback(overlay *C.GtkOverlay, widget *C.GtkWidget, rect *C.GdkRectangle, data C.gpointer) C.gboolean { + var nat C.GtkRequisition + + a := (*area)(unsafe.Pointer(data)) + rect.x = C.int(a.textfieldx) + rect.y = C.int(a.textfieldy) + C.gtk_widget_get_preferred_size(a.textfieldw, nil, &nat) + rect.width = C.int(nat.width) + rect.height = C.int(nat.height) + return C.TRUE +} + +var area_get_child_position_callback = C.GCallback(C.our_area_get_child_position_callback) + +//export our_area_textfield_focus_out_event_callback +func our_area_textfield_focus_out_event_callback(widget *C.GtkWidget, event *C.GdkEvent, data C.gpointer) C.gboolean { + a := (*area)(unsafe.Pointer(data)) + C.gtk_widget_hide(a.textfieldw) + return continueEventChain +} + +var area_textfield_focus_out_event_callback = C.GCallback(C.our_area_textfield_focus_out_event_callback) + var areaCallbacks = []struct { name string callback C.GCallback diff --git a/redo/yz_repaint_test.go b/redo/yz_repaint_test.go index 254c079..5783bac 100644 --- a/redo/yz_repaint_test.go +++ b/redo/yz_repaint_test.go @@ -56,7 +56,16 @@ func (r *repainter) Paint(rect image.Rectangle) *image.RGBA { return r.img.SubImage(rect).(*image.RGBA) } -func (r *repainter) Mouse(me MouseEvent) {} +func (r *repainter) Mouse(me MouseEvent) { + if me.Up == 1 { + r.area.OpenTextFieldAt(me.Pos.X, me.Pos.Y) + } +} + +func (r *repainter) tfdone() { + println(r.area.TextFieldText()) +} + func (r *repainter) Key(ke KeyEvent) bool { return false } func (r *repainter) setx() {