diff --git a/examples/drawtext/hittest.c b/examples/drawtext/hittest.c index 4d7098d2..04daaa33 100644 --- a/examples/drawtext/hittest.c +++ b/examples/drawtext/hittest.c @@ -3,6 +3,7 @@ // TODO have a ligature // TODO allow clicking on the end of a line to put the caret there +// TODO the hiding and showing does not work properly on GTK+ static const char *text = "Each of the glyphs an end user interacts with are called graphemes. " @@ -172,12 +173,12 @@ static void mouse(uiAreaMouseEvent *e) // urgh %zd is not supported by MSVC with sprintf() // TODO get that part in test/ about having no other option // TODO byte 1 is actually byte 684?! -/* sprintf(labelText, "pos %d line %d x position %s y position %s", - (int) (res.Pos), res.Line, + sprintf(labelText, "pos %d line %d x %g",// x position %s y position %s", + (int) pos, caretLine, caretX); +/* (int) (res.Pos), res.Line, positions[res.XPosition], positions[res.YPosition]); -*/ sprintf(labelText, "TODO\n"); - uiLabelSetText(caretLabel, labelText); +*/ uiLabelSetText(caretLabel, labelText); /* cursorX = res.CaretX; cursorY = res.CaretY; diff --git a/unix/drawtext.c b/unix/drawtext.c index 88cc8726..879cace9 100644 --- a/unix/drawtext.c +++ b/unix/drawtext.c @@ -187,6 +187,8 @@ void uiDrawTextLayoutLineGetMetrics(uiDrawTextLayout *tl, int line, uiDrawTextLa } #endif +#if 0 /* TODO */ + // caret behavior, based on GtkTextView (both 2 and 3) and Qt's text views (both 4 and 5), which are all based on TkTextView: // - clicking on the right side of a line places the cursor at the beginning of the LAST grapheme on the line // - pressing Right goes to the first grapheme of the next line, pressing Left goes back to the last grapheme of the original line @@ -264,3 +266,63 @@ void uiDrawTextLayoutByteRangeToRectangle(uiDrawTextLayout *tl, size_t start, si // TODO unref pll? } + +#endif + +// this algorithm comes from Microsoft's PadWrite sample, following TextEditor::SetSelectionFromPoint() +// TODO this or the next one doesn't work right for the end of a line? it still behaves like TkTextView... +void uiDrawTextLayoutHitTest(uiDrawTextLayout *tl, double x, double y, size_t *pos, int *line) +{ + int p, trailing; + + pango_layout_xy_to_index(tl->layout, + cairoToPango(x), cairoToPango(y), + &p, &trailing); + // on a trailing hit, align to the nearest cluster + // fortunately Pango provides that info directly + if (trailing != 0) + p += trailing; + if (pos != NULL) + *pos = p; + + // TODO do the line detection unconditionally? + // TODO optimize the call to pango_layout_get_line_count() + if (line != NULL) { + int i; + + for (i = 0; i < pango_layout_get_line_count(tl->layout); i++) { + double ltop, lbottom; + + ltop = tl->lineMetrics[i].Y; + lbottom = ltop + tl->lineMetrics[i].Height; + // y will already >= ltop at this point since the past lbottom should == ltop + if (y < lbottom) + break; + } + if (i == pango_layout_get_line_count(tl->layout)) + i--; + *line = i; + } +} + +// TODO find a good API for indicating that this character isn't on the line for when the layout is resized and doing that recalculation... +double uiDrawTextLayoutByteLocationInLine(uiDrawTextLayout *tl, size_t pos, int line) +{ + PangoLayoutLine *pll; + gboolean trailing; + int pangox; + + pll = pango_layout_get_line_readonly(tl->layout, line); + // this behavior seems correct + // there's also PadWrite's TextEditor::GetCaretRect() but that requires state... + // TODO where does this fail? + // TODO optimize everything to avoid calling strlen() + trailing = 0; + if (pos != 0 && pos != strlen(pango_layout_get_text(tl->layout)) && pos == (pll->start_index + pll->length)) { + pos--; + trailing = 1; + } + pango_layout_line_index_to_x(pll, pos, trailing, &pangox); + // TODO unref pll? + return pangoToCairo(pangox); +}