summaryrefslogtreecommitdiff
path: root/src/ui/documentwidget.c
diff options
context:
space:
mode:
authorJaakko Keränen <jaakko.keranen@iki.fi>2021-04-11 08:34:14 +0300
committerJaakko Keränen <jaakko.keranen@iki.fi>2021-04-11 08:34:14 +0300
commite08bf8f56c078ba0e5c2aa363c43ea5db3561aa2 (patch)
tree6fbe657e55d531988e184b289275e5599ba2d4e4 /src/ui/documentwidget.c
parent44b1f58bb25817d2bc4578e727ce637752d00299 (diff)
Mobile: Long press to select; navbar page menu
DocumentWidget now supports a long-press drag selection mode. The context menu was moved to the URL bar, replacing the reload button.
Diffstat (limited to 'src/ui/documentwidget.c')
-rw-r--r--src/ui/documentwidget.c202
1 files changed, 165 insertions, 37 deletions
diff --git a/src/ui/documentwidget.c b/src/ui/documentwidget.c
index e18d5283..105a7158 100644
--- a/src/ui/documentwidget.c
+++ b/src/ui/documentwidget.c
@@ -200,6 +200,8 @@ enum iDocumentWidgetFlag {
200 selectWords_DocumentWidgetFlag = iBit(7), 200 selectWords_DocumentWidgetFlag = iBit(7),
201 selectLines_DocumentWidgetFlag = iBit(8), 201 selectLines_DocumentWidgetFlag = iBit(8),
202 pinchZoom_DocumentWidgetFlag = iBit(9), 202 pinchZoom_DocumentWidgetFlag = iBit(9),
203 movingSelectMarkStart_DocumentWidgetFlag = iBit(10),
204 movingSelectMarkEnd_DocumentWidgetFlag = iBit(11),
203}; 205};
204 206
205enum iDocumentLinkOrdinalMode { 207enum iDocumentLinkOrdinalMode {
@@ -251,6 +253,7 @@ struct Impl_DocumentWidget {
251 const iGmRun * firstVisibleRun; 253 const iGmRun * firstVisibleRun;
252 const iGmRun * lastVisibleRun; 254 const iGmRun * lastVisibleRun;
253 iClick click; 255 iClick click;
256 iInt2 contextPos; /* coordinates of latest right click */
254 iString pendingGotoHeading; 257 iString pendingGotoHeading;
255 float initNormScrollY; 258 float initNormScrollY;
256 iAnim scrollY; 259 iAnim scrollY;
@@ -259,6 +262,7 @@ struct Impl_DocumentWidget {
259 iScrollWidget *scroll; 262 iScrollWidget *scroll;
260 iWidget * menu; 263 iWidget * menu;
261 iWidget * playerMenu; 264 iWidget * playerMenu;
265 iWidget * copyMenu;
262 iVisBuf * visBuf; 266 iVisBuf * visBuf;
263 iPtrSet * invalidRuns; 267 iPtrSet * invalidRuns;
264 iDrawBufs * drawBufs; /* dynamic state for drawing */ 268 iDrawBufs * drawBufs; /* dynamic state for drawing */
@@ -324,6 +328,7 @@ void init_DocumentWidget(iDocumentWidget *d) {
324 addChild_Widget(w, iClob(d->scroll = new_ScrollWidget())); 328 addChild_Widget(w, iClob(d->scroll = new_ScrollWidget()));
325 d->menu = NULL; /* created when clicking */ 329 d->menu = NULL; /* created when clicking */
326 d->playerMenu = NULL; 330 d->playerMenu = NULL;
331 d->copyMenu = NULL;
327 d->drawBufs = new_DrawBufs(); 332 d->drawBufs = new_DrawBufs();
328 d->translation = NULL; 333 d->translation = NULL;
329 addChildFlags_Widget(w, 334 addChildFlags_Widget(w,
@@ -367,6 +372,15 @@ void deinit_DocumentWidget(iDocumentWidget *d) {
367 deinit_PersistentDocumentState(&d->mod); 372 deinit_PersistentDocumentState(&d->mod);
368} 373}
369 374
375static iRangecc selectMark_DocumentWidget_(const iDocumentWidget *d) {
376 /* Normalize so start < end. */
377 iRangecc norm = d->selectMark;
378 if (norm.start > norm.end) {
379 iSwap(const char *, norm.start, norm.end);
380 }
381 return norm;
382}
383
370static void enableActions_DocumentWidget_(iDocumentWidget *d, iBool enable) { 384static void enableActions_DocumentWidget_(iDocumentWidget *d, iBool enable) {
371 /* Actions are invisible child widgets of the DocumentWidget. */ 385 /* Actions are invisible child widgets of the DocumentWidget. */
372 iForEach(ObjectList, i, children_Widget(d)) { 386 iForEach(ObjectList, i, children_Widget(d)) {
@@ -1747,6 +1761,26 @@ static iBool handleCommand_DocumentWidget_(iDocumentWidget *d, const char *cmd)
1747 updateWindowTitle_DocumentWidget_(d); 1761 updateWindowTitle_DocumentWidget_(d);
1748 return iFalse; 1762 return iFalse;
1749 } 1763 }
1764 else if (equal_Command(cmd, "document.select") && d == document_App()) {
1765 /* Touch selection mode. */
1766 if (!arg_Command(cmd)) {
1767 d->selectMark = iNullRange;
1768 setFlags_Widget(w, touchDrag_WidgetFlag, iFalse);
1769 setFadeEnabled_ScrollWidget(d->scroll, iTrue);
1770 }
1771 else {
1772 setFlags_Widget(w, touchDrag_WidgetFlag, iTrue);
1773 d->flags |= movingSelectMarkEnd_DocumentWidgetFlag |
1774 selectWords_DocumentWidgetFlag; /* finger-based selection is imprecise */
1775 d->flags &= ~selectLines_DocumentWidgetFlag;
1776 setFadeEnabled_ScrollWidget(d->scroll, iFalse);
1777 d->selectMark = sourceLoc_DocumentWidget_(d, d->contextPos);
1778 extendRange_Rangecc(&d->selectMark, range_String(source_GmDocument(d->doc)),
1779 word_RangeExtension | bothStartAndEnd_RangeExtension);
1780 d->initialSelectMark = d->selectMark;
1781 }
1782 return iTrue;
1783 }
1750 else if (equal_Command(cmd, "document.info") && d == document_App()) { 1784 else if (equal_Command(cmd, "document.info") && d == document_App()) {
1751 const char *unchecked = red_ColorEscape "\u2610"; 1785 const char *unchecked = red_ColorEscape "\u2610";
1752 const char *checked = green_ColorEscape "\u2611"; 1786 const char *checked = green_ColorEscape "\u2611";
@@ -1870,6 +1904,9 @@ static iBool handleCommand_DocumentWidget_(iDocumentWidget *d, const char *cmd)
1870 } 1904 }
1871 SDL_SetClipboardText(cstr_String(copied)); 1905 SDL_SetClipboardText(cstr_String(copied));
1872 delete_String(copied); 1906 delete_String(copied);
1907 if (flags_Widget(w) & touchDrag_WidgetFlag) {
1908 postCommand_App("document.select arg:0");
1909 }
1873 return iTrue; 1910 return iTrue;
1874 } 1911 }
1875 else if (equal_Command(cmd, "document.copylink") && document_App() == d) { 1912 else if (equal_Command(cmd, "document.copylink") && document_App() == d) {
@@ -1960,7 +1997,7 @@ static iBool handleCommand_DocumentWidget_(iDocumentWidget *d, const char *cmd)
1960 cacheDocumentGlyphs_DocumentWidget_(d); 1997 cacheDocumentGlyphs_DocumentWidget_(d);
1961 return iFalse; 1998 return iFalse;
1962 } 1999 }
1963 else if (equalWidget_Command(cmd, w, "document.translate")) { 2000 else if (equal_Command(cmd, "document.translate") && d == document_App()) {
1964 if (!d->translation) { 2001 if (!d->translation) {
1965 d->translation = new_Translation(d); 2002 d->translation = new_Translation(d);
1966 } 2003 }
@@ -2623,10 +2660,12 @@ static iBool processEvent_DocumentWidget_(iDocumentWidget *d, const SDL_Event *e
2623 } 2660 }
2624 if (ev->button.button == SDL_BUTTON_RIGHT && 2661 if (ev->button.button == SDL_BUTTON_RIGHT &&
2625 contains_Widget(w, init_I2(ev->button.x, ev->button.y))) { 2662 contains_Widget(w, init_I2(ev->button.x, ev->button.y))) {
2626 if (!d->menu || !isVisible_Widget(d->menu)) { 2663 if (!isVisible_Widget(d->menu)) {
2627 d->contextLink = d->hoverLink; 2664 d->contextLink = d->hoverLink;
2665 d->contextPos = init_I2(ev->button.x, ev->button.y);
2628 if (d->menu) { 2666 if (d->menu) {
2629 destroy_Widget(d->menu); 2667 destroy_Widget(d->menu);
2668 d->menu = NULL;
2630 } 2669 }
2631 setFocus_Widget(NULL); 2670 setFocus_Widget(NULL);
2632 iArray items; 2671 iArray items;
@@ -2637,6 +2676,12 @@ static iBool processEvent_DocumentWidget_(iDocumentWidget *d, const SDL_Event *e
2637 const iRangecc scheme = urlScheme_String(linkUrl); 2676 const iRangecc scheme = urlScheme_String(linkUrl);
2638 const iBool isGemini = equalCase_Rangecc(scheme, "gemini"); 2677 const iBool isGemini = equalCase_Rangecc(scheme, "gemini");
2639 iBool isNative = iFalse; 2678 iBool isNative = iFalse;
2679 if (deviceType_App() != desktop_AppDeviceType) {
2680 /* Show the link as the first, non-interactive item. */
2681 pushBack_Array(&items, &(iMenuItem){
2682 format_CStr("```%s", cstr_String(linkUrl)),
2683 0, 0, NULL });
2684 }
2640 if (willUseProxy_App(scheme) || isGemini || 2685 if (willUseProxy_App(scheme) || isGemini ||
2641 equalCase_Rangecc(scheme, "finger") || 2686 equalCase_Rangecc(scheme, "finger") ||
2642 equalCase_Rangecc(scheme, "gopher")) { 2687 equalCase_Rangecc(scheme, "gopher")) {
@@ -2707,24 +2752,18 @@ static iBool processEvent_DocumentWidget_(iDocumentWidget *d, const SDL_Event *e
2707 } 2752 }
2708 } 2753 }
2709 } 2754 }
2710 else { 2755 else if (deviceType_App() == desktop_AppDeviceType) {
2711 if (!isEmpty_Range(&d->selectMark)) { 2756 if (!isEmpty_Range(&d->selectMark)) {
2712 pushBackN_Array( 2757 pushBackN_Array(&items,
2713 &items, 2758 (iMenuItem[]){ { "${menu.copy}", 0, 0, "copy" },
2714 (iMenuItem[]){ { "${menu.copy}", 0, 0, "copy" }, { "---", 0, 0, NULL } }, 2759 { "---", 0, 0, NULL } },
2715 2); 2760 2);
2716 }
2717 if (deviceType_App() == desktop_AppDeviceType) {
2718 pushBackN_Array(
2719 &items,
2720 (iMenuItem[]){
2721 { "${menu.back}", navigateBack_KeyShortcut, "navigate.back" },
2722 { "${menu.forward}", navigateForward_KeyShortcut, "navigate.forward" } },
2723 2);
2724 } 2761 }
2725 pushBackN_Array( 2762 pushBackN_Array(
2726 &items, 2763 &items,
2727 (iMenuItem[]){ 2764 (iMenuItem[]){
2765 { "${menu.back}", navigateBack_KeyShortcut, "navigate.back" },
2766 { "${menu.forward}", navigateForward_KeyShortcut, "navigate.forward" },
2728 { upArrow_Icon " ${menu.parent}", navigateParent_KeyShortcut, "navigate.parent" }, 2767 { upArrow_Icon " ${menu.parent}", navigateParent_KeyShortcut, "navigate.parent" },
2729 { upArrowBar_Icon " ${menu.root}", navigateRoot_KeyShortcut, "navigate.root" }, 2768 { upArrowBar_Icon " ${menu.root}", navigateRoot_KeyShortcut, "navigate.root" },
2730 { "---", 0, 0, NULL }, 2769 { "---", 0, 0, NULL },
@@ -2738,7 +2777,7 @@ static iBool processEvent_DocumentWidget_(iDocumentWidget *d, const SDL_Event *e
2738 { globe_Icon " ${menu.page.translate}", 0, 0, "document.translate" }, 2777 { globe_Icon " ${menu.page.translate}", 0, 0, "document.translate" },
2739 { "---", 0, 0, NULL }, 2778 { "---", 0, 0, NULL },
2740 { "${menu.page.copyurl}", 0, 0, "document.copylink" } }, 2779 { "${menu.page.copyurl}", 0, 0, "document.copylink" } },
2741 12); 2780 15);
2742 if (isEmpty_Range(&d->selectMark)) { 2781 if (isEmpty_Range(&d->selectMark)) {
2743 pushBackN_Array( 2782 pushBackN_Array(
2744 &items, 2783 &items,
@@ -2748,6 +2787,21 @@ static iBool processEvent_DocumentWidget_(iDocumentWidget *d, const SDL_Event *e
2748 2); 2787 2);
2749 } 2788 }
2750 } 2789 }
2790 else {
2791 /* Mobile text selection menu. */
2792#if 0
2793 pushBackN_Array(
2794 &items,
2795 (iMenuItem[]){
2796 { "${menu.select}", 0, 0, "document.select arg:1" },
2797 { "${menu.select.word}", 0, 0, "document.select arg:2" },
2798 { "${menu.select.par}", 0, 0, "document.select arg:3" },
2799 },
2800 3);
2801#endif
2802 postCommand_App("document.select arg:1");
2803 return iTrue;
2804 }
2751 d->menu = makeMenu_Widget(w, data_Array(&items), size_Array(&items)); 2805 d->menu = makeMenu_Widget(w, data_Array(&items), size_Array(&items));
2752 deinit_Array(&items); 2806 deinit_Array(&items);
2753 } 2807 }
@@ -2763,26 +2817,29 @@ static iBool processEvent_DocumentWidget_(iDocumentWidget *d, const SDL_Event *e
2763 if (d->grabbedPlayer) { 2817 if (d->grabbedPlayer) {
2764 return iTrue; 2818 return iTrue;
2765 } 2819 }
2820 /* Enable hover state now that scrolling has surely finished. */
2766 if (d->flags & noHoverWhileScrolling_DocumentWidgetFlag) { 2821 if (d->flags & noHoverWhileScrolling_DocumentWidgetFlag) {
2767 d->flags &= ~noHoverWhileScrolling_DocumentWidgetFlag; 2822 d->flags &= ~noHoverWhileScrolling_DocumentWidgetFlag;
2768 updateHover_DocumentWidget_(d, mouseCoord_Window(get_Window())); 2823 updateHover_DocumentWidget_(d, mouseCoord_Window(get_Window()));
2769 } 2824 }
2770 iChangeFlags(d->flags, selecting_DocumentWidgetFlag, iFalse); 2825 if (~flags_Widget(w) & touchDrag_WidgetFlag) {
2771 iChangeFlags(d->flags, selectWords_DocumentWidgetFlag, d->click.count == 2); 2826 iChangeFlags(d->flags, selecting_DocumentWidgetFlag, iFalse);
2772 iChangeFlags(d->flags, selectLines_DocumentWidgetFlag, d->click.count >= 3); 2827 iChangeFlags(d->flags, selectWords_DocumentWidgetFlag, d->click.count == 2);
2773 /* Double/triple clicks marks the selection immediately. */ 2828 iChangeFlags(d->flags, selectLines_DocumentWidgetFlag, d->click.count >= 3);
2774 if (d->click.count >= 2) { 2829 /* Double/triple clicks marks the selection immediately. */
2775 beginMarkingSelection_DocumentWidget_(d, d->click.startPos); 2830 if (d->click.count >= 2) {
2776 extendRange_Rangecc( 2831 beginMarkingSelection_DocumentWidget_(d, d->click.startPos);
2777 &d->selectMark, 2832 extendRange_Rangecc(
2778 range_String(source_GmDocument(d->doc)), 2833 &d->selectMark,
2779 bothStartAndEnd_RangeExtension | 2834 range_String(source_GmDocument(d->doc)),
2780 (d->click.count == 2 ? word_RangeExtension : line_RangeExtension)); 2835 bothStartAndEnd_RangeExtension |
2781 d->initialSelectMark = d->selectMark; 2836 (d->click.count == 2 ? word_RangeExtension : line_RangeExtension));
2782 refresh_Widget(w); 2837 d->initialSelectMark = d->selectMark;
2783 } 2838 refresh_Widget(w);
2784 else { 2839 }
2785 d->initialSelectMark = iNullRange; 2840 else {
2841 d->initialSelectMark = iNullRange;
2842 }
2786 } 2843 }
2787 return iTrue; 2844 return iTrue;
2788 case drag_ClickResult: { 2845 case drag_ClickResult: {
@@ -2796,29 +2853,59 @@ static iBool processEvent_DocumentWidget_(iDocumentWidget *d, const SDL_Event *e
2796 refresh_Widget(w); 2853 refresh_Widget(w);
2797 return iTrue; 2854 return iTrue;
2798 } 2855 }
2799 /* Begin selecting a range of text. */ 2856 /* Fold/unfold a preformatted block. */
2800 if (~d->flags & selecting_DocumentWidgetFlag && d->hoverPre && 2857 if (~d->flags & selecting_DocumentWidgetFlag && d->hoverPre &&
2801 preIsFolded_GmDocument(d->doc, d->hoverPre->preId)) { 2858 preIsFolded_GmDocument(d->doc, d->hoverPre->preId)) {
2802 return iTrue; 2859 return iTrue;
2803 } 2860 }
2861 /* Begin selecting a range of text. */
2804 if (~d->flags & selecting_DocumentWidgetFlag) { 2862 if (~d->flags & selecting_DocumentWidgetFlag) {
2805 beginMarkingSelection_DocumentWidget_(d, d->click.startPos); 2863 beginMarkingSelection_DocumentWidget_(d, d->click.startPos);
2806 } 2864 }
2807 iRangecc loc = sourceLoc_DocumentWidget_(d, pos_Click(&d->click)); 2865 iRangecc loc = sourceLoc_DocumentWidget_(d, pos_Click(&d->click));
2808 if (!d->selectMark.start) { 2866 if (d->selectMark.start == NULL) {
2809 d->selectMark = loc; 2867 d->selectMark = loc;
2810 } 2868 }
2811 else if (loc.end) { 2869 else if (loc.end) {
2812 d->selectMark.end = (d->selectMark.end > d->selectMark.start ? loc.end : loc.start); 2870 if (flags_Widget(w) & touchDrag_WidgetFlag) {
2871 /* Choose which end to move. */
2872 if (!(d->flags & (movingSelectMarkStart_DocumentWidgetFlag |
2873 movingSelectMarkEnd_DocumentWidgetFlag))) {
2874 const iRangecc mark = selectMark_DocumentWidget_(d);
2875 const char * midMark = mark.start + size_Range(&mark) / 2;
2876 const iRangecc loc = sourceLoc_DocumentWidget_(d, pos_Click(&d->click));
2877 const iBool isCloserToStart = d->selectMark.start > d->selectMark.end ?
2878 (loc.start > midMark) : (loc.start < midMark);
2879 iChangeFlags(d->flags, movingSelectMarkStart_DocumentWidgetFlag, isCloserToStart);
2880 iChangeFlags(d->flags, movingSelectMarkEnd_DocumentWidgetFlag, !isCloserToStart);
2881 }
2882 /* Move the start or the end depending on which is nearer. */
2883 if (d->flags & movingSelectMarkStart_DocumentWidgetFlag) {
2884 d->selectMark.start = loc.start;
2885 }
2886 else {
2887 d->selectMark.end = (d->selectMark.end > d->selectMark.start ? loc.end : loc.start);
2888 }
2889 }
2890 else {
2891 d->selectMark.end = (d->selectMark.end > d->selectMark.start ? loc.end : loc.start);
2892 }
2813 } 2893 }
2814 iAssert((!d->selectMark.start && !d->selectMark.end) || 2894 iAssert((!d->selectMark.start && !d->selectMark.end) ||
2815 ( d->selectMark.start && d->selectMark.end)); 2895 ( d->selectMark.start && d->selectMark.end));
2816 /* Extend the selection when double/triple clicking. */ 2896 /* Extend to full words/paragraphs. */
2817 if (d->flags & (selectWords_DocumentWidgetFlag | selectLines_DocumentWidgetFlag)) { 2897 if (d->flags & (selectWords_DocumentWidgetFlag | selectLines_DocumentWidgetFlag)) {
2818 extendRange_Rangecc( 2898 extendRange_Rangecc(
2819 &d->selectMark, 2899 &d->selectMark,
2820 range_String(source_GmDocument(d->doc)), 2900 range_String(source_GmDocument(d->doc)),
2821 d->click.count == 2 ? word_RangeExtension : line_RangeExtension); 2901 (d->flags & movingSelectMarkStart_DocumentWidgetFlag ? moveStart_RangeExtension
2902 : moveEnd_RangeExtension) |
2903 (d->flags & selectWords_DocumentWidgetFlag ? word_RangeExtension
2904 : line_RangeExtension));
2905 if (d->flags & movingSelectMarkStart_DocumentWidgetFlag) {
2906 d->initialSelectMark.start =
2907 d->initialSelectMark.end = d->selectMark.start;
2908 }
2822 if (!isEmpty_Range(&d->initialSelectMark)) { 2909 if (!isEmpty_Range(&d->initialSelectMark)) {
2823 if (d->selectMark.end > d->selectMark.start) { 2910 if (d->selectMark.end > d->selectMark.start) {
2824 d->selectMark.start = d->initialSelectMark.start; 2911 d->selectMark.start = d->initialSelectMark.start;
@@ -2842,13 +2929,42 @@ static iBool processEvent_DocumentWidget_(iDocumentWidget *d, const SDL_Event *e
2842 if (isVisible_Widget(d->menu)) { 2929 if (isVisible_Widget(d->menu)) {
2843 closeMenu_Widget(d->menu); 2930 closeMenu_Widget(d->menu);
2844 } 2931 }
2932 d->flags &= ~(movingSelectMarkStart_DocumentWidgetFlag |
2933 movingSelectMarkEnd_DocumentWidgetFlag);
2845 if (!isMoved_Click(&d->click)) { 2934 if (!isMoved_Click(&d->click)) {
2846 setFocus_Widget(NULL); 2935 setFocus_Widget(NULL);
2936 /* Tap in tap selection mode. */
2937 if (flags_Widget(w) & touchDrag_WidgetFlag) {
2938 const iRangecc tapLoc = sourceLoc_DocumentWidget_(d, pos_Click(&d->click));
2939 /* Tapping on the selection will show a menu. */
2940 const iRangecc mark = selectMark_DocumentWidget_(d);
2941 if (tapLoc.start >= mark.start && tapLoc.end <= mark.end) {
2942 if (d->copyMenu) {
2943 closeMenu_Widget(d->copyMenu);
2944 destroy_Widget(d->copyMenu);
2945 d->copyMenu = NULL;
2946 }
2947 d->copyMenu = makeMenu_Widget(w, (iMenuItem[]){
2948 { clipCopy_Icon " ${menu.copy}", 0, 0, "copy" },
2949 { "---", 0, 0, NULL },
2950 { close_Icon " Clear Selection", 0, 0, "document.select arg:0" },
2951 }, 3);
2952 setFlags_Widget(d->copyMenu, noFadeBackground_WidgetFlag, iTrue);
2953 openMenu_Widget(d->copyMenu, pos_Click(&d->click));
2954 return iTrue;
2955 }
2956 else {
2957 /* Tapping elsewhere exits selection mode. */
2958 postCommand_Widget(d, "document.select arg:0");
2959 return iTrue;
2960 }
2961 }
2847 if (d->hoverPre) { 2962 if (d->hoverPre) {
2848 togglePreFold_DocumentWidget_(d, d->hoverPre->preId); 2963 togglePreFold_DocumentWidget_(d, d->hoverPre->preId);
2849 return iTrue; 2964 return iTrue;
2850 } 2965 }
2851 if (d->hoverLink) { 2966 if (d->hoverLink) {
2967 /* TODO: Move this to a method. */
2852 const iGmLinkId linkId = d->hoverLink->linkId; 2968 const iGmLinkId linkId = d->hoverLink->linkId;
2853 const int linkFlags = linkFlags_GmDocument(d->doc, linkId); 2969 const int linkFlags = linkFlags_GmDocument(d->doc, linkId);
2854 iAssert(linkId); 2970 iAssert(linkId);
@@ -3636,6 +3752,18 @@ static void draw_DocumentWidget_(const iDocumentWidget *d) {
3636 drawCentered_Text(font, bounds, iFalse, uiBackground_ColorId, "%d %%", 3752 drawCentered_Text(font, bounds, iFalse, uiBackground_ColorId, "%d %%",
3637 d->pinchZoomPosted); 3753 d->pinchZoomPosted);
3638 } 3754 }
3755 /* Touch selection indicator. */
3756 if (flags_Widget(w) & touchDrag_WidgetFlag) {
3757 iString msg;
3758 init_String(&msg);
3759 format_String(&msg, "Selecting: drag and tap");
3760 fillRect_Paint(&ctx.paint, (iRect){ topLeft_Rect(bounds),
3761 init_I2(width_Rect(bounds), lineHeight_Text(uiLabelBold_FontId))},
3762 uiTextAction_ColorId);
3763 drawRange_Text(uiLabelBold_FontId, addX_I2(topLeft_Rect(bounds), 3 * gap_UI),
3764 uiBackground_ColorId, range_String(&msg));
3765 deinit_String(&msg);
3766 }
3639} 3767}
3640 3768
3641/*----------------------------------------------------------------------------------------------*/ 3769/*----------------------------------------------------------------------------------------------*/