Qt Quick keyboard focus (本文源於 http://doc.qt.io/qt-5/qtquick-input-focus.html)

按下或釋放按鍵時,會生成一個按鍵事件並將其傳送到focusQt Quick item 為了便於構建可重複使用的組件(reusable components)並解決Fluid UI特有的一些案例,Qt Quick itemQt的傳統keyboard focus model添加了以scope為基礎的擴充。

 

Key Handling Overview - 按鍵處理概觀

當使用者按下或釋放按鍵時,會發生以下情況:

1. Qt收到key action並產生key event

2. 如果QQuickWindowactive window,則會將key event傳遞給它。

3. Key event由場景(scene)傳遞給具有active focusItem 如果沒有item具有active focus,則忽略key event

4. 如果具有active focusQQuickItem接受key event,則停止傳播(propagation) 否則,event將被發送到item的父項(parent),直到接受event或到達root item

Rectangle type具有active focus並且按下了A鍵,則不會進一步傳播該event 按下B鍵後,event將傳播到root item,從而被忽略。

Rectangle {
    width: 100; height: 100
    focus: true
    Keys.onPressed: {
        if (event.key == Qt.Key_A) {
            console.log('Key A was pressed');
            event.accepted = true;
        }
    }
}

5. 如果到root item,則忽略key event並繼續常規Qt鍵處理。

另請參閱Keys附加屬性和KeyNavigation附加屬性。

 

Querying the Active Focus Item - 查詢Active Focus Item

可以通過Item :: activeFocus屬性查詢Item是否具有active focus 例如,這裡我們有一個Text類型,其text取決於它是否具有活動焦點。

   Text {
        text: activeFocus ? "I have active focus!" : "I do not have active focus"
    }

 

 

Acquiring Focus and Focus Scopes - 獲得FocusFocus Scopes

Item通過將focus屬性設置為true來請求焦點。

 

對於非常簡單的情況,僅設置focus屬性有時就足夠了。 如果我們使用qmlscene運行以下示例,我們會看到keyHandler類型具有active focus,按ABC鍵可以適當地修改text

Rectangle {
    color: "lightsteelblue"; width: 240; height: 25
    Text { id: myText }
    Item {
        id: keyHandler
        focus: true
        Keys.onPressed: {
            if (event.key == Qt.Key_A)
                myText.text = 'Key A was pressed'
            else if (event.key == Qt.Key_B)
                myText.text = 'Key B was pressed'
            else if (event.key == Qt.Key_C)
                myText.text = 'Key C was pressed'
        }
    }
}

 

如果將上述範例用作可重複使用或importcomponent,則只需使用focus屬性即可。

 

為了示範,我們創建了先前定義的component的兩個實例,並將第一個組件設置為具有焦點。 目的是當按下ABC鍵時,兩個component中的第一個component去接收事件並相應地作出響應。

 

導入和創建兩個MyWidget實例的代碼:

//Window code that imports MyWidget
Rectangle {
    id: window
    color: "white"; width: 240; height: 150
 
    Column {
        anchors.centerIn: parent; spacing: 15
 
        MyWidget {
            focus: true             //set this MyWidget to receive the focus
            color: "lightblue"
        }
        MyWidget {
            color: "palegreen"
        }
    }
}

The MyWidget code:

Rectangle {
    id: widget
    color: "lightsteelblue"; width: 175; height: 25; radius: 10; antialiasing: true
    Text { id: label; anchors.centerIn: parent}
    focus: true
    Keys.onPressed: {
        if (event.key == Qt.Key_A)
            label.text = 'Key A was pressed'
        else if (event.key == Qt.Key_B)
            label.text = 'Key B was pressed'
        else if (event.key == Qt.Key_C)
            label.text = 'Key C was pressed'
    }
}

 

我們希望第一個MyWidget對象具有focus,因此我們將其focus屬性設置為true 但是,通過運行代碼,我們可以確認第二個小部件是否獲得焦點。

 

查看MyWidgetwindow代碼,問題很明顯 - 有三種typefocus屬性設置為true

兩個MyWidgets將焦點設置為truewindow component也設置焦點。最終,只有一種類型可以具有鍵盤焦點,系統必須決定哪種類型獲得焦點。 創建第二個MyWidget時,它會收到焦點,因為它是將focus屬性設置為true的最後一種類型。

 

這個問題是由於可見性(visibility) MyWidget component希望具有焦點,但在導入或重用時無法控制焦點 同樣,window component無法知道其導入的組件是否正在請求焦點。

 

為了解決這個問題,QML引入了一個被稱為焦點範圍(focus scope)的概念。 對於現有的Qt使用者,一個focus scope就像自動焦點代理(automatic focus proxy) 通過聲明FocusScope類型創focus scope

 

在下一個範例中,將FocusScope類型添加到component中,並顯示視覺結果。

FocusScope {
 
    //FocusScope needs to bind to visual properties of the Rectangle
    property alias color: rectangle.color
    x: rectangle.x; y: rectangle.y
    width: rectangle.width; height: rectangle.height
 
    Rectangle {
        id: rectangle
        anchors.centerIn: parent
        color: "lightsteelblue"; width: 175; height: 25; radius: 10; antialiasing: true
        Text { id: label; anchors.centerIn: parent }
        focus: true
        Keys.onPressed: {
            if (event.key == Qt.Key_A)
                label.text = 'Key A was pressed'
            else if (event.key == Qt.Key_B)
                label.text = 'Key B was pressed'
            else if (event.key == Qt.Key_C)
                label.text = 'Key C was pressed'
        }
    }
}

從概念上講,focus scope非常簡單。

 

1. 在每個focus scope內,就一個object可以將Item :: focus設置為true 如果設置了focus屬性的多個item,則設置focus的最後一種type將具有focus而其他則沒有設置,類似於沒有focus scope時。

2. 當focus scope接收到active focus時,在其scope內中有設focustype(如果有)也會獲得active focus 如果此type也是FocusScope,則代理行為將繼續下去。 focus scope和子焦點項(sub-focused item)都都將設置activeFocus屬性。

 

 

請注意,由於FocusScope類型不是視覺類型(visual type) (只是綁為群組的概念),因此需要將其子項的屬性公開給FocusScope的父項。 Layoutspositioning類型將使用這些視覺(visual)和樣式(styling)屬性來創建layout 在我們的範例中,Column類型無法正確顯示兩個widgets,因為FocusScope缺少自己的可視屬性。 MyWidget component直接綁定到矩形屬性(rectangle properties),以允許Column類型創建包含FocusScope子項的layout

 

到目前為止,該範例已靜態選擇了第二個component 現在擴展此組件以使其可點擊(clickable),並將其添加到原始應用程序。 我們仍默認將其中一個widget設置為focus 現在,點擊MyClickableWidget會使其focus,而另一個widget會失去focus

 

導入並創建兩個MyClickableWidget實例的代碼:

Rectangle {
    id: window
 
    color: "white"; width: 240; height: 150
 
    Column {
        anchors.centerIn: parent; spacing: 15
 
        MyClickableWidget {
            focus: true             //set this MyWidget to receive the focus
            color: "lightblue"
        }
        MyClickableWidget {
            color: "palegreen"
        }
    }
 
}

The MyClickableWidget code:

FocusScope {
 
    id: scope
 
    //FocusScope needs to bind to visual properties of the children
    property alias color: rectangle.color
    x: rectangle.x; y: rectangle.y
    width: rectangle.width; height: rectangle.height
 
    Rectangle {
        id: rectangle
        anchors.centerIn: parent
        color: "lightsteelblue"; width: 175; height: 25; radius: 10; antialiasing: true
        Text { id: label; anchors.centerIn: parent }
        focus: true
        Keys.onPressed: {
            if (event.key == Qt.Key_A)
                label.text = 'Key A was pressed'
            else if (event.key == Qt.Key_B)
                label.text = 'Key B was pressed'
            else if (event.key == Qt.Key_C)
                label.text = 'Key C was pressed'
        }
    }
    MouseArea { anchors.fill: parent; onClicked: { scope.focus = true } }
}

 

QML Item明確地放棄focus時(經由設定: 當處於active focus時將其focus屬性設置為false),系統不會自動選擇另一種類型來獲得focus 也就是說,可能沒有目前的active focus

 

有關使用FocusScope類型在多個區域之間移動鍵盤focus的範例,請參閱Qt Quick Examples - Key Interaction (Qt快速示例 鍵盤交互)

 

 

Advanced Uses of Focus Scopes – Focus Scopes的進階使用

Focus scope允許集中分配(allocation)以便輕鬆分區(partitioned) 有一些QML項目就是使用它來達到這個效果。

 

例如,ListView本身就是一個focus scope 這通常不容易被注意到,因為ListView通常不會手動添加可視子項。 通過作為focus scopeListView可以focus當下的list item而不必擔心它將如何影響應用程序的其餘部分。這是由於允許當下item delegate對按鍵做出反應。

 

以下這個人工的例子說明了這是如何運作的。 Return鍵將印出當下list itemname

Rectangle {
    color: "lightsteelblue"; width: 100; height: 50
 
    ListView {
        anchors.fill: parent
        focus: true
 
        model: ListModel {
            ListElement { name: "Bob" }
            ListElement { name: "John" }
            ListElement { name: "Michael" }
        }
 
        delegate: FocusScope {
                width: childrenRect.width; height: childrenRect.height
                x:childrenRect.x; y: childrenRect.y
                TextInput {
                    focus: true
                    text: name
                    Keys.onReturnPressed: console.log(name)
                }
        }
    }
}

雖然這個例子很簡單,但幕後有很多事情要做。 每當當下的item改變時,ListView都會設置delegateItem :: focus屬性。 由於ListView是一個focus scope,因此不會影響應用程序的其餘部分。 但是,如果ListView本身俱有active focus,則會造成delegate本身去接收active focus 在此範例中,delegateroot類型也是一個focus scope,會將active focus給予實際執行處理Return鍵工作的Text type

 

所有QML view classes(如PathViewGridView)的行為方式都相似於此 - 允許了在各自delegate中的鍵盤處理方式。

arrow
arrow
    全站熱搜

    lynn770707 發表在 痞客邦 留言(0) 人氣()