on xenial help sign in
Merge pull request #391 from ubports/xenial_-_drawer-input-field-changes
e71457dRodney
qml/Launcher/Drawer.qml+23−17
@@ -1,6 +1,6 @@
1/*
1/*
2 * Copyright (C) 2016 Canonical, Ltd.
2 * Copyright (C) 2016 Canonical, Ltd.
3− * Copyright (C) 2020 UBports Foundation.
3+ * Copyright (C) 2020-2021 UBports Foundation
4 *
4 *
5 * This program is free software; you can redistribute it and/or modify
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
6 * it under the terms of the GNU General Public License as published by
@@ -56,16 +56,7 @@ FocusScope {
56 property var oldSelectionEnd: null
56 property var oldSelectionEnd: null
57
57
58 anchors {
58 anchors {
59− onRightMarginChanged: {
59+ onRightMarginChanged: refocusInputAfterUserLetsGo()
60− if (fullyOpen && hadFocus) {
61− // See onDraggingHorizontallyChanged below
62− searchField.focus = hadFocus;
63− searchField.select(oldSelectionStart, oldSelectionEnd);
64− } else if (fullyClosed || fullyOpen) {
65− searchField.text = "";
66− resetOldFocus();
67− }
68− }
69 }
60 }
70
61
71 Behavior on anchors.rightMargin {
62 Behavior on anchors.rightMargin {
@@ -77,10 +68,8 @@ FocusScope {
77 }
68 }
78
69
79 onDraggingHorizontallyChanged: {
70 onDraggingHorizontallyChanged: {
71+ // See refocusInputAfterUserLetsGo()
80 if (draggingHorizontally) {
72 if (draggingHorizontally) {
81− // Remove (and put back using anchors.onRightMarginChanged) the
82− // focus for the searchfield in order to hide the copy/paste
83− // popover when we move the drawer
84 hadFocus = searchField.focus;
73 hadFocus = searchField.focus;
85 oldSelectionStart = searchField.selectionStart;
74 oldSelectionStart = searchField.selectionStart;
86 oldSelectionEnd = searchField.selectionEnd;
75 oldSelectionEnd = searchField.selectionEnd;
@@ -91,6 +80,7 @@ FocusScope {
91 } else {
80 } else {
92 openRequested();
81 openRequested();
93 }
82 }
83+ refocusInputAfterUserLetsGo();
94 }
84 }
95 }
85 }
96
86
@@ -106,9 +96,24 @@ FocusScope {
106 hadFocus = false;
96 hadFocus = false;
107 oldSelectionStart = null;
97 oldSelectionStart = null;
108 oldSelectionEnd = null;
98 oldSelectionEnd = null;
99+ }
100+
101+ function refocusInputAfterUserLetsGo() {
102+ if (!draggingHorizontally) {
103+ if (fullyOpen && hadFocus) {
104+ searchField.focus = hadFocus;
105+ searchField.select(oldSelectionStart, oldSelectionEnd);
106+ } else if (fullyOpen || fullyClosed) {
107+ resetOldFocus();
108+ }
109+
110+ if (fullyClosed) {
111+ searchField.text = "";
109− appList.currentIndex = 0;
112+ appList.currentIndex = 0;
110− searchField.focus = false;
113+ searchField.focus = false;
111− appList.focus = false;
114+ appList.focus = false;
115+ }
116+ }
112 }
117 }
113
118
114 function focusInput() {
119 function focusInput() {
@@ -202,6 +207,7 @@ FocusScope {
202 function reset() {
207 function reset() {
203 root.draggingHorizontally = false;
208 root.draggingHorizontally = false;
204 handle.active = false;
209 handle.active = false;
210+ root.dragDistance = 0;
205 }
211 }
206
212
207 Handle {
213 Handle {
qml/Launcher/Launcher.qml+15−4
@@ -1,5 +1,6 @@
1/*
1/*
2 * Copyright (C) 2013-2015 Canonical, Ltd.
2 * Copyright (C) 2013-2015 Canonical, Ltd.
3+ * Copyright (C) 2021 UBports Foundation
3 *
4 *
4 * This program is free software; you can redistribute it and/or modify
5 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * it under the terms of the GNU General Public License as published by
@@ -566,10 +567,12 @@ FocusScope {
566 name: "" // hidden state. Must be the default state ("") because "when:" falls back to this.
567 name: "" // hidden state. Must be the default state ("") because "when:" falls back to this.
567 PropertyChanges {
568 PropertyChanges {
568 target: panel
569 target: panel
570+ restoreEntryValues: false
569 x: -root.panelWidth
571 x: -root.panelWidth
570 }
572 }
571 PropertyChanges {
573 PropertyChanges {
572 target: drawer
574 target: drawer
575+ restoreEntryValues: false
573 anchors.rightMargin: 0
576 anchors.rightMargin: 0
574 focus: false
577 focus: false
575 }
578 }
@@ -578,32 +581,38 @@ FocusScope {
578 name: "visible"
581 name: "visible"
579 PropertyChanges {
582 PropertyChanges {
580 target: panel
583 target: panel
584+ restoreEntryValues: false
581 x: -root.x // so we never go past panelWidth, even when teased by tutorial
585 x: -root.x // so we never go past panelWidth, even when teased by tutorial
582 focus: true
586 focus: true
583 }
587 }
584 PropertyChanges {
588 PropertyChanges {
585 target: drawer
589 target: drawer
590+ restoreEntryValues: false
586 anchors.rightMargin: 0
591 anchors.rightMargin: 0
587 focus: false
592 focus: false
588 }
593 }
589 },
594 },
590 State {
595 State {
591 name: "drawer"
596 name: "drawer"
592− extend: "visible"
597+ PropertyChanges {
598+ target: panel
599+ restoreEntryValues: false
600+ x: -root.x // so we never go past panelWidth, even when teased by tutorial
601+ focus: false
602+ }
593 PropertyChanges {
603 PropertyChanges {
594 target: drawer
604 target: drawer
605+ restoreEntryValues: false
595 anchors.rightMargin: -drawer.width + root.x // so we never go past panelWidth, even when teased by tutorial
606 anchors.rightMargin: -drawer.width + root.x // so we never go past panelWidth, even when teased by tutorial
596 focus: true
607 focus: true
597 }
608 }
598− PropertyChanges {
599− target: panel
600− }
601 },
609 },
602 State {
610 State {
603 name: "visibleTemporary"
611 name: "visibleTemporary"
604 extend: "visible"
612 extend: "visible"
605 PropertyChanges {
613 PropertyChanges {
606 target: root
614 target: root
615+ restoreEntryValues: false
607 autohideEnabled: true
616 autohideEnabled: true
608 }
617 }
609 },
618 },
@@ -612,6 +621,7 @@ FocusScope {
612 when: teaseTimer.running && teaseTimer.mode == "teasing"
621 when: teaseTimer.running && teaseTimer.mode == "teasing"
613 PropertyChanges {
622 PropertyChanges {
614 target: panel
623 target: panel
624+ restoreEntryValues: false
615 x: -root.panelWidth + units.gu(2)
625 x: -root.panelWidth + units.gu(2)
616 }
626 }
617 },
627 },
@@ -620,6 +630,7 @@ FocusScope {
620 when: teaseTimer.running && teaseTimer.mode == "hinting"
630 when: teaseTimer.running && teaseTimer.mode == "hinting"
621 PropertyChanges {
631 PropertyChanges {
622 target: panel
632 target: panel
633+ restoreEntryValues: false
623 x: 0
634 x: 0
624 }
635 }
625 }
636 }
tests/qmltests/Launcher/tst_Drawer.qml+95
@@ -1,5 +1,6 @@
1/*
1/*
2 * Copyright 2013-2016 Canonical Ltd.
2 * Copyright 2013-2016 Canonical Ltd.
3+ * Copyright (C) 2021 UBports Foundation
3 *
4 *
4 * This program is free software; you can redistribute it and/or modify
5 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * it under the terms of the GNU General Public License as published by
@@ -421,5 +422,99 @@ StyledItem {
421
422
422 tryCompare(searchField, "focus", false);
423 tryCompare(searchField, "focus", false);
423 }
424 }
425+
426+ function test_dragDistanceReset() {
427+ // Regression test: If the user dragged the Drawer open further than
428+ // necessary, dragging the Drawer closed would take an equal amount
429+ // of distance to start the animation as the original overshoot.
430+
431+ var drawer = dragDrawerIntoView();
432+ var dragY = drawer.height / 2;
433+ // Drag the Drawer most of the way closed, then *way* open
434+ mousePress(root, drawer.width - units.gu(1), dragY);
435+ mouseMove(root, 10, dragY, 500);
436+ mouseMove(root, units.gu(100), dragY, 500);
437+ mouseRelease(root, units.gu(100), dragY);
438+
439+ // Ensure the Drawer's margin changes correctly on the next drag closed
440+ mousePress(root, drawer.width - units.gu(1), dragY);
441+ mouseMove(root, 1, dragY);
442+ // If the Drawer's position is within 2gu of hidden, it's probably okay.
443+ tryVerify(function () {return drawer.x < -(drawer.width - units.gu(2))});
444+ // But it should not be completely hidden.
445+ tryCompare(drawer, "visible", true);
446+ mouseRelease(root, 1, dragY);
447+ }
448+
449+ function test_userCancellingExitWithSearch_data() {
450+ return [
451+ {tag: "drag all the way back to fullyOpen", fullyOpen: true, cursorPosition: 3, selectedText: ""},
452+ {tag: "do not drag all the way back to fullyOpen", fullyOpen: false, cursorPosition: 3, selectedText: ""},
453+ {tag: "move cursor and drag all the way back to fullyOpen", fullyOpen: true, cursorPosition: 2, selectionStart: 2, selectionEnd: 2, selectedText: ""},
454+ {tag: "move cursor and do not drag all the way back to fullyOpen", fullyOpen: false, cursorPosition: 2, selectionStart: 2, selectionEnd: 2, selectedText: ""},
455+ {tag: "select text and drag all the way back to fullyOpen", fullyOpen: true, cursorPosition: 2, selectionStart: 0, selectionEnd: 2, selectedText: "ca"},
456+ {tag: "select text and do not drag all the way back to fullyOpen", fullyOpen: false, cursorPosition: 2, selectionStart: 0, selectionEnd: 2, selectedText: "ca"}
457+ ]
458+ }
459+
460+ function test_userCancellingExitWithSearch(data) {
461+ // * If I open the Drawer, start searching, start closing the
462+ // drawer, then change my mind and cancel my closing gesture, my
463+ // search remains.
464+ // * If I start searching, place my cursor at a different point in
465+ // the text field or select text, then start closing the Drawer:
466+ // - The keyboard text selection handles and copy/paste dialog
467+ // become invisible or move with the text box. Our keyboard
468+ // isn't quite fancy enough for the handles to move with the
469+ // search box, so we remove the search field's focus to remove
470+ // the handles.
471+ // - If I cancel my Drawer close action, my search, cursor
472+ // position, and selection rectangles are retained.
473+ var drawer = dragDrawerIntoView();
474+ var searchField = drawer.searchTextField;
475+ var dragY = drawer.height / 2;
476+ if (data.fullyOpen) {
477+ var dragEndPointX = drawer.width + units.gu(20);
478+ } else {
479+ var dragEndPointX = drawer.width - units.gu(10);
480+ }
481+
482+ typeString("cam");
483+ tryCompare(searchField, "displayText", "cam");
484+ tryCompare(searchField, "focus", true);
485+
486+ if (data.selectionStart !== undefined) {
487+ searchField.select(data.selectionStart, data.selectionEnd);
488+ }
489+
490+ // Simply clicking the drag handle shouldn't be enough to remove focus
491+ mousePress(root, drawer.width - units.gu(1), dragY);
492+ tryCompare(searchField, "focus", true);
493+
494+ // The search field should not have focus during the drag so the
495+ // cursor or drag handles don't float in place.
496+ mouseMove(root, 10, dragY, 500);
497+ tryCompare(drawer, "fullyOpen", false);
498+ tryCompare(searchField, "focus", false);
499+ mouseMove(root, dragEndPointX, dragY, 500);
500+ tryCompare(drawer, "fullyOpen", data.fullyOpen);
501+
502+ // The search field should not get focus back until the user lets go
503+ tryCompare(searchField, "focus", false);
504+ mouseRelease(root, dragEndPointX, dragY);
505+ // Wait for animations to stop so anything that results from them
506+ // can resolve
507+ wait(1000);
508+ tryCompare(drawer, "fullyOpen", true);
509+ tryCompare(searchField, "displayText", "cam");
510+ tryCompare(searchField, "focus", true);
511+ tryCompare(searchField, "cursorPosition", data.cursorPosition);
512+ tryCompare(searchField, "selectedText", data.selectedText);
513+ if (data.selectionStart) {
514+ // The selected area should be the same as it was before the drag
515+ tryCompare(searchField, "selectionStart", data.selectionStart);
516+ tryCompare(searchField, "selectionEnd", data.selectionEnd);
517+ }
518+ }
424 }
519 }
425}
520}