diff options
author | Jaakko Keränen <jaakko.keranen@iki.fi> | 2021-04-11 08:34:14 +0300 |
---|---|---|
committer | Jaakko Keränen <jaakko.keranen@iki.fi> | 2021-04-11 08:34:14 +0300 |
commit | e08bf8f56c078ba0e5c2aa363c43ea5db3561aa2 (patch) | |
tree | 6fbe657e55d531988e184b289275e5599ba2d4e4 /src/ui/documentwidget.c | |
parent | 44b1f58bb25817d2bc4578e727ce637752d00299 (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.c | 202 |
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 | ||
205 | enum iDocumentLinkOrdinalMode { | 207 | enum 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 | ||
375 | static 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 | |||
370 | static void enableActions_DocumentWidget_(iDocumentWidget *d, iBool enable) { | 384 | static 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 | /*----------------------------------------------------------------------------------------------*/ |