added key navigation for palette panel
This commit is contained in:
parent
b732a3f81a
commit
1b73eed0ae
24 changed files with 493 additions and 665 deletions
|
@ -158,7 +158,11 @@ DockPage {
|
|||
|
||||
Component {
|
||||
id: keynavComp
|
||||
KeyNavExample{}
|
||||
KeyNavExample {
|
||||
onActiveFocusRequested: {
|
||||
devtoolsCentral.forceActiveFocus()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -10,6 +10,8 @@ Rectangle {
|
|||
|
||||
property string lastClickedInfo: ""
|
||||
|
||||
signal activeFocusRequested()
|
||||
|
||||
Rectangle {
|
||||
id: infoPanel
|
||||
|
||||
|
@ -36,6 +38,13 @@ Rectangle {
|
|||
sectionName: "mainMenu"
|
||||
sectionOrder: 101
|
||||
|
||||
onActiveChanged: {
|
||||
if (active) {
|
||||
root.activeFocusRequested()
|
||||
root.forceActiveFocus()
|
||||
}
|
||||
}
|
||||
|
||||
RowLayout {
|
||||
anchors.fill: parent
|
||||
spacing: 8
|
||||
|
|
|
@ -9,6 +9,7 @@ Rectangle {
|
|||
property alias keynavSection: keynavsec
|
||||
property alias sectionName: keynavsec.name
|
||||
property alias sectionOrder: keynavsec.order
|
||||
property alias active: keynavsec.active
|
||||
|
||||
default property alias content: contentItem.data
|
||||
|
||||
|
@ -20,14 +21,8 @@ Rectangle {
|
|||
onActiveChanged: {
|
||||
console.debug("KeyNavSection.qml active: " + keynavsec.active)
|
||||
if (keynavsec.active) {
|
||||
root.forceActiveFocus()
|
||||
|
||||
var p = root.parent
|
||||
while (p) {
|
||||
p.focus = true
|
||||
p = p.parent
|
||||
}
|
||||
|
||||
root.focus = true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -133,12 +133,20 @@ DockPage {
|
|||
}
|
||||
|
||||
property KeyNavigationControl keynavTab: KeyNavigationControl {
|
||||
name: "PanelTab"
|
||||
name: "PaletteTab"
|
||||
order: 1 //! TODO Needs order from DockPanel
|
||||
subsection: notationPage.keynavPanelTabSubSec(palettePanel.area)
|
||||
onActiveChanged: {
|
||||
if (active) {
|
||||
palettePanel.forceActiveFocus()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
PalettesWidget {}
|
||||
PalettesWidget {
|
||||
anchors.fill: parent
|
||||
keynavSection: notationPage.keynavPanelSec(palettePanel.area)
|
||||
}
|
||||
},
|
||||
|
||||
DockPanel {
|
||||
|
@ -167,11 +175,17 @@ DockPage {
|
|||
name: "InstrumentsTab"
|
||||
order: 2 //! TODO Needs order from DockPanel
|
||||
subsection: notationPage.keynavPanelTabSubSec(instrumentsPanel.area)
|
||||
onActiveChanged: {
|
||||
if (active) {
|
||||
instrumentsPanel.forceActiveFocus()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
InstrumentsPanel {
|
||||
anchors.fill: parent
|
||||
keynavSection: notationPage.keynavPanelSec(instrumentsPanel.area)
|
||||
visible: instrumentsPanel.isShown
|
||||
}
|
||||
},
|
||||
|
||||
|
@ -201,6 +215,11 @@ DockPage {
|
|||
name: "InspectorTab"
|
||||
order: 3 //! TODO Needs order from DockPanel
|
||||
subsection: notationPage.keynavPanelTabSubSec(inspectorPanel.area)
|
||||
onActiveChanged: {
|
||||
if (active) {
|
||||
inspectorPanel.forceActiveFocus()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
InspectorForm {
|
||||
|
|
|
@ -81,6 +81,7 @@ DockWindow {
|
|||
|
||||
toolbars: [
|
||||
DockToolBar {
|
||||
id: mainToolBar
|
||||
objectName: "mainToolBar"
|
||||
minimumWidth: 296
|
||||
minimumHeight: dockWindow.toolbarHeight
|
||||
|
@ -96,6 +97,12 @@ DockWindow {
|
|||
keynav.order: 1
|
||||
currentUri: dockWindow.currentPageUri
|
||||
|
||||
keynav.onActiveChanged: {
|
||||
if (keynav.active) {
|
||||
mainToolBar.forceActiveFocus()
|
||||
}
|
||||
}
|
||||
|
||||
onSelected: {
|
||||
api.launcher.open(uri)
|
||||
}
|
||||
|
@ -120,6 +127,11 @@ DockWindow {
|
|||
keynav.section: topToolKeyNavSec
|
||||
keynav.order: 2
|
||||
keynav.enabled: notationToolBar.visible
|
||||
onActiveFocusRequested: {
|
||||
if (keynav.active) {
|
||||
notationToolBar.forceActiveFocus()
|
||||
}
|
||||
}
|
||||
|
||||
Connections {
|
||||
target: notationToolBar
|
||||
|
|
|
@ -37,9 +37,17 @@ DockPanel::DockPanel(QQuickItem* parent)
|
|||
connect(this, &DockPanel::visibleEdited, this, [this](bool visible) {
|
||||
if (m_dock.panel->isVisible() != visible) {
|
||||
m_dock.panel->setVisible(visible);
|
||||
if (!visible) {
|
||||
m_isShown = false;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
connect(m_dock.panel, &QDockWidget::visibilityChanged, this, [this](bool vsbl) {
|
||||
m_isShown = vsbl;
|
||||
emit isShownChanged(vsbl);
|
||||
});
|
||||
|
||||
m_eventsWatcher = new EventsWatcher(this);
|
||||
m_dock.panel->installEventFilter(m_eventsWatcher);
|
||||
connect(m_eventsWatcher, &EventsWatcher::eventReceived, this, &DockPanel::onWidgetEvent);
|
||||
|
@ -58,6 +66,7 @@ void DockPanel::onComponentCompleted()
|
|||
updateStyle();
|
||||
|
||||
m_preferedWidth = width();
|
||||
m_isShown = panel()->isVisible();
|
||||
|
||||
if (minimumWidth() == 0) {
|
||||
panel()->setMinimumWidth(width());
|
||||
|
@ -161,6 +170,11 @@ bool DockPanel::closable() const
|
|||
return featureEnabled(QDockWidget::DockWidgetClosable);
|
||||
}
|
||||
|
||||
bool DockPanel::isShown() const
|
||||
{
|
||||
return m_isShown;
|
||||
}
|
||||
|
||||
bool DockPanel::visible() const
|
||||
{
|
||||
return m_dock.panel ? m_dock.panel->isVisible() : false;
|
||||
|
|
|
@ -36,6 +36,8 @@ class DockPanel : public DockView
|
|||
Q_PROPERTY(bool floatable READ floatable WRITE setFloatable NOTIFY floatableChanged)
|
||||
Q_PROPERTY(bool closable READ closable WRITE setClosable NOTIFY closableChanged)
|
||||
|
||||
Q_PROPERTY(bool isShown READ isShown NOTIFY isShownChanged)
|
||||
|
||||
public:
|
||||
explicit DockPanel(QQuickItem* parent = nullptr);
|
||||
~DockPanel() override;
|
||||
|
@ -49,6 +51,7 @@ public:
|
|||
|
||||
bool floatable() const;
|
||||
bool closable() const;
|
||||
bool isShown() const;
|
||||
bool visible() const override;
|
||||
|
||||
struct Widget {
|
||||
|
@ -78,6 +81,7 @@ signals:
|
|||
void floatableChanged(bool floatable);
|
||||
void closableChanged(bool closable);
|
||||
void closed();
|
||||
void isShownChanged(bool isShown);
|
||||
|
||||
protected:
|
||||
void onComponentCompleted() override;
|
||||
|
@ -94,6 +98,7 @@ private:
|
|||
EventsWatcher* m_eventsWatcher = nullptr;
|
||||
|
||||
int m_preferedWidth = 0;
|
||||
bool m_isShown = false;
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
@ -42,13 +42,29 @@
|
|||
<seq>Down</seq>
|
||||
</SC>
|
||||
<SC>
|
||||
<key>nav-trigger</key>
|
||||
<key>nav-trigger-control</key>
|
||||
<seq>Return</seq>
|
||||
</SC>
|
||||
<SC>
|
||||
<key>nav-trigger</key>
|
||||
<key>nav-trigger-control</key>
|
||||
<seq>Space</seq>
|
||||
</SC>
|
||||
<SC>
|
||||
<key>nav-first-control</key>
|
||||
<seq>Home</seq>
|
||||
</SC>
|
||||
<SC>
|
||||
<key>nav-last-control</key>
|
||||
<seq>End</seq>
|
||||
</SC>
|
||||
<SC>
|
||||
<key>nav-nextrow-control</key>
|
||||
<seq>PgDown</seq>
|
||||
</SC>
|
||||
<SC>
|
||||
<key>nav-prevrow-control</key>
|
||||
<seq>PgUp</seq>
|
||||
</SC>
|
||||
<!-- end Keyboard navigation -->
|
||||
|
||||
<SC>
|
||||
|
|
|
@ -42,13 +42,30 @@
|
|||
<seq>Down</seq>
|
||||
</SC>
|
||||
<SC>
|
||||
<key>nav-trigger</key>
|
||||
<key>nav-trigger-control</key>
|
||||
<seq>Return</seq>
|
||||
</SC>
|
||||
<SC>
|
||||
<key>nav-trigger</key>
|
||||
<key>nav-trigger-control</key>
|
||||
<seq>Space</seq>
|
||||
</SC>
|
||||
<SC>
|
||||
<key>nav-first-control</key>
|
||||
<seq>Home</seq>
|
||||
</SC>
|
||||
<SC>
|
||||
<key>nav-last-control</key>
|
||||
<seq>End</seq>
|
||||
</SC>
|
||||
<SC>
|
||||
<key>nav-nextrow-control</key>
|
||||
<seq>PgDown</seq>
|
||||
</SC>
|
||||
<SC>
|
||||
<key>nav-prevrow-control</key>
|
||||
<seq>PgUp</seq>
|
||||
</SC>
|
||||
|
||||
<!-- end Keyboard navigation -->
|
||||
|
||||
<SC>
|
||||
|
|
|
@ -28,60 +28,6 @@ using namespace mu::ui;
|
|||
using MoveDirection = KeyNavigationController::MoveDirection;
|
||||
|
||||
// algorithms
|
||||
template<class T>
|
||||
static IKeyNavigation::Index determinateExtremeIndex(const QSet<T*>& set, MoveDirection direction)
|
||||
{
|
||||
IKeyNavigation::Index index;
|
||||
switch (direction) {
|
||||
case MoveDirection::Right: {
|
||||
index.column = -1;
|
||||
|
||||
// find min row
|
||||
index.row = std::numeric_limits<int>::max();
|
||||
for (T* v : set) {
|
||||
if (v->index().row < index.row) {
|
||||
index.row = v->index().row;
|
||||
}
|
||||
}
|
||||
} break;
|
||||
case MoveDirection::Left: {
|
||||
index.column = std::numeric_limits<int>::max();
|
||||
|
||||
// find max row
|
||||
index.row = -1;
|
||||
for (T* v : set) {
|
||||
if (v->index().row > index.row) {
|
||||
index.row = v->index().row;
|
||||
}
|
||||
}
|
||||
} break;
|
||||
case MoveDirection::Down: {
|
||||
index.row = -1;
|
||||
|
||||
// find min column
|
||||
index.column = std::numeric_limits<int>::max();
|
||||
for (T* v : set) {
|
||||
if (v->index().column < index.column) {
|
||||
index.column = v->index().column;
|
||||
}
|
||||
}
|
||||
} break;
|
||||
case MoveDirection::Up: {
|
||||
index.row = std::numeric_limits<int>::max();
|
||||
|
||||
// find max row
|
||||
index.column = -1;
|
||||
for (T* v : set) {
|
||||
if (v->index().column > index.column) {
|
||||
index.column = v->index().row;
|
||||
}
|
||||
}
|
||||
} break;
|
||||
}
|
||||
|
||||
return index;
|
||||
}
|
||||
|
||||
template<class T>
|
||||
static T* findNearestEnabled(const QSet<T*>& set, const IKeyNavigation::Index& currentIndex, MoveDirection direction)
|
||||
{
|
||||
|
@ -92,6 +38,32 @@ static T* findNearestEnabled(const QSet<T*>& set, const IKeyNavigation::Index& c
|
|||
}
|
||||
|
||||
switch (direction) {
|
||||
case MoveDirection::First: {
|
||||
if (!ret) {
|
||||
ret = v;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (v->index().row <= ret->index().row) {
|
||||
if (v->index().column <= ret->index().column) {
|
||||
ret = v;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
} break;
|
||||
case MoveDirection::Last: {
|
||||
if (!ret) {
|
||||
ret = v;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (v->index().row >= ret->index().row) {
|
||||
if (v->index().column >= ret->index().column) {
|
||||
ret = v;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
} break;
|
||||
case MoveDirection::Right: {
|
||||
if (v->index().row != currentIndex.row) {
|
||||
continue;
|
||||
|
@ -163,9 +135,7 @@ static T* findNearestEnabled(const QSet<T*>& set, const IKeyNavigation::Index& c
|
|||
}
|
||||
|
||||
template<class T>
|
||||
static T* firstEnabled(const QSet<T*>& set,
|
||||
const IKeyNavigation::Index& currentIndex = IKeyNavigation::Index(),
|
||||
MoveDirection direction = MoveDirection::Right)
|
||||
static T* firstEnabled(const QSet<T*>& set, const IKeyNavigation::Index& currentIndex, MoveDirection direction)
|
||||
{
|
||||
if (set.empty()) {
|
||||
return nullptr;
|
||||
|
@ -175,31 +145,41 @@ static T* firstEnabled(const QSet<T*>& set,
|
|||
return nullptr;
|
||||
}
|
||||
|
||||
if (currentIndex.column < 0 && currentIndex.row < 0) {
|
||||
return findNearestEnabled<T>(set, determinateExtremeIndex(set, direction), direction);
|
||||
return findNearestEnabled<T>(set, currentIndex, direction);
|
||||
}
|
||||
|
||||
template<class T>
|
||||
static T* firstEnabled(const QSet<T*>& set)
|
||||
{
|
||||
if (set.empty()) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return findNearestEnabled<T>(set, IKeyNavigation::Index(), MoveDirection::First);
|
||||
}
|
||||
|
||||
template<class T>
|
||||
static T* lastEnabled(const QSet<T*>& set, const IKeyNavigation::Index& currentIndex, MoveDirection direction)
|
||||
{
|
||||
if (set.empty()) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
IF_ASSERT_FAILED(direction == MoveDirection::Left || (direction == MoveDirection::Up)) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return findNearestEnabled<T>(set, currentIndex, direction);
|
||||
}
|
||||
|
||||
template<class T>
|
||||
static T* lastEnabled(const QSet<T*>& set,
|
||||
const IKeyNavigation::Index& currentIndex = IKeyNavigation::Index(),
|
||||
MoveDirection direction = MoveDirection::Left)
|
||||
static T* lastEnabled(const QSet<T*>& set)
|
||||
{
|
||||
if (set.empty()) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
IF_ASSERT_FAILED(direction == MoveDirection::Left || direction == MoveDirection::Up) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (currentIndex.column < 0 && currentIndex.row < 0) {
|
||||
return findNearestEnabled<T>(set, determinateExtremeIndex(set, direction), direction);
|
||||
}
|
||||
|
||||
return findNearestEnabled<T>(set, currentIndex, direction);
|
||||
return findNearestEnabled<T>(set, IKeyNavigation::Index(), MoveDirection::Last);
|
||||
}
|
||||
|
||||
template<class T>
|
||||
|
@ -250,11 +230,18 @@ void KeyNavigationController::init()
|
|||
dispatcher()->reg(this, "nav-prev-section", this, &KeyNavigationController::goToPrevSection);
|
||||
dispatcher()->reg(this, "nav-next-subsection", this, &KeyNavigationController::goToNextSubSection);
|
||||
dispatcher()->reg(this, "nav-prev-subsection", this, &KeyNavigationController::goToPrevSubSection);
|
||||
|
||||
dispatcher()->reg(this, "nav-trigger-control", this, &KeyNavigationController::doTriggerControl);
|
||||
|
||||
dispatcher()->reg(this, "nav-right", this, &KeyNavigationController::onRight);
|
||||
dispatcher()->reg(this, "nav-left", this, &KeyNavigationController::onLeft);
|
||||
dispatcher()->reg(this, "nav-up", this, &KeyNavigationController::onUp);
|
||||
dispatcher()->reg(this, "nav-down", this, &KeyNavigationController::onDown);
|
||||
dispatcher()->reg(this, "nav-trigger", this, &KeyNavigationController::doTriggerControl);
|
||||
|
||||
dispatcher()->reg(this, "nav-first-control", this, &KeyNavigationController::goToFirstControl); // typically Home key
|
||||
dispatcher()->reg(this, "nav-last-control", this, &KeyNavigationController::goToLastControl); // typically End key
|
||||
dispatcher()->reg(this, "nav-nextrow-control", this, &KeyNavigationController::goToNextRowControl); // typically PageDown key
|
||||
dispatcher()->reg(this, "nav-prevrow-control", this, &KeyNavigationController::goToPrevRowControl); // typically PageUp key
|
||||
}
|
||||
|
||||
void KeyNavigationController::reg(IKeyNavigationSection* s)
|
||||
|
@ -594,6 +581,80 @@ void KeyNavigationController::onUp()
|
|||
}
|
||||
}
|
||||
|
||||
void KeyNavigationController::goToFirstControl()
|
||||
{
|
||||
LOGI() << "====";
|
||||
goToControl(MoveDirection::First);
|
||||
}
|
||||
|
||||
void KeyNavigationController::goToLastControl()
|
||||
{
|
||||
LOGI() << "====";
|
||||
goToControl(MoveDirection::Last);
|
||||
}
|
||||
|
||||
void KeyNavigationController::goToNextRowControl()
|
||||
{
|
||||
LOGI() << "====";
|
||||
|
||||
IKeyNavigationSubSection* activeSubSec = activeSubSection();
|
||||
if (!activeSubSec) {
|
||||
return;
|
||||
}
|
||||
|
||||
IKeyNavigationControl* activeControl = findActive(activeSubSec->controls());
|
||||
if (activeControl) {
|
||||
doDeactivateControl(activeControl);
|
||||
}
|
||||
|
||||
IKeyNavigationControl* toControl = nullptr;
|
||||
if (activeControl) {
|
||||
IKeyNavigation::Index index = activeControl->index();
|
||||
index.column = 0;
|
||||
toControl = nextEnabled(activeSubSec->controls(), index, MoveDirection::Down);
|
||||
if (!toControl) { // last row
|
||||
toControl = firstEnabled(activeSubSec->controls());
|
||||
}
|
||||
} else {
|
||||
toControl = firstEnabled(activeSubSec->controls());
|
||||
}
|
||||
|
||||
if (toControl) {
|
||||
doActivateControl(toControl);
|
||||
}
|
||||
}
|
||||
|
||||
void KeyNavigationController::goToPrevRowControl()
|
||||
{
|
||||
LOGI() << "====";
|
||||
|
||||
IKeyNavigationSubSection* activeSubSec = activeSubSection();
|
||||
if (!activeSubSec) {
|
||||
return;
|
||||
}
|
||||
|
||||
IKeyNavigationControl* activeControl = findActive(activeSubSec->controls());
|
||||
if (activeControl) {
|
||||
doDeactivateControl(activeControl);
|
||||
}
|
||||
|
||||
IKeyNavigationControl* toControl = nullptr;
|
||||
if (activeControl) {
|
||||
IKeyNavigation::Index index = activeControl->index();
|
||||
index.column = 0;
|
||||
toControl = prevEnabled(activeSubSec->controls(), index, MoveDirection::Up);
|
||||
if (!toControl) { // first row
|
||||
toControl = lastEnabled(activeSubSec->controls());
|
||||
}
|
||||
} else {
|
||||
toControl = lastEnabled(activeSubSec->controls());
|
||||
}
|
||||
|
||||
if (toControl) {
|
||||
doActivateControl(toControl);
|
||||
}
|
||||
}
|
||||
|
||||
void KeyNavigationController::goToControl(MoveDirection direction, IKeyNavigationSubSection* activeSubSec)
|
||||
{
|
||||
LOGI() << "direction: " << direction;
|
||||
|
@ -614,6 +675,12 @@ void KeyNavigationController::goToControl(MoveDirection direction, IKeyNavigatio
|
|||
IKeyNavigationControl* toControl = nullptr;
|
||||
|
||||
switch (direction) {
|
||||
case MoveDirection::First: {
|
||||
toControl = firstEnabled(activeSubSec->controls());
|
||||
} break;
|
||||
case MoveDirection::Last: {
|
||||
toControl = lastEnabled(activeSubSec->controls());
|
||||
} break;
|
||||
case MoveDirection::Right: {
|
||||
if (!activeControl) { // no any active
|
||||
toControl = firstEnabled(activeSubSec->controls(), IKeyNavigation::Index(), direction);
|
||||
|
|
|
@ -35,7 +35,9 @@ public:
|
|||
KeyNavigationController() = default;
|
||||
|
||||
enum MoveDirection {
|
||||
Right = 0,
|
||||
First = 0,
|
||||
Last,
|
||||
Right,
|
||||
Left,
|
||||
Up,
|
||||
Down
|
||||
|
@ -52,7 +54,12 @@ private:
|
|||
void goToNextSubSection();
|
||||
void goToPrevSubSection();
|
||||
|
||||
void goToControl(MoveDirection direction, IKeyNavigationSubSection* activeSubSec);
|
||||
void goToFirstControl();
|
||||
void goToLastControl();
|
||||
void goToNextRowControl();
|
||||
void goToPrevRowControl();
|
||||
|
||||
void goToControl(MoveDirection direction, IKeyNavigationSubSection* activeSubSec = nullptr);
|
||||
|
||||
void onLeft();
|
||||
void onRight();
|
||||
|
|
|
@ -48,7 +48,19 @@ const UiActionList KeyNavigationUiActions::m_actions = {
|
|||
UiAction("nav-down",
|
||||
mu::context::UiCtxAny
|
||||
),
|
||||
UiAction("nav-trigger",
|
||||
UiAction("nav-trigger-control",
|
||||
mu::context::UiCtxAny
|
||||
),
|
||||
UiAction("nav-first-control",
|
||||
mu::context::UiCtxAny
|
||||
),
|
||||
UiAction("nav-last-control",
|
||||
mu::context::UiCtxAny
|
||||
),
|
||||
UiAction("nav-nextrow-control",
|
||||
mu::context::UiCtxAny
|
||||
),
|
||||
UiAction("nav-prevrow-control",
|
||||
mu::context::UiCtxAny
|
||||
)
|
||||
};
|
||||
|
|
|
@ -33,17 +33,8 @@ FocusableControl {
|
|||
|
||||
opacity: root.enabled ? 1.0 : ui.theme.itemOpacityDisabled
|
||||
|
||||
onInternalClicked: {
|
||||
root.clicked()
|
||||
}
|
||||
|
||||
onInternalPressAndHold: {
|
||||
root.pressAndHold()
|
||||
}
|
||||
|
||||
onInternalTriggered: {
|
||||
root.clicked()
|
||||
}
|
||||
mouseArea.onClicked: root.clicked()
|
||||
mouseArea.onPressAndHold: root.pressAndHold()
|
||||
|
||||
mouseArea.hoverEnabled: true
|
||||
mouseArea.onContainsMouseChanged: {
|
||||
|
@ -58,6 +49,8 @@ FocusableControl {
|
|||
}
|
||||
}
|
||||
|
||||
keynav.onTriggered: root.clicked()
|
||||
|
||||
background.color: normalStateColor
|
||||
background.opacity: ui.theme.buttonOpacityNormal
|
||||
background.radius: 3
|
||||
|
|
|
@ -12,13 +12,6 @@ FocusScope {
|
|||
|
||||
property alias keynav: keynavItem
|
||||
|
||||
signal internalPressed()
|
||||
signal internalReleased()
|
||||
signal internalClicked()
|
||||
signal internalPressAndHold()
|
||||
|
||||
signal internalTriggered()
|
||||
|
||||
function insureActiveFocus() {
|
||||
if (!root.activeFocus) {
|
||||
root.forceActiveFocus()
|
||||
|
@ -39,10 +32,6 @@ FocusScope {
|
|||
root.insureActiveFocus()
|
||||
}
|
||||
}
|
||||
|
||||
onTriggered: {
|
||||
root.internalTriggered()
|
||||
}
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
|
@ -64,19 +53,6 @@ FocusScope {
|
|||
|
||||
onClicked: {
|
||||
root.insureActiveFocus()
|
||||
root.internalClicked()
|
||||
}
|
||||
|
||||
onPressAndHold: {
|
||||
root.internalPressAndHold()
|
||||
}
|
||||
|
||||
onPressed: {
|
||||
root.internalPressed()
|
||||
}
|
||||
|
||||
onReleased: {
|
||||
root.internalReleased()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,13 +2,13 @@ import QtQuick 2.15
|
|||
|
||||
import MuseScore.Ui 1.0
|
||||
|
||||
Item {
|
||||
FocusableControl {
|
||||
id: root
|
||||
|
||||
property string hint
|
||||
|
||||
property bool isSelected: false
|
||||
property alias radius: background.radius
|
||||
property alias radius: root.background.radius
|
||||
|
||||
property color normalStateColor: "transparent"
|
||||
property color hoveredStateColor: privateProperties.defaultColor
|
||||
|
@ -24,20 +24,31 @@ Item {
|
|||
Accessible.selectable: true
|
||||
Accessible.selected: isSelected
|
||||
|
||||
mouseArea.hoverEnabled: root.visible
|
||||
mouseArea.onHoveredChanged: root.hovered(mouseArea.containsMouse, mouseArea.mouseX, mouseArea.mouseY)
|
||||
|
||||
mouseArea.onClicked: root.clicked()
|
||||
mouseArea.onDoubleClicked: root.doubleClicked()
|
||||
|
||||
mouseArea.onContainsMouseChanged: {
|
||||
if (!Boolean(root.hint)) {
|
||||
return
|
||||
}
|
||||
|
||||
if (mouseArea.containsMouse) {
|
||||
ui.tooltip.show(this, root.hint)
|
||||
} else {
|
||||
ui.tooltip.hide(this)
|
||||
}
|
||||
}
|
||||
|
||||
QtObject {
|
||||
id: privateProperties
|
||||
|
||||
property color defaultColor: ui.theme.buttonColor
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
id: background
|
||||
|
||||
anchors.fill: parent
|
||||
|
||||
color: normalStateColor
|
||||
opacity: root.enabled ? 1 : ui.theme.itemOpacityDisabled
|
||||
}
|
||||
background.opacity: root.enabled ? 1 : ui.theme.itemOpacityDisabled
|
||||
|
||||
states: [
|
||||
State {
|
||||
|
@ -45,7 +56,7 @@ Item {
|
|||
when: mouseArea.containsMouse && !mouseArea.pressed && !root.isSelected
|
||||
|
||||
PropertyChanges {
|
||||
target: background
|
||||
target: root.background
|
||||
opacity: ui.theme.buttonOpacityHover
|
||||
color: hoveredStateColor
|
||||
}
|
||||
|
@ -56,7 +67,7 @@ Item {
|
|||
when: mouseArea.pressed && !root.isSelected
|
||||
|
||||
PropertyChanges {
|
||||
target: background
|
||||
target: root.background
|
||||
opacity: ui.theme.buttonOpacityHit
|
||||
color: pressedStateColor
|
||||
}
|
||||
|
@ -67,42 +78,10 @@ Item {
|
|||
when: root.isSelected
|
||||
|
||||
PropertyChanges {
|
||||
target: background
|
||||
target: root.background
|
||||
opacity: ui.theme.accentOpacityHit
|
||||
color: ui.theme.accentColor
|
||||
}
|
||||
}
|
||||
]
|
||||
|
||||
MouseArea {
|
||||
id: mouseArea
|
||||
|
||||
anchors.fill: parent
|
||||
|
||||
hoverEnabled: root.visible
|
||||
|
||||
onHoveredChanged: {
|
||||
root.hovered(mouseArea.containsMouse, mouseX, mouseY)
|
||||
}
|
||||
|
||||
onClicked: {
|
||||
root.clicked()
|
||||
}
|
||||
|
||||
onDoubleClicked: {
|
||||
root.doubleClicked()
|
||||
}
|
||||
|
||||
onContainsMouseChanged: {
|
||||
if (!Boolean(root.hint)) {
|
||||
return
|
||||
}
|
||||
|
||||
if (containsMouse) {
|
||||
ui.tooltip.show(this, root.hint)
|
||||
} else {
|
||||
ui.tooltip.hide(this)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -12,6 +12,7 @@ import MuseScore.Instruments 1.0
|
|||
import "internal"
|
||||
|
||||
Item {
|
||||
|
||||
id: root
|
||||
|
||||
property KeyNavigationSection keynavSection: null
|
||||
|
@ -37,6 +38,7 @@ Item {
|
|||
name: "InstrumentsTree"
|
||||
section: root.keynavSection
|
||||
direction: KeyNavigationSubSection.Both
|
||||
enabled: root.visible
|
||||
order: 3
|
||||
}
|
||||
|
||||
|
@ -57,6 +59,7 @@ Item {
|
|||
Layout.rightMargin: contentColumn.sideMargin
|
||||
|
||||
keynav.section: root.keynavSection
|
||||
keynav.enabled: root.visible
|
||||
keynav.order: 2
|
||||
|
||||
isMovingUpAvailable: instrumentTreeModel.isMovingUpAvailable
|
||||
|
|
|
@ -31,7 +31,7 @@ RowLayout {
|
|||
|
||||
KeyNavigationSubSection {
|
||||
id: keynavSub
|
||||
name: "InstrumentsControlPanel"
|
||||
name: "InstrumentsHeader"
|
||||
}
|
||||
|
||||
FlatButton {
|
||||
|
|
|
@ -262,7 +262,6 @@ bool InstrumentPanelTreeModel::moveRows(const QModelIndex& sourceParent, int sou
|
|||
|
||||
bool InstrumentPanelTreeModel::isSelected(const QModelIndex& rowIndex) const
|
||||
{
|
||||
TRACEFUNC;
|
||||
if (m_selectionModel->selectedIndexes().isEmpty()) {
|
||||
return false;
|
||||
}
|
||||
|
@ -346,7 +345,6 @@ int InstrumentPanelTreeModel::columnCount(const QModelIndex&) const
|
|||
|
||||
QVariant InstrumentPanelTreeModel::data(const QModelIndex& index, int role) const
|
||||
{
|
||||
TRACEFUNC;
|
||||
if (!index.isValid() && role != ItemRole) {
|
||||
return QVariant();
|
||||
}
|
||||
|
|
|
@ -8,6 +8,8 @@ Rectangle {
|
|||
|
||||
property alias keynav: keynavSub
|
||||
|
||||
signal activeFocusRequested()
|
||||
|
||||
Component.onCompleted: {
|
||||
toolbarModel.load()
|
||||
}
|
||||
|
@ -15,6 +17,12 @@ Rectangle {
|
|||
KeyNavigationSubSection {
|
||||
id: keynavSub
|
||||
name: "NotationToolBar"
|
||||
onActiveChanged: {
|
||||
if (active) {
|
||||
root.activeFocusRequested()
|
||||
root.forceActiveFocus()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
NotationToolBarModel {
|
||||
|
|
|
@ -17,23 +17,22 @@
|
|||
// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
//=============================================================================
|
||||
|
||||
import QtQuick 2.8
|
||||
import QtQuick 2.15
|
||||
import QtQuick.Controls 2.1
|
||||
import QtQuick.Window 2.2
|
||||
import MuseScore.Ui 1.0
|
||||
import MuseScore.UiComponents 1.0
|
||||
import MuseScore.Palette 1.0
|
||||
|
||||
// TODO: make some properties 'property alias`?
|
||||
// and `readonly property`?
|
||||
|
||||
import "internal"
|
||||
|
||||
Rectangle {
|
||||
|
||||
id: palettesWidget
|
||||
|
||||
readonly property PaletteWorkspace paletteWorkspace: paletteRootModel.paletteWorkspace
|
||||
property KeyNavigationSection keynavSection: null
|
||||
|
||||
readonly property bool hasFocus: Window.activeFocusItem
|
||||
readonly property PaletteWorkspace paletteWorkspace: paletteRootModel.paletteWorkspace
|
||||
|
||||
implicitHeight: 4 * palettesWidgetHeader.implicitHeight
|
||||
implicitWidth: paletteTree.implicitWidth
|
||||
|
@ -46,14 +45,6 @@ Rectangle {
|
|||
|
||||
color: ui.theme.backgroundPrimaryColor
|
||||
|
||||
FocusableItem {
|
||||
id: focusBreaker
|
||||
|
||||
onActiveFocusChanged: {
|
||||
parent.focus = false
|
||||
}
|
||||
}
|
||||
|
||||
PaletteRootModel {
|
||||
id: paletteRootModel
|
||||
|
||||
|
@ -77,6 +68,9 @@ Rectangle {
|
|||
rightMargin: 12
|
||||
}
|
||||
|
||||
keynav.section: palettesWidget.keynavSection
|
||||
keynav.order: 2
|
||||
|
||||
onAddCustomPaletteRequested: paletteTree.insertCustomPalette(0, paletteName);
|
||||
}
|
||||
|
||||
|
@ -96,6 +90,11 @@ Rectangle {
|
|||
id: paletteTree
|
||||
clip: true
|
||||
paletteWorkspace: palettesWidget.paletteWorkspace
|
||||
backgroundColor: palettesWidget.color
|
||||
|
||||
keynav.section: palettesWidget.keynavSection
|
||||
keynav.order: 3
|
||||
keynav.enabled: paletteTree.visible
|
||||
|
||||
filter: palettesWidgetHeader.searchText
|
||||
enableAnimations: !palettesWidgetHeader.searching
|
||||
|
|
|
@ -50,6 +50,10 @@ GridView {
|
|||
|
||||
property bool enableAnimations: true
|
||||
|
||||
property KeyNavigationSubSection keynavSubSection: null
|
||||
property int keynavRow: 0
|
||||
property int keynavCol: 1
|
||||
|
||||
states: [
|
||||
State {
|
||||
name: "default"
|
||||
|
@ -97,7 +101,8 @@ GridView {
|
|||
cellWidth: stretchWidth ? Math.floor(Utils.stretched(cellDefaultWidth, width)) : cellDefaultWidth
|
||||
cellHeight: cellSize.height
|
||||
|
||||
readonly property real ncolumns: Math.floor(width / cellWidth);
|
||||
readonly property int nrows: Math.max(0, Math.floor(height / cellHeight))
|
||||
readonly property int ncolumns: Math.max(0, Math.floor(width / cellWidth))
|
||||
readonly property real lastColumnCellWidth : cellWidth + (width % cellWidth) // width of last cell in a row: might be stretched to avoid a gap at row end
|
||||
|
||||
signal moreButtonClicked()
|
||||
|
@ -164,7 +169,12 @@ GridView {
|
|||
id: moreButton
|
||||
|
||||
anchors.fill: parent
|
||||
activeFocusOnTab: this === paletteTree.currentTreeItem
|
||||
|
||||
keynav.subsection: paletteView.keynavSubSection
|
||||
//! NOTE Just Up/Down navigation now
|
||||
keynav.row: paletteView.ncells + paletteView.keynavRow
|
||||
keynav.column: 1
|
||||
keynav.enabled: paletteView.visible
|
||||
|
||||
onActiveFocusChanged: {
|
||||
if (activeFocus) {
|
||||
|
@ -183,42 +193,6 @@ GridView {
|
|||
pressedStateColor: ui.theme.accentColor
|
||||
|
||||
onClicked: paletteView.moreButtonClicked()
|
||||
|
||||
Keys.onShortcutOverride: {
|
||||
// Intercept all keys that we want to use with Keys.onPressed
|
||||
// in case they are assigned as shortcuts in Preferences.
|
||||
event.accepted = true; // intercept everything
|
||||
switch (event.key) {
|
||||
case Qt.Key_Up:
|
||||
case Qt.Key_Down:
|
||||
return;
|
||||
}
|
||||
event.accepted = false; // allow key to function as shortcut (don't intercept)
|
||||
}
|
||||
|
||||
Keys.onPressed: {
|
||||
// NOTE: All keys must be intercepted with Keys.onShortcutOverride.
|
||||
switch (event.key) {
|
||||
case Qt.Key_Up:
|
||||
focusPreviousItem();
|
||||
break;
|
||||
case Qt.Key_Down:
|
||||
paletteTree.focusNextItem(false);
|
||||
break;
|
||||
default:
|
||||
return; // don't accept event
|
||||
}
|
||||
event.accepted = true;
|
||||
}
|
||||
|
||||
function focusPreviousItem() {
|
||||
if (paletteView.count == 0) {
|
||||
paletteTree.currentItem.forceActiveFocus();
|
||||
} else {
|
||||
paletteView.currentIndex = paletteView.count - 1
|
||||
paletteView.currentItem.forceActiveFocus();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -408,34 +382,6 @@ GridView {
|
|||
Utils.removeSelectedItems(paletteController, selectionModel, paletteRootIndex);
|
||||
}
|
||||
|
||||
function focusNextItem(flags) {
|
||||
if (flags === undefined) {
|
||||
flags = ItemSelectionModel.ClearAndSelect;
|
||||
}
|
||||
|
||||
if (currentIndex == count - 1) {
|
||||
if (moreButton.visible) {
|
||||
moreButton.forceActiveFocus();
|
||||
} else {
|
||||
paletteTree.focusNextItem(false);
|
||||
}
|
||||
} else {
|
||||
currentIndex++; // next grid item
|
||||
}
|
||||
}
|
||||
|
||||
function focusPreviousItem(flags) {
|
||||
if (flags === undefined) {
|
||||
flags = ItemSelectionModel.ClearAndSelect;
|
||||
}
|
||||
|
||||
if (currentIndex == 0) {
|
||||
paletteTree.currentItem.forceActiveFocus();
|
||||
} else {
|
||||
currentIndex--; // previous grid item
|
||||
}
|
||||
}
|
||||
|
||||
function focusFirstItem() {
|
||||
if (count == 0 && moreButton.visible) {
|
||||
moreButton.forceActiveFocus();
|
||||
|
@ -504,48 +450,6 @@ GridView {
|
|||
}
|
||||
}
|
||||
|
||||
Keys.onShortcutOverride: {
|
||||
// Intercept all keys that we want to use with Keys.onPressed
|
||||
// in case they are assigned as shortcuts in Preferences.
|
||||
event.accepted = true; // intercept everything
|
||||
switch (event.key) {
|
||||
case Qt.Key_Up:
|
||||
case Qt.Key_Down:
|
||||
case Qt.Key_Left:
|
||||
case Qt.Key_Right:
|
||||
case Qt.Key_Backspace:
|
||||
case Qt.Key_Delete:
|
||||
return;
|
||||
}
|
||||
event.accepted = false; // allow key to function as shortcut (don't intercept)
|
||||
}
|
||||
|
||||
Keys.onPressed: {
|
||||
// NOTE: All keys must be intercepted with Keys.onShortcutOverride.
|
||||
switch (event.key) {
|
||||
case Qt.Key_Up:
|
||||
focusPreviousItem();
|
||||
break;
|
||||
case Qt.Key_Down:
|
||||
focusNextItem();
|
||||
break;
|
||||
case Qt.Key_Left:
|
||||
paletteTree.currentItem.forceActiveFocus();
|
||||
break;
|
||||
case Qt.Key_Right:
|
||||
if (moreButton.visible)
|
||||
moreButton.forceActiveFocus();
|
||||
break;
|
||||
case Qt.Key_Backspace:
|
||||
case Qt.Key_Delete:
|
||||
removeSelectedCells();
|
||||
break;
|
||||
default:
|
||||
return; // don't accept event
|
||||
}
|
||||
event.accepted = true;
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
id: draggedIcon
|
||||
|
||||
|
@ -576,6 +480,10 @@ GridView {
|
|||
property var modelIndex: paletteView.model.modelIndex(index)
|
||||
property var parentModelIndex: paletteView.paletteRootIndex
|
||||
|
||||
//! NOTE Please, don't remove (igor.korsukov@gmail.com)
|
||||
//property int cellRow: paletteView.ncolumns == 0 ? 0 : Math.floor(model.index / paletteView.ncolumns)
|
||||
//property int cellCol: model.index - (cellRow * paletteView.ncolumns)
|
||||
|
||||
onActiveFocusChanged: {
|
||||
if (activeFocus) {
|
||||
paletteTree.currentTreeItem = this;
|
||||
|
@ -593,7 +501,17 @@ GridView {
|
|||
width: paletteView.cellWidth
|
||||
height: paletteView.cellHeight
|
||||
|
||||
activeFocusOnTab: this === paletteTree.currentTreeItem
|
||||
keynav.subsection: paletteView.keynavSubSection
|
||||
|
||||
//! NOTE Please, don't remove (igor.korsukov@gmail.com)
|
||||
//keynav.row: paletteCell.cellRow + paletteView.keynavRow
|
||||
//keynav.column: paletteCell.cellCol + paletteView.keynavCol
|
||||
|
||||
//! NOTE Just Up/Down navigation now
|
||||
keynav.row: model.index + paletteView.keynavRow
|
||||
keynav.column: 1
|
||||
keynav.enabled: paletteView.visible
|
||||
keynav.onTriggered: paletteCell.doClicked()
|
||||
|
||||
IconView {
|
||||
anchors.fill: parent
|
||||
|
@ -605,108 +523,37 @@ GridView {
|
|||
|
||||
Accessible.name: model.accessibleText;
|
||||
|
||||
Keys.onShortcutOverride: {
|
||||
// Intercept all keys that we want to use with Keys.onPressed
|
||||
// in case they are assigned as shortcuts in Preferences.
|
||||
event.accepted = true; // intercept everything
|
||||
switch (event.key) {
|
||||
case Qt.Key_Space:
|
||||
case Qt.Key_Enter:
|
||||
case Qt.Key_Return:
|
||||
case Qt.Key_Menu:
|
||||
case Qt.Key_Asterisk:
|
||||
return;
|
||||
}
|
||||
|
||||
if (event.key === Qt.Key_F10 && event.modifiers & Qt.ShiftModifier) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (event.text.match(/[^\x00-\x20\x7F]+$/) !== null) {
|
||||
return;
|
||||
}
|
||||
|
||||
event.accepted = false; // allow key to function as shortcut (don't intercept)
|
||||
// leftClickArea
|
||||
mouseArea.drag.target: this
|
||||
mouseArea.onPressed: {
|
||||
paletteView.currentIndex = paletteCell.rowIndex;
|
||||
paletteCell.forceActiveFocus();
|
||||
paletteView.updateSelection(true);
|
||||
paletteCell.beginDrag();
|
||||
}
|
||||
|
||||
Keys.onPressed: {
|
||||
// NOTE: All keys must be intercepted with Keys.onShortcutOverride.
|
||||
const shiftHeld = event.modifiers & Qt.ShiftModifier;
|
||||
const ctrlHeld = event.modifiers & Qt.ControlModifier;
|
||||
switch (event.key) {
|
||||
case Qt.Key_Space:
|
||||
if (paletteTree.typeAheadStr.length) {
|
||||
paletteView.typeAheadFind(' ');
|
||||
} else {
|
||||
paletteView.updateSelection(true);
|
||||
}
|
||||
break;
|
||||
case Qt.Key_Enter:
|
||||
case Qt.Key_Return:
|
||||
paletteView.selectionModel.setCurrentIndex(modelIndex, ItemSelectionModel.ClearAndSelect);
|
||||
paletteView.paletteController.applyPaletteElement(modelIndex, ui.keyboardModifiers());
|
||||
break;
|
||||
case Qt.Key_F10:
|
||||
if (!shiftHeld) {
|
||||
return;
|
||||
}
|
||||
// fallthrough
|
||||
case Qt.Key_Menu:
|
||||
showCellMenu();
|
||||
break;
|
||||
case Qt.Key_Asterisk:
|
||||
if (paletteTree.typeAheadStr.length) {
|
||||
paletteView.typeAheadFind('*');
|
||||
} else if (!paletteTree.expandCollapseAll(null)) {
|
||||
paletteTree.currentItem.forceActiveFocus();
|
||||
}
|
||||
break;
|
||||
default:
|
||||
if (event.text.match(/[^\x00-\x20\x7F]+$/) !== null) {
|
||||
// Pressed non-control character(s) (e.g. "D") so go
|
||||
// to matching item (e.g. "D Major" in keysig palette)
|
||||
paletteView.typeAheadFind(event.text);
|
||||
} else {
|
||||
return; // don't accept event
|
||||
}
|
||||
function doClicked() {
|
||||
if (paletteView.paletteController.applyPaletteElement(paletteCell.modelIndex, ui.keyboardModifiers())) {
|
||||
paletteView.selectionModel.setCurrentIndex(paletteCell.modelIndex, ItemSelectionModel.Current);
|
||||
}
|
||||
event.accepted = true;
|
||||
}
|
||||
|
||||
MouseArea {
|
||||
id: leftClickArea
|
||||
anchors.fill: parent
|
||||
drag.target: this
|
||||
onClicked: paletteCell.doClicked()
|
||||
|
||||
onPressed: {
|
||||
paletteView.currentIndex = paletteCell.rowIndex;
|
||||
paletteCell.forceActiveFocus();
|
||||
paletteView.updateSelection(true);
|
||||
paletteCell.beginDrag();
|
||||
}
|
||||
|
||||
onClicked: {
|
||||
if (paletteView.paletteController.applyPaletteElement(paletteCell.modelIndex, ui.keyboardModifiers())) {
|
||||
paletteView.selectionModel.setCurrentIndex(paletteCell.modelIndex, ItemSelectionModel.Current);
|
||||
}
|
||||
}
|
||||
|
||||
onDoubleClicked: {
|
||||
const index = paletteCell.modelIndex;
|
||||
paletteView.selectionModel.setCurrentIndex(index, ItemSelectionModel.Current);
|
||||
paletteView.paletteController.applyPaletteElement(index, mouse.modifiers);
|
||||
}
|
||||
onDoubleClicked: {
|
||||
const index = paletteCell.modelIndex;
|
||||
paletteView.selectionModel.setCurrentIndex(index, ItemSelectionModel.Current);
|
||||
paletteView.paletteController.applyPaletteElement(index, mouseArea.mouse.modifiers);
|
||||
}
|
||||
|
||||
MouseArea {
|
||||
id: rightClickArea
|
||||
anchors.fill: parent
|
||||
acceptedButtons: Qt.RightButton
|
||||
|
||||
onClicked: showCellMenu(true)
|
||||
}
|
||||
|
||||
Drag.active: leftClickArea.drag.active
|
||||
Drag.active: mouseArea.drag.active
|
||||
Drag.dragType: Drag.Automatic
|
||||
Drag.supportedActions: Qt.CopyAction | (model.editable ? Qt.MoveAction : 0)
|
||||
Drag.mimeData: Drag.active ? mimeData : {}
|
||||
|
|
|
@ -29,25 +29,44 @@ import "utils.js" as Utils
|
|||
|
||||
ListView {
|
||||
id: paletteTree
|
||||
Accessible.name: qsTrc("palette", "Palettes Tree, contains %n palette(s)", "", count)
|
||||
|
||||
activeFocusOnTab: true // allow focus even when empty
|
||||
|
||||
property PaletteWorkspace paletteWorkspace
|
||||
property var paletteModel: Boolean(paletteWorkspace) ? paletteWorkspace.mainPaletteModel : null
|
||||
property PaletteController paletteController: paletteWorkspace ? paletteWorkspace.mainPaletteController : null
|
||||
|
||||
property alias keynav: keynavTree
|
||||
|
||||
// Scroll palettes list when dragging a palette close to the list's border
|
||||
property bool itemDragged: false
|
||||
preferredHighlightBegin: Math.min(48, Math.floor(0.1 * height))
|
||||
preferredHighlightEnd: Math.ceil(height - preferredHighlightBegin)
|
||||
highlightRangeMode: itemDragged ? ListView.ApplyRange : ListView.NoHighlightRange
|
||||
|
||||
property Item currentTreeItem: currentItem // most recently focused item at any level of the tree
|
||||
|
||||
property string filter: ""
|
||||
property bool searchOpened: false
|
||||
|
||||
property bool enableAnimations: true
|
||||
property int expandDuration: enableAnimations ? 150 : 0 // duration of expand / collapse animations
|
||||
|
||||
property string backgroundColor: "#ffffff"
|
||||
|
||||
preferredHighlightBegin: Math.min(48, Math.floor(0.1 * height))
|
||||
preferredHighlightEnd: Math.ceil(height - preferredHighlightBegin)
|
||||
highlightRangeMode: itemDragged ? ListView.ApplyRange : ListView.NoHighlightRange
|
||||
|
||||
Accessible.name: qsTrc("palette", "Palettes Tree, contains %n palette(s)", "", count)
|
||||
|
||||
|
||||
KeyNavigationSubSection {
|
||||
id: keynavTree
|
||||
name: "PalettesTree"
|
||||
direction: KeyNavigationSubSection.Both
|
||||
onActiveChanged: {
|
||||
if (active) {
|
||||
paletteTree.forceActiveFocus()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
onSearchOpenedChanged: {
|
||||
if (paletteWorkspace) {
|
||||
paletteWorkspace.setSearching(searchOpened)
|
||||
|
@ -65,9 +84,6 @@ ListView {
|
|||
}
|
||||
}
|
||||
|
||||
property bool enableAnimations: true
|
||||
property int expandDuration: enableAnimations ? 150 : 0 // duration of expand / collapse animations
|
||||
|
||||
function insertCustomPalette(idx, paletteName) {
|
||||
if (paletteTree.paletteController.insertNewItem(paletteTreeDelegateModel.rootIndex, idx, paletteName)) {
|
||||
positionViewAtIndex(idx, ListView.Contain)
|
||||
|
@ -132,56 +148,6 @@ ListView {
|
|||
Utils.removeSelectedItems(paletteController, paletteSelectionModel, parentIndex);
|
||||
}
|
||||
|
||||
Keys.onShortcutOverride: {
|
||||
// Intercept all keys that we want to use with Keys.onPressed
|
||||
// in case they are assigned as shortcuts in Preferences.
|
||||
event.accepted = true; // intercept everything
|
||||
switch (event.key) {
|
||||
case Qt.Key_Down:
|
||||
case Qt.Key_Up:
|
||||
case Qt.Key_Home:
|
||||
case Qt.Key_End:
|
||||
case Qt.Key_PageUp:
|
||||
case Qt.Key_PageDown:
|
||||
case Qt.Key_Backspace:
|
||||
case Qt.Key_Delete:
|
||||
return;
|
||||
}
|
||||
event.accepted = false; // allow key to function as shortcut (don't intercept)
|
||||
}
|
||||
|
||||
Keys.onPressed: {
|
||||
// NOTE: All keys must be intercepted with Keys.onShortcutOverride.
|
||||
switch (event.key) {
|
||||
case Qt.Key_Down:
|
||||
focusNextItem();
|
||||
break;
|
||||
case Qt.Key_Up:
|
||||
focusPreviousItem();
|
||||
break;
|
||||
case Qt.Key_Home:
|
||||
focusFirstItem();
|
||||
break;
|
||||
case Qt.Key_End:
|
||||
focusLastItem();
|
||||
break;
|
||||
case Qt.Key_PageUp:
|
||||
focusPreviousPageItem();
|
||||
break;
|
||||
case Qt.Key_PageDown:
|
||||
focusNextPageItem();
|
||||
break;
|
||||
case Qt.Key_Backspace:
|
||||
case Qt.Key_Delete:
|
||||
expandedPopupIndex = null;
|
||||
removeSelectedItems();
|
||||
break;
|
||||
default:
|
||||
return; // don't accept event
|
||||
}
|
||||
event.accepted = true;
|
||||
}
|
||||
|
||||
displaced: Transition {
|
||||
enabled: paletteTree.enableAnimations
|
||||
NumberAnimation { property: "y"; duration: 150 }
|
||||
|
@ -200,103 +166,6 @@ ListView {
|
|||
return { display: "", gridSize: Qt.size(1, 1), drawGrid: false, custom: false, editable: false, expanded: false };
|
||||
}
|
||||
|
||||
function focusNextItem(includeChildren) {
|
||||
if (includeChildren === undefined) { // https://stackoverflow.com/a/44128406
|
||||
includeChildren = true;
|
||||
}
|
||||
|
||||
if (includeChildren && currentItem.expanded) {
|
||||
currentItem.focusFirstItem();
|
||||
return;
|
||||
}
|
||||
|
||||
if (currentIndex == count - 1) {
|
||||
return; // no next item
|
||||
}
|
||||
|
||||
incrementCurrentIndex();
|
||||
currentItem.forceActiveFocus();
|
||||
positionViewAtIndex(currentIndex, ListView.Contain);
|
||||
}
|
||||
|
||||
function focusPreviousItem(includeChildren) {
|
||||
if (includeChildren === undefined) { // https://stackoverflow.com/a/44128406
|
||||
includeChildren = true;
|
||||
}
|
||||
|
||||
if (currentIndex == 0) {
|
||||
return; // no previous item
|
||||
}
|
||||
|
||||
decrementCurrentIndex();
|
||||
|
||||
if (includeChildren && currentItem.expanded) {
|
||||
currentItem.focusLastItem();
|
||||
} else {
|
||||
currentItem.forceActiveFocus();
|
||||
}
|
||||
|
||||
positionViewAtIndex(currentIndex, ListView.Contain);
|
||||
}
|
||||
|
||||
function focusNextPageItem() {
|
||||
if (currentIndex < count - 1) {
|
||||
currentIndex++; // move by at least one item
|
||||
// try to keep going, but new item must stay entirely in view
|
||||
var distance = currentItem.height;
|
||||
|
||||
while (currentIndex < count - 1) {
|
||||
currentIndex++; // try another
|
||||
distance += currentItem.height;
|
||||
if (distance > height) {
|
||||
currentIndex--; // too far, go back one
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
currentItem.forceActiveFocus();
|
||||
positionViewAtIndex(currentIndex, ListView.Contain);
|
||||
}
|
||||
|
||||
function focusPreviousPageItem() {
|
||||
if (currentIndex > 0) {
|
||||
currentIndex--; // move by at least one item
|
||||
// try to keep going, but new item must stay entirely in view
|
||||
var distance = currentItem.height;
|
||||
|
||||
while (currentIndex > 0) {
|
||||
currentIndex--; // try another
|
||||
distance += currentItem.height;
|
||||
if (distance > height) {
|
||||
currentIndex++; // too far, go back one
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
currentItem.forceActiveFocus();
|
||||
positionViewAtIndex(currentIndex, ListView.Contain);
|
||||
}
|
||||
|
||||
function focusFirstItem() {
|
||||
currentIndex = 0;
|
||||
currentItem.forceActiveFocus();
|
||||
positionViewAtIndex(currentIndex, ListView.Contain);
|
||||
}
|
||||
|
||||
function focusLastItem() {
|
||||
currentIndex = count - 1;
|
||||
|
||||
if (currentItem.expanded) {
|
||||
currentItem.focusLastItem();
|
||||
} else {
|
||||
currentItem.forceActiveFocus();
|
||||
}
|
||||
|
||||
positionViewAtIndex(currentIndex, ListView.Contain);
|
||||
}
|
||||
|
||||
function focusNextMatchingItem(str, startIndex) {
|
||||
const modelIndex = paletteModel.index(startIndex, 0);
|
||||
const matchedIndexList = paletteModel.match(modelIndex, Qt.ToolTipRole, str);
|
||||
|
@ -373,6 +242,7 @@ ListView {
|
|||
topPadding: 0
|
||||
bottomPadding: expanded ? 4 : 0
|
||||
property int rowIndex: index
|
||||
property int keynavRow: (index + 1) * 10000 // to make unique
|
||||
property var modelIndex: paletteTree.model.modelIndex(index, 0)
|
||||
|
||||
onActiveFocusChanged: {
|
||||
|
@ -386,14 +256,6 @@ ListView {
|
|||
paletteTree.implicitWidth = Math.max(paletteTree.implicitWidth, w);
|
||||
}
|
||||
|
||||
function focusFirstItem() {
|
||||
mainPalette.focusFirstItem();
|
||||
}
|
||||
|
||||
function focusLastItem() {
|
||||
mainPalette.focusLastItem();
|
||||
}
|
||||
|
||||
property bool expanded: filter.length || model.expanded
|
||||
|
||||
function toggleExpand() {
|
||||
|
@ -418,13 +280,17 @@ ListView {
|
|||
|
||||
property bool selected: paletteSelectionModel.hasSelection ? paletteSelectionModel.isSelected(modelIndex) : false
|
||||
|
||||
onClicked: {
|
||||
function doItemClicked() {
|
||||
forceActiveFocus();
|
||||
const cmd = selected ? ItemSelectionModel.Toggle : ItemSelectionModel.ClearAndSelect;
|
||||
paletteSelectionModel.setCurrentIndex(modelIndex, cmd);
|
||||
paletteTree.currentIndex = index;
|
||||
}
|
||||
|
||||
onClicked: {
|
||||
control.doItemClicked()
|
||||
}
|
||||
|
||||
onDoubleClicked: {
|
||||
forceActiveFocus();
|
||||
paletteSelectionModel.setCurrentIndex(modelIndex, ItemSelectionModel.Deselect);
|
||||
|
@ -432,9 +298,21 @@ ListView {
|
|||
}
|
||||
|
||||
background: ListItemBlank {
|
||||
background.color: paletteTree.backgroundColor
|
||||
visible: !control.Drag.active
|
||||
|
||||
isSelected: control.selected
|
||||
|
||||
keynav.name: "PaletteTreeItemDelegate"
|
||||
keynav.subsection: keynavTree
|
||||
keynav.row: control.keynavRow
|
||||
keynav.column: 0
|
||||
enabled: control.visible
|
||||
keynav.onActiveChanged: {
|
||||
if (keynav.active && !control.selected) {
|
||||
control.doItemClicked()
|
||||
}
|
||||
paletteTree.positionViewAtIndex(control.rowIndex, ListView.Contain);
|
||||
}
|
||||
}
|
||||
|
||||
highlighted: (activeFocus && !selected) || DelegateModel.isUnresolved
|
||||
|
@ -449,94 +327,11 @@ ListView {
|
|||
property size cellSize: model.gridSize
|
||||
property bool drawGrid: model.drawGrid
|
||||
|
||||
activeFocusOnTab: this === paletteTree.currentTreeItem
|
||||
|
||||
function hidePalette() {
|
||||
paletteTree.expandedPopupIndex = null;
|
||||
paletteTree.paletteController.remove(modelIndex);
|
||||
}
|
||||
|
||||
Keys.onShortcutOverride: {
|
||||
// Intercept all keys that we want to use with Keys.onPressed
|
||||
// in case they are assigned as shortcuts in Preferences.
|
||||
event.accepted = true; // intercept everything
|
||||
switch (event.key) {
|
||||
case Qt.Key_Right:
|
||||
case Qt.Key_Plus:
|
||||
case Qt.Key_Left:
|
||||
case Qt.Key_Minus:
|
||||
case Qt.Key_Space:
|
||||
case Qt.Key_Enter:
|
||||
case Qt.Key_Return:
|
||||
case Qt.Key_Menu:
|
||||
case Qt.Key_Asterisk:
|
||||
return;
|
||||
}
|
||||
|
||||
if (event.key === Qt.Key_F10 && event.modifiers & Qt.ShiftModifier) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (event.text.match(/[^\x00-\x20\x7F]+$/) !== null) {
|
||||
return;
|
||||
}
|
||||
|
||||
event.accepted = false; // allow key to function as shortcut (don't intercept)
|
||||
}
|
||||
|
||||
Keys.onPressed: {
|
||||
// NOTE: All keys must be intercepted with Keys.onShortcutOverride.
|
||||
switch (event.key) {
|
||||
case Qt.Key_Right:
|
||||
case Qt.Key_Plus:
|
||||
if (!expanded) {
|
||||
toggleExpand();
|
||||
} else if (event.key === Qt.Key_Right) {
|
||||
focusFirstItem();
|
||||
}
|
||||
break;
|
||||
case Qt.Key_Left:
|
||||
case Qt.Key_Minus:
|
||||
if (expanded)
|
||||
toggleExpand();
|
||||
break;
|
||||
case Qt.Key_Space:
|
||||
if (paletteTree.typeAheadStr.length) {
|
||||
paletteTree.typeAheadFind(' ');
|
||||
break;
|
||||
}
|
||||
// fallthrough
|
||||
case Qt.Key_Enter:
|
||||
case Qt.Key_Return:
|
||||
toggleExpand();
|
||||
break;
|
||||
case Qt.Key_F10:
|
||||
if (!(event.modifiers & Qt.ShiftModifier)) {
|
||||
return;
|
||||
}
|
||||
// fallthrough
|
||||
case Qt.Key_Menu:
|
||||
paletteHeader.showPaletteMenu();
|
||||
break;
|
||||
case Qt.Key_Asterisk:
|
||||
if (paletteTree.typeAheadStr.length) {
|
||||
paletteTree.typeAheadFind('*');
|
||||
} else {
|
||||
paletteTree.expandCollapseAll(null);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
if (event.text.match(/[^\x00-\x20\x7F]+$/) !== null) {
|
||||
// Pressed non-control character(s) (e.g. "L")
|
||||
// so go to matching palette (e.g. "Lines")
|
||||
paletteTree.typeAheadFind(event.text);
|
||||
} else {
|
||||
return; // don't accept event
|
||||
}
|
||||
}
|
||||
event.accepted = true;
|
||||
}
|
||||
|
||||
text: filter.length ? qsTrc("palette", "%1, contains %n matching element(s)", "palette", mainPalette.count).arg(model.accessibleText)
|
||||
: model.expanded ? qsTrc("palette", "%1 expanded", "tree item not collapsed").arg(model.accessibleText)
|
||||
: model.accessibleText
|
||||
|
@ -577,8 +372,8 @@ ListView {
|
|||
|
||||
if (dropAction == Qt.MoveAction) {
|
||||
controller.move(
|
||||
root, rowIndex,
|
||||
root, destIndex);
|
||||
root, rowIndex,
|
||||
root, destIndex);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -647,10 +442,13 @@ ListView {
|
|||
hovered: control.hovered
|
||||
text: model.display
|
||||
|
||||
keynavSubsection: keynavTree
|
||||
keynavRow: control.keynavRow
|
||||
|
||||
hidePaletteElementVisible: {
|
||||
return !control.selected && control.expanded
|
||||
&& paletteSelectionModel.hasSelection && paletteSelectionModel.columnIntersectsSelection(0, control.modelIndex)
|
||||
&& paletteTree.paletteModel.parent(paletteSelectionModel.currentIndex) === control.modelIndex; // HACK to work around a (possible?) bug in columnIntersectsSelection
|
||||
&& paletteSelectionModel.hasSelection && paletteSelectionModel.columnIntersectsSelection(0, control.modelIndex)
|
||||
&& paletteTree.paletteModel.parent(paletteSelectionModel.currentIndex) === control.modelIndex; // HACK to work around a (possible?) bug in columnIntersectsSelection
|
||||
}
|
||||
|
||||
custom: model.custom
|
||||
|
@ -706,6 +504,9 @@ ListView {
|
|||
id: mainPalette
|
||||
anchors { fill: parent; margins: parent.padding }
|
||||
|
||||
keynavSubSection: keynavTree
|
||||
keynavRow: control.keynavRow + 1
|
||||
|
||||
cellSize: control.cellSize
|
||||
drawGrid: control.drawGrid
|
||||
|
||||
|
@ -811,14 +612,4 @@ ListView {
|
|||
|
||||
// placeholder footer item to reserve a space for "More" popup to expand
|
||||
footer: Item { height: 0 }
|
||||
|
||||
Connections {
|
||||
target: palettesWidget
|
||||
function onHasFocusChanged() {
|
||||
if (!palettesWidget.hasFocus) {
|
||||
paletteSelectionModel.clearSelection();
|
||||
expandedPopupIndex = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -17,12 +17,12 @@
|
|||
// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
//=============================================================================
|
||||
|
||||
import QtQuick 2.8
|
||||
import QtQuick 2.15
|
||||
import QtQuick.Controls 2.1
|
||||
|
||||
import MuseScore.Palette 1.0
|
||||
import MuseScore.UiComponents 1.0
|
||||
import MuseScore.Ui 1.0
|
||||
import MuseScore.UiComponents 1.0
|
||||
import MuseScore.Palette 1.0
|
||||
|
||||
import "utils.js" as Utils
|
||||
|
||||
|
@ -36,6 +36,8 @@ Item {
|
|||
|
||||
property alias popupMaxHeight: palettePopup.maxHeight
|
||||
|
||||
property alias keynav: keynavSub
|
||||
|
||||
signal addCustomPaletteRequested(var paletteName)
|
||||
|
||||
implicitHeight: childrenRect.height
|
||||
|
@ -55,20 +57,39 @@ Item {
|
|||
}
|
||||
}
|
||||
|
||||
KeyNavigationSubSection {
|
||||
id: keynavSub
|
||||
name: "PalettesHeader"
|
||||
onActiveChanged: {
|
||||
if (active) {
|
||||
header.forceActiveFocus()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
FlatButton {
|
||||
id: morePalettesButton
|
||||
anchors.left: parent.left
|
||||
anchors.right: searchTextButton.left
|
||||
anchors.rightMargin: 8
|
||||
objectName: "AddPalettesBtn"
|
||||
keynav.subsection: keynavSub
|
||||
keynav.order: 1
|
||||
enabled: !searchTextInput.visible
|
||||
text: qsTrc("palette", "Add Palettes")
|
||||
onClicked: palettePopup.visible = !palettePopup.visible
|
||||
onClicked: {
|
||||
palettePopup.visible = !palettePopup.visible
|
||||
}
|
||||
}
|
||||
|
||||
FlatButton {
|
||||
id: searchTextButton
|
||||
anchors.right: parent.right
|
||||
objectName: "SearchPalettesBtn"
|
||||
keynav.subsection: keynavSub
|
||||
keynav.order: 2
|
||||
enabled: !searchTextInput.visible
|
||||
icon: IconCode.SEARCH
|
||||
|
||||
onClicked: {
|
||||
toggleSearch()
|
||||
}
|
||||
|
@ -78,12 +99,47 @@ Item {
|
|||
id: searchTextInput
|
||||
width: parent.width
|
||||
|
||||
//! TODO Move to SearchField inside
|
||||
KeyNavigationControl {
|
||||
id: keynavSearchField
|
||||
name: "SearchPalettesField"
|
||||
subsection: keynavSub
|
||||
order: 3
|
||||
enabled: searchTextInput.visible
|
||||
onActiveChanged: {
|
||||
if (keynavSearchField.active) {
|
||||
searchTextInput.forceActiveFocus()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
KeyNavigationControl {
|
||||
id: keynavSearchFieldClose
|
||||
name: "SearchPalettesFieldClose"
|
||||
subsection: keynavSub
|
||||
order: 4
|
||||
enabled: searchTextInput.visible && searchTextInput.clearTextButtonVisible
|
||||
onTriggered: toggleSearch()
|
||||
}
|
||||
|
||||
onVisibleChanged: {
|
||||
if (!searchTextInput.visible) {
|
||||
morePalettesButton.keynav.forceActive()
|
||||
}
|
||||
}
|
||||
|
||||
//! ----------
|
||||
|
||||
visible: false
|
||||
|
||||
onSearchTextChanged: resultsTimer.restart()
|
||||
onActiveFocusChanged: {
|
||||
resultsTimer.stop();
|
||||
Accessible.name = qsTrc("palette", "Palette Search")
|
||||
|
||||
if (searchTextInput.activeFocus) {
|
||||
keynavSearchField.forceActive()
|
||||
}
|
||||
}
|
||||
|
||||
Timer {
|
||||
|
@ -94,10 +150,6 @@ Item {
|
|||
}
|
||||
}
|
||||
|
||||
KeyNavigation.tab: paletteTree.currentTreeItem
|
||||
|
||||
Keys.onDownPressed: paletteTree.focusFirstItem();
|
||||
Keys.onUpPressed: paletteTree.focusLastItem();
|
||||
Keys.onEscapePressed: toggleSearch()
|
||||
|
||||
clearTextButtonVisible: true
|
||||
|
@ -137,12 +189,4 @@ Item {
|
|||
createCustomPalettePopup.open()
|
||||
}
|
||||
}
|
||||
|
||||
Connections {
|
||||
target: palettesWidget
|
||||
function onHasFocusChanged() {
|
||||
if (!palettesWidget.hasFocus && !palettePopup.inMenuAction)
|
||||
palettePopup.visible = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -37,6 +37,9 @@ Item {
|
|||
property PaletteWorkspace paletteWorkspace
|
||||
property var modelIndex: null
|
||||
|
||||
property KeyNavigationSubSection keynavSubsection: null
|
||||
property int keynavRow: 0
|
||||
|
||||
signal toggleExpandRequested()
|
||||
signal enableEditingToggled(bool val)
|
||||
signal hideSelectedElementsRequested()
|
||||
|
@ -66,6 +69,11 @@ Item {
|
|||
icon: paletteHeader.expanded ? IconCode.SMALL_ARROW_DOWN : IconCode.SMALL_ARROW_RIGHT
|
||||
normalStateColor: "transparent"
|
||||
|
||||
enabled: paletteExpandArrow.visible
|
||||
keynav.subsection: paletteHeader.keynavSubsection
|
||||
keynav.row: paletteHeader.keynavRow
|
||||
keynav.column: 1
|
||||
|
||||
onClicked: paletteHeader.toggleExpandRequested()
|
||||
}
|
||||
|
||||
|
@ -99,8 +107,10 @@ Item {
|
|||
activeFocusOnTab: mainPalette.currentItem === paletteTree.currentTreeItem
|
||||
normalStateColor: "transparent"
|
||||
|
||||
KeyNavigation.backtab: mainPalette.currentItem
|
||||
KeyNavigation.tab: focusBreaker
|
||||
enabled: deleteButton.visible
|
||||
keynav.subsection: paletteHeader.keynavSubsection
|
||||
keynav.row: paletteHeader.keynavRow
|
||||
keynav.column: 2
|
||||
|
||||
onClicked: {
|
||||
hideSelectedElementsRequested()
|
||||
|
@ -115,7 +125,10 @@ Item {
|
|||
|
||||
visible: paletteHeader.expanded || paletteHeader.hovered || paletteHeaderMenu.visible
|
||||
|
||||
activeFocusOnTab: parent.parent.parent === paletteTree.currentTreeItem
|
||||
enabled: paletteHeaderMenuButton.visible
|
||||
keynav.subsection: paletteHeader.keynavSubsection
|
||||
keynav.row: paletteHeader.keynavRow
|
||||
keynav.column: 3
|
||||
|
||||
icon: IconCode.MENU_THREE_DOTS
|
||||
normalStateColor: "transparent"
|
||||
|
|
Loading…
Reference in a new issue