Is there a way to hide the language selection key from the virtual keyboard without use a custom layout?
I was able to hide the language key with a workaround:
property var keyboardLayout: inputPanel.keyboard.layout
function findChildByProperty(parent, propertyName, propertyValue, compareCb) {
var obj = null
if (parent === null)
return null
var children = parent.children
for (var i = 0; i < children.length; i++) {
obj = children[i]
if (obj.hasOwnProperty(propertyName)) {
if (compareCb !== null) {
if (compareCb(obj[propertyName], propertyValue))
break
} else if (obj[propertyName] === propertyValue) {
break
}
}
obj = findChildByProperty(obj, propertyName, propertyValue, compareCb)
if (obj)
break
}
return obj
}
onKeyboardLayoutChanged: {
if(keyboardLayout!=""){
var ChangeLanguageKey= findChildByProperty(inputPanel.keyboard, "objectName", "changeLanguageKey", null)
if(ChangeLanguageKey){
ChangeLanguageKey.visible=false
}
}
}
InputPanel {
id: inputPanel
z: 99
y: parent.height
anchors.left: parent.left
anchors.right: parent.right
states: State {
name: "visible"
when: inputPanel.active
PropertyChanges {
target: inputPanel
y: parent.height - inputPanel.height
}
}
transitions: Transition {
from: ""
to: "visible"
reversible: true
ParallelAnimation {
NumberAnimation {
properties: "y"
duration: 400
easing.type: Easing.InOutBack
}
}
}
CustomComponents.AutoScroller {
id:autoscroller
panelY: inputPanel.y
}
}
This only works in version 5.9 where the objectname property is defined with "changeLanguageKey", for previous versions set the property in the source code and recompile.
No, not without using a custom layout.
You can always modify the layouts that come with the keyboard though.
I was able to hide the hideKeyboard key with this trick. I basically tried to get the reference of the emoji key and thereby was able to disable the next key which is hideKeyboard key.
function disableKey(parent, objectText)
{
var obj = null
if (parent === null)
return null
var children = parent.children
for (var i = 0; i < children.length; i++) {
obj = children[i]
if (obj.text === objectText && obj.toString().substring(0, 7) === "BaseKey") {
console.log("Disabling symbols. " + obj.text)
obj.enabled = false
}
else if(obj.displayText === "HWR"){
console.log("Disabling Handwriting mode button." + obj.displayText + " " + objectText)
obj.visible = false
}
else if(obj.text === ":-)" && obj.toString().substring(0, 7) === "BaseKey"){
console.log("Disabling hidekeyboard key." + obj.text)
children[i+1].visible = false
}
obj = disableKey(obj, objectText)
if (obj)
break
}
return obj
}
Related
I'm using this example: QML Flipable Example.
I created rectangles with GridLayout. I just added my new state the example:
states: [
State {
name: 'back'
PropertyChanges { target: rotation; angle: 180 }
when: flipable.flipped
},
State {
name: 'remove'
PropertyChanges {
target: card
visible: false
}
}
]
I wanted when I click the rectangles, check rectangles, they open and if they are same or not. My algorithm for this job:
property int card1: -1
property int card2: -1
property int remaining: 20
function cardClicked(index) {
var card = repeater.itemAt(index); // Selected card
if (!card.flipped) { // If selected card is not opened
card.flipped = true; // Open card
if (card1 === -1) { // If card is first selected card
card1 = index; // Set first selected card
} else { // If selected card is not first card, I mean that is second card because first card is already selected
card2 = index; // Set second card
area.enabled = false; // Disabled GridLayout (area)
delayTimer.start(); // Start `Timer` QML component
}
} else { // If card is opened so, close that card, because that card is opened and player clicked its
card.flipped = false; // Close that card
card1 = -1; // first card is not selected
}
}
function validateCards() {
var state = ''; // Default state: Close cards
if (imageIndexes[card1] === imageIndexes[card2]) { // If first card and second card are equal, you found same cards :)
state = 'remove'; // You found cards so, remove cards
--remaining; // If this equals 0, you found all cards
}
// If cards are not same, close cards but if they are same remove them
repeater.itemAt(card1).state = state;
repeater.itemAt(card2).state = state;
card1 = -1; // first card is not selected
card2 = -1; // second card is not selected
area.enabled = true; // Enabled area (GridLayout)
if (remaining === 0) { // If you found all cards, game over
console.log('Game Over!');
}
}
I added MouseArea into Rectangles:
MouseArea {
anchors.fill: parent
onClicked: cardClicked(index) // This index belongs to my `Repeater`.
}
I put a Timer for animation to work correctly and check cards:
Timer {
id: delayTimer
interval: 1000
onTriggered: validateCards()
}
This example and animations are running nice but sometimes they aren't running correctly:
How can I solve this animation bug?
UPDATE!
You can find all source code on here.
I think you are complicating the application by using the timer since you do not know if the item finished changing its position, it would be appropriate to execute that task when it finishes turning the letter over.
Another error that I see in your code is that you are assigning a state = "".
I have modified in the following parts:
GameArea.qml
GridLayout {
[...]
property variant imageIndexes: GameUtils.generateCardIndexes(
imageCount, repeatCount)
property int lastIndex : -1
Repeater {
id: repeater
model: 40
Card {
id: card
backImageSource: 'qrc:/images/img_' + area.imageIndexes[index] + '.jpg'
onFinished: verify(index)
}
}
function verify(index){
if(lastIndex == -1){
lastIndex = index
return
}
area.enabled = false
var lastItem = repeater.itemAt(lastIndex)
var currentItem = repeater.itemAt(index)
if(lastItem.backImageSource === currentItem.backImageSource){
lastItem.state = "remove"
currentItem.state = "remove"
}
else{
lastItem.flipped = false
currentItem.flipped = false
}
if(repeater.model === 0){
console.log("Winning")
}
lastIndex = -1
area.enabled = true
}
}
Card.qml
Item {
[...]
property alias state: flipable.state
signal finished()
Flipable {
[...]
transitions: Transition {
NumberAnimation { target: rotation; property: 'angle'; duration: 400 }
onRunningChanged: {
if ((state == "back") && (!running))
finished()
}
}
MouseArea {
anchors.fill: parent
onClicked: card.flipped = true
}
}
}
The complete code can be found at the following link
I have this working Plunker Example, you can drag the mouse and select the cards.
My problem is that the code has alot of bugs and I want to do
something similar to this Demo
Here something that I want to fix (img1 and img2 - the cards in img #2 were clicked before), another thing is that if you drag the mouse fast sometimes the cards will not be select.
Here is my component code
export class App {
private dragStart:number = 0;
private dragOver:number = 0;
public users:Array<{id?: number; name: string; admin: boolean;}> = [
{ name: 'Alexis Wursten', admin: false },
{ name: 'Janco Boscan', admin: true },
{ name: 'Noemi Iturralde', admin: false },
];
public added: string[] = [];
x1 = 0; y1 = 0; x2 = 0; y2 = 0;
#ViewChild('selector') selector: ElementRef;
constructor(private renderer: Renderer2) {
}
isRectangeVisible = false;
isMouseDown = false;
#HostListener('mousedown', ['$event'])
onMouseDown(ev) {
this.dragStart = ev.clientY;
this.isMouseDown = true;
}
#HostListener('document:mouseup', ['$event'])
onMouseUp(ev) {
this.dragStart = 0;
this.dragOver = 0;
this.renderer.setStyle(this.selector.nativeElement, 'display', 'none');
this.isRectangeVisible = false;
this.isMouseDown = false;
}
#HostListener('document:mousemove', ['$event'])
onMouseMove(ev) {
if(!this.isRectangeVisible && this.isMouseDown){
this.renderer.setStyle(this.selector.nativeElement, 'display', 'block');
this.x1 = ev.clientX;
this.y1 = ev.clientY;
this.isRectangeVisible = true;
}
this.x2 = ev.clientX;
this.y2 = ev.clientY;
this.reCalc();
}
reCalc() {
const x3 = Math.min(this.x1, this.x2);
const x4 = Math.max(this.x1, this.x2);
const y3 = Math.min(this.y1, this.y2);
const y4 = Math.max(this.y1, this.y2);
this.renderer.setStyle(this.selector.nativeElement, 'left', x3 + 'px');
this.renderer.setStyle(this.selector.nativeElement, 'top', y3 + 'px');
this.renderer.setStyle(this.selector.nativeElement, 'width', x4 - x3 + 'px');
this.renderer.setStyle(this.selector.nativeElement, 'height', y4 - y3 + 'px');
}
onSelecUser(item) {
if(this.added.indexOf(item.name)===-1) { // or compare by id
this.added = this.added.concat([item.name]);
}
else {
this.added = this.added.filter((x) => item.name!==x); // or compare by id
}
item.selected = !item.selected ? true : false;
}
onMouseOver(ev, item) {
if(ev.which!==1) {
return false;
}
ev.preventDefault();
if(ev.type==='mouseenter' && !item.selected) {
this.dragOver = ev.clientY - this.dragStart > 0 ? 1:-1;
this.onSelecUser(item);
return false;
}
if(ev.type==='mouseleave') {
if(this.dragOver===1 && ev.clientY < ev.target.offsetTop && item.selected) {
console.log('desel...', item);
this.onSelecUser(item);
return false;
}
if(this.dragOver===-1 && ev.clientY > ev.target.offsetTop && item.selected) {
console.log('desel...', item);
this.onSelecUser(item);
return false;
}
}
}
}
Thanks for read.
UPDATE #1:
https://plnkr.co/edit/d9aTb0E0OKFfTSIAM0MY?p=preview
Added option to select/unselect a user by a click.
For that, no reset code needed.
#HostListener('mousedown', ['$event'])
onMouseDown(ev) {
this.dragStart = ev.clientY;
this.isMouseDown = true;
}
Only div's click handler changed.
(click)="onSelecPersona(user, !user.selected)"
INITIAL ANSWER:
Here is modified code: https://plnkr.co/edit/QryFWtLQwNuGkrtzDehm?p=preview
It solves few issues:
(1) HTML selection: the "user-select" CSS should be on the "row" no on the "card" because the selection starts at the "row" boundaries
.row {
user-select: none;
-moz-user-select: none;
}
.card-content {
padding: 0;
}
(2) Handling of selected divs: initial implementation relies on mouse events on the user's div. That does not handle the case when "selector" rectangle never crosses "user" div's boundaries (i.e. goes around but still within selection boundaries).
My implementation calculates an overlap of a "selector" and "user" divs to determine if a user selected.
<div class="card"
#ucard
[attr.id]="user.name"
[class.selected]="user.selected"
*ngFor="let user of users"
(click)="onSelecPersona(user, !user.selected)"
>
import {Component, NgModule, HostListener, Renderer2, ElementRef, ViewChild, ViewChildren } from '#angular/core'
...
#ViewChildren('ucard') components: QueryList<ElementRef>;
...
// return true if two HTML elements overlap
overlap(e1:ElementRef, e2:ElementRef){
var rect1 = e1.getBoundingClientRect();
var rect2 = e2.getBoundingClientRect();
return !(
rect1.top > rect2.bottom ||
rect1.right < rect2.left ||
rect1.bottom < rect2.top ||
rect1.left > rect2.right
);
}
// updates user selection based on the current "selector"
markSelected(){
this.components.forEach(it=> {
var overlaps: boolean = this.overlap(this.selector.nativeElement, it.nativeElement);
this.onSelecPersona(this.users.find(u=> u.name == it.nativeElement.id), overlaps);
});
}
I have a treeview from which I drag items to a drop area by creating draggable items dynamically. Everything works except I cannot pass mimedata
function createItem(parentItem)
{
if (itemComponent.status === Component.Ready && draggedItem == null)
{
draggedItem = itemComponent.createObject(
parentItem,
{
"x": positionInParent.x,
"y": positionInParent.y,
"baseColor":Qt.lighter("red", 1.5),
"copyable":false,
visible: true
}
);
draggedItem.Drag.mimeData = { "text/plain": "Hello!" };
draggedItem.Drag.supportedActions = Qt.CopyAction;
draggedItem.Drag.dragType = Drag.Automatic;
draggedItem.Drag.start();
}
else if (itemComponent.status === Component.Error) {
draggedItem = null;
console.log("error creating component");
console.log(itemComponent.errorString());
}
}
when I try to get the mime data from drop event (drop.text field) it is empty.
DropArea
{
height:parent.height
width:parent.width
onDropped: {
drop.acceptProposedAction()
var txt = drop.text; // Empty!!!
console.log("dropped into dest")
}
onEntered: {
console.log("entered dest")
}
}
Resolved it myself! Just to add
draggedItem.Drag.active = true;
I've been learning QtQuick for about a week and I'm facing a weird behaviour on what I'm trying to achieve. I would like to make a vertical ListView with a Keyboard navigation so that when I press UP or DOWN, the items move up or down and if an item goes in or out of the "viewport", its opacity property will change smoothly to 0 or 1.
Here is my current QML code:
import QtQuick 2.4
Rectangle {
width:200
height:400
ListView {
property int activePosition:1
property int itemDisplayed:3
width:parent.width-50
height:parent.height-50
anchors.centerIn:parent
model:10
snapMode:ListView.SnapToItem
focus:true
cacheBuffer:2000
Component.onCompleted: {
console.log(count+' != '+contentItem.children.length+' ???')
}
Keys.onPressed: {
var i = 0
console.log('pos='+activePosition)
console.log(count+' != '+contentItem.children.length+' ???')
if (event.key === Qt.Key_Up) {
if (activePosition == 1 && currentIndex > 0) {
i = currentIndex+itemDisplayed-1
if (i < contentItem.children.length - 2/* why -2 instead of -1 ??? */) {
console.log('out='+i)
contentItem.children[i].state = 'out'
}
}
activePosition = activePosition > 1 ? activePosition - 1 : activePosition
}
if (event.key === Qt.Key_Down) {
if (activePosition == itemDisplayed && currentIndex < contentItem.children.length - 2) {
i = currentIndex-itemDisplayed+1
if (i >= 0) {
console.log('out='+i)
contentItem.children[i].state = 'out'
}
}
activePosition = activePosition < itemDisplayed ? activePosition + 1 : activePosition
}
}
delegate: Rectangle {
id:rect
state:index < ListView.view.itemDisplayed ? 'in' : 'out'
opacity:1.0
width:ListView.view.width
height:ListView.view.height/ListView.view.itemDisplayed
border.color:'white'
border.width:1
color:activeFocus ? 'red': 'gray'
onActiveFocusChanged: {
if (activeFocus) {
state = 'in'
console.log('in='+index)
}
}
states: [
State { name:'in'; PropertyChanges { target:rect; opacity:1.0 } },
State { name:'out'; PropertyChanges { target:rect; opacity:0.0 } }
]
transitions: [
Transition {
to:'in'
NumberAnimation { property:'opacity'; duration:250 }
},
Transition {
to:'out'
NumberAnimation { property:'opacity'; duration:250 }
}
]
Text {
text:index
anchors.centerIn:parent
}
}
}
}
First question : model=10, why model.count is not equal to contentItem.children.length? onCompleted gives 5 vs 11 and during navigation 10 vs 11
Second question: If I press UP or DOWN, it works fine until I reach index=4, why?
As I'm a beginner on QtQuick so maybe it's not the right approach. I tried to use the visible property but every item has visible = true even if they are outside. I tried also indexAt() with no success.
Any help would be great :-)
Now I know better about the ListView behavior. My previous code can be fixed by removing the Keys.onPressed event which is no longer useful, and by using the itemAt() method directly into the onActiveFocusChanged handler.
I try to handle a set of actions for multiple items selection on a ListView. The context: a RSS reader. An article can be read/unread, marked/unmarked, published/unpublished. Currently, my code adds every possible action when the multi-selection is started by the user: mark as read, keep unread, … So mark as read is available even if every selected article is already marked as read.
I'm trying to hide irrelevant actions. There's an example in the documentation to switch between multiple selection handlers, with predefined action lists. I would need to create an handler for every possibility, so 8 handlers. That's clearly not the solution.
I tried to call MultiSelectionHandler::removeAllActions() every time my selection changes and MultiSelectionHandler::addAction() to add back needed actions. This is the relevant part of my current code:
ListView {
dataModel: _articleModel
id: listView
multiSelectAction: MultiSelectActionItem {
}
multiSelectHandler {
status: qsTr("None selected")
actions: []
attachedObjects: [
ActionItem {
id: actionMultiRead
title: qsTr("Mark as read")
imageSource: "asset:///images/mark_as_read.png"
onTriggered: {
var selectionList = listView.selectionList();
listView.clearSelection();
for (var i = 0; i < selectionList.length; ++i)
_articleModel.data(selectionList[i]).unread = false;
}
},
ActionItem {
id: actionMultiUnread
title: qsTr("Keep unread")
imageSource: "asset:///images/keep_unread.png"
onTriggered: {
var selectionList = listView.selectionList();
listView.clearSelection();
for (var i = 0; i < selectionList.length; ++ i)
_articleModel.data(selectionList[i]).unread = true;
}
},
ActionItem {
id: actionMultiPublish
title: qsTr("Publish")
imageSource: "asset:///images/publish.png"
onTriggered: {
var selectionList = listView.selectionList();
listView.clearSelection();
for (var i = 0; i < selectionList.length; ++ i)
_articleModel.data(selectionList[i]).published = true;
}
},
ActionItem {
id: actionMultiUnpublish
title: qsTr("Unpublish")
imageSource: "asset:///images/unpublish.png"
onTriggered: {
var selectionList = listView.selectionList();
listView.clearSelection();
for (var i = 0; i < selectionList.length; ++ i)
_articleModel.data(selectionList[i]).published = false;
}
},
ActionItem {
id: actionMultiStar
title: qsTr("Star")
imageSource: "asset:///images/star.png"
onTriggered: {
var selectionList = listView.selectionList();
listView.clearSelection();
for (var i = 0; i < selectionList.length; ++ i)
_articleModel.data(selectionList[i]).marked = true;
}
},
ActionItem {
id: actionMultiUnstar
title: qsTr("Unstar")
imageSource: "asset:///images/unstar.png"
onTriggered: {
var selectionList = listView.selectionList();
listView.clearSelection();
for (var i = 0; i < selectionList.length; ++ i)
_articleModel.data(selectionList[i]).marked = false;
}
}
]
}
onSelectionChanged: {
if (selectionList().length > 1) {
multiSelectHandler.status = qsTr("%1 items selected").arg(selectionList().length);
} else if (selectionList().length == 1) {
multiSelectHandler.status = qsTr("1 item selected");
} else {
multiSelectHandler.status = qsTr("None selected");
}
// Update available actions
multiSelectHandler.removeAllActions();
for (var i = 0; i < selectionList().length; ++ i) {
var elt = _articleModel.data(selectionList()[i]);
if (elt.marked) {
multiSelectHandler.addAction(actionMultiUnstar);
console.log("Adding unstar");
} else {
multiSelectHandler.addAction(actionMultiStar);
console.log("Adding star");
}
if (elt.published) {
multiSelectHandler.addAction(actionMultiUnpublish);
console.log("Adding unpublish");
} else {
multiSelectHandler.addAction(actionMultiPublish);
console.log("Adding publish");
}
if (elt.unread) {
multiSelectHandler.addAction(actionMultiRead);
console.log("Adding read");
} else {
multiSelectHandler.addAction(actionMultiUnread);
console.log("Adding unread");
}
}
}
}
While it could greatly be optimized and works fine until the selection changes, it doesn't work anymore after a change: MultiSelectionHandler::removeAllActions() delete the actions, they can't be added back.
Is there any way to achieve this without declaring 8 multi selection handlers?
the question is quite old, and probably you have already solved, but for any other one who stumbled up on this question my solution is to use MultiSelectionHandler::removeAction() instead of removeAllActions().
removeAllActions automatically delete the object, thus one should recreated the actions every time.
for (var i = 0; i < multiSelectAction.actionCount(); ++ i)
multiSelectAction.removeAction(multiSelectAction.actionAt(i));
Cheers.