QML: onDragStarted / finished not called even though the drag property is active - drag-and-drop

In the following example I would expect that onDragStarted / onDragFinished are called when one rectangle is dragged. However only drag.onActiveChanged (of the mouseArea) and Drag.onActiveChanged (of the rectangle are called). I get the expected output when setting Drag.dragType to Drag.Automatic but then I don't see the rectangle anymore. I am using Qt 5.5 on a Mac (El Capitan).
import QtQuick 2.5
import QtQuick.Window 2.2
Window {
visible: true
width: 100
height: 200
ListModel {
id: testModel
ListElement { name: "red"; value: "#f00" }
ListElement { name: "green"; value: "#0f0" }
ListElement { name: "blue"; value: "#00f" }
}
Component {
id: rect
Rectangle {
Drag.active: mouseArea.drag.active
Drag.hotSpot.x: width / 2
Drag.hotSpot.y: height / 2
//Drag.dragType: Drag.Automatic
Drag.onActiveChanged: {
console.log("Active changed..")
}
Drag.onDragStarted: {
console.log("Drag started..")
}
Drag.onDragFinished: {
console.log("Drag finished!")
}
MouseArea {
id: mouseArea
anchors.fill: parent
hoverEnabled: true
drag.target: parent
drag.onActiveChanged: {
console.log("Drag prop became active..")
}
onClicked: {
colorButtonClicked(buttonName, buttonColor);
}
}
width: 80
height: 20
radius: 6
color: model.value
}
}
Column {
spacing: 3
anchors.centerIn: parent
Repeater {
model: testModel
delegate: rect
}
}
}

I was puzzled about the same problem based on my assumptions from the signal names, but a look at the documentation showed that the signals will only work when using Drag.Automatic or explicitly calling startDrag.
dragStarted()
This signal is emitted when a drag is started with the startDrag() method or when it is started automatically using the dragType property.
dragFinished(DropAction action)
This signal is emitted when a drag finishes and the drag was started with the startDrag() method or started automatically using the dragType property.
The other issue seen when using Drag.Automatic appears to be fixed in Qt 5.6.1

Related

Is there a way to make an ion-modal-sheet to snap the header in Ionic V6

I'm currently developing an app using Ionic V6 and i'm figured out if it was possible to make an ion-modal-sheet snap the header when it fully swiped like the Uber eats application ?
Thanks in advance for your answers and have a nice day !
For those who wondering how to do it, here's how i managed to get something similar:
first of all i've added an id to my header (my ion-header is wrapped inside a component called header) if your header is always in the same page of where you're creating the modal, use a template reference variable instead of an id:
<header
id="getHeight"
[title]="'Solutions disponibles'"
[displayGoBack]="true"></header>
then i've created a helper to:
Get the height of the header (can be different between iOS and Android)
Get the body height
Use the body height and the header height to return the height my modal should have in px and in %
export const getContentHeight = (): any => {
const header = document.querySelector('#getHeight');
const body = document.querySelector('body');
if (body && header) {
const percent = (100 - (header.clientHeight * 100 / body.clientHeight));
return {
height: body.clientHeight - header.clientHeight,
percent: percent
}
}
}
After that i've created another helper to change the visual aspect of my modal when breakpoint reach 0.9:
export const setModalSheetContentHeight = (modal: HTMLIonModalElement, breakpoint: number, expand: boolean): any => {
if (breakpoint >= 0.9 && !expand) {
modal.classList.add('no-radius');
modal.setCurrentBreakpoint(1);
expand = true;
} else if (breakpoint <= 0.9 && expand) {
modal.classList.remove('no-radius');
expand = false;
}
return expand;
}
The boolean "expand" avoid the helper to always set the modal breakpoint to 1.
Then i've created a method that add some listeners on the modal:
initModalListener(): void {
let expand = false;
// Define if the modal is fully expand
const content = getContentHeight();
// Return an object with content height in px and in %
addEventListener('ionModalWillPresent', () => {
this.modal.setAttribute('style', `--height: ${content.height}px`);
// Set the modal height before it shown to avoid visual bug
});
addEventListener('ionBreakpointDidChange', (e: any) => {
const breakpoint = e.detail.breakpoint;
expand = setModalSheetContentHeight(this.modal, breakpoint, expand);
});
}
And after all of that i call this method inside of the method that create my modal:
private async presentModal(): Promise<void> {
this.modal = await this.modalCtrl.create({
component: YourModalComponent,
breakpoints: [0.3, 0.65, 1],
initialBreakpoint: 0.3,
backdropBreakpoint: 1,
showBackdrop: false,
canDismiss: false,
keyboardClose: true,
id: 'modalSheet',
}
});
this.initModalListener();
await this.modal.present();
}
This is a first shot so i'm sure that there is plenty of thing to adjust but for now, it work pretty well with Ionic 6.

How to resize QML TextField whenever parent form is resized

I have a form and a TextField on it. The TextField size is fixed regardless of form size. I want it to be resized whenever form is resized (specially getting bigger when form is maximized). How to achieve this (a fix margin between the two is OK).
import QtQuick 2.0
import QtQuick.Layouts 1.1
import QtQuick.Controls 1.3
import org.kde.plasma.plasmoid 2.0
import org.kde.plasma.core 2.0 as PlasmaCore
import org.kde.plasma.components 3.0 as PlasmaComponents
Item
{
property alias cfg_mainText: mainText.text
anchors.centerIn: parent
ColumnLayout
{
RowLayout
{
Label { text: "Main" }
TextField
{
id: mainText
Layout.fillWidth: true
placeholderText: "important text here"
text: ""
}
}
}
}
you have one Item that contains your TextField First of all you should set id for it and anchors, then say to your TextField that its width should be the same as Item.
import QtQuick 2.12
import QtQuick.Window 2.12
import QtQuick.Layouts 1.1
import QtQuick.Controls 2.12
Window {
id: window
width: 640
height: 480
visible: true
title: qsTr("Hello World")
Item
{
id:txtField
property alias cfg_mainText: mainText.text
anchors.fill: parent
anchors.topMargin: 240
anchors.bottomMargin: 200
anchors.leftMargin: 320
anchors.rightMargin: 91
ColumnLayout
{
RowLayout
{
Label { text: "Main" }
TextField
{
id: mainText
width: txtField.width
Layout.fillWidth: true
placeholderText: "important text here"
text: ""
}
}
}
}
}

StencilJS component with shadow dom enabled does not generate the helper CSS classes for dynamically added elements on IE11/Edge

I've created a new project using the stencil component starter. Inside my component I'm using an external JS nouislider, which injects HTML elements into my div (this.slider ref):
...
componentDidLoad() {
noUiSlider.create(this.slider, {
start: [20, 80],
range: {
'min': 0,
'max': 100
}
})
}
...
I've copied the slider's CSS into my-component.css and rewrote everything with :host selectors for the shadow dom:
:host(.my-component) .noUi-target {
position: relative;
direction: ltr
}
Everything works fine on Chrome/Firefox but the slider styles are not working on IE11/Edge because Stencil appends a helper sc-my-component class to every element that I have inside the render method and generates CSS rules like so:
.my-component.sc-my-component-h .noUi-target.sc-my-component {
position: relative;
direction: ltr
}
but the injected nouislider child HTML elements don't have the helper classes on them. I have an ugly fix for this case atm:
...
componentDidLoad() {
noUiSlider.create(this.slider, {
start: [20, 80],
range: {
'min': 0,
'max': 100
}
})
this.slider.querySelectorAll('div').forEach((child)=>{
child.classList.add('sc-my-component')
})
}
...
I'm appending the helper classes after the slider is created (the slider generates child divs only). Is there a better way to tell Stencil that I'm injecting elements inside lifecycle methods and that it needs to recognize those elements when CSS rules are being generated?
This is not an answer to your question, nevertheless this could also be interesting for you:
We are currently working on the same topic (StencilJS, shadow: true, noUiSlider) and encountered the problem, that the slider's touch events are not working correctly in shadowDOM on mobile devices. We found a solution for this and already created a PR (https://github.com/leongersen/noUiSlider/pull/1060).
I too had problems using nouislider in StencilJS but just managed to make it work.
my-slider.scss
#import '~nouislider/distribute/nouislider.css';
:host {
padding: 50px 30px;
display: block;
}
my-slider.tsx
import { Component, h, Prop, Event, EventEmitter } from '#stencil/core';
import noUiSlider from "nouislider";
#Component({
tag: 'skim-slider',
styleUrl: 'skim-slider.scss',
shadow: true
})
export class SkimSlider {
#Prop() min!: number;
#Prop() max!: number;
private slider: HTMLElement;
#Event() update: EventEmitter;
componentDidLoad() {
const slider = noUiSlider.create(this.slider, {
start: [this.min, this.max],
tooltips: [true, true],
range: {
'min': this.min,
'max': this.max
}
});
slider.on('change', (value) => this.update.emit(value));
}
render() {
return (
<div ref={e => this.slider = e}></div>
);
}
}
The trick that did it for me was 'display: block'

Nativescript Responsive #proplugin/nativescript-platform-css not triggering repaint

I am not quite sure that the title is the appropriate, because I donĀ“t know whether 'repaint' is correct. Nevertheless I think is clear what it means.
I am using the plugin '#proplugin/nativescript-platform-css' and followed Tiago's Alves excellent blog.
As I am using angular I require the plugin in the main.tns.ts
import { platformNativeScriptDynamic } from 'nativescript-angular/platform';
import { AppModule } from '#src/app/app.module';
require( '#proplugins/nativescript-platform-css' );
platformNativeScriptDynamic().bootstrapModule(AppModule);
I am dynamically changing the rows and columns depending on the device width:
HTML
<GridLayout (layoutChanged)="updateLayout($event)" horizontalAlignment="center" verticalAlignment="center" class="wrap-login100"
[class.phone]="gridLayout.isPhone" [class.tablet]="!gridLayout.isPhone"
row="1"
[rows]="gridLayout.rows"
[columns]="gridLayout.columns">
<StackLayout marginBottom="50" [col]="gridLayout.image.col" [row]="gridLayout.image.row" verticalAlignment="center" class="login100-pic">
UPDATE LAYOUT EVENT
updateLayout(value) {
this.zone.run(() => {
this.gridLayout = this.platformService.updateLayout('login');
});
}
UPDATE METHOD
updateLayout(route) {
let width;
width = screen.mainScreen.widthDIPs;
let gridLayout;
if (width < 1000) {
gridLayout = {
isPhone: true,
columns: '*',
rows: '*,2*',
image: {
row: 0,
col: 0
},
form: {
row: 1,
col: 0
}
};
} else {
gridLayout = {
isPhone: false,
columns: '*,2*',
rows: '*',
image: {
row: 0,
col: 0
},
form: {
row: 0,
col: 1
}
};
}
return gridLayout;
}
When the app started I triggered the updateLayout method and the pp will paint correctly the layout, either tablet or phone. When I change then the orientation, the updateLayout method is also triggered returning the appropriate gridLayout object but nothing happens on the screen.
As you can see in the code I tried to use the angular zone, but nothing changed.
Any ideas will be appreciated as I don't really know where to look...

How can I properly animate the removal of an item from a data model in Cascades?

When I try to animate the removal of an item from a data model, the item gets removed, but apparently because the animation I am using sets the item's dimensions to 0, the next item added to the data model is not visible. To see it, I have to reload the entire data model, or close and re-open my app. If I don't do the animation, items are removed and added correctly, just not with the effect I'm trying to achieve. My sample code is as follows:
ListView {
dataModel: GroupDataModel {
id: noteDataModel
}
listItemComponents: [
ListItemComponent {
type: "item"
StandardListItem {
id: noteItem
title: ListItemData.noteTitle
description: ListItemData.noteText
animations: [
ScaleTransition {
id: deleteAnimation
toY: 0
toX: 0
duration: 500
onEnded: {
noteItem.ListItem.view.dataModel.remove(noteItem.ListItem.view.dataModel.data(noteItem.ListItem.indexPath));
}
}
]
contextActions: [
ActionSet {
DeleteActionItem {
onTriggered: {
deleteAnimation.play();
}
}
}
]
}
The reason the issue is occurring is that the Cascades framework at times reuses objects for better performance. When you remove an item from the data model, and set its dimensions to zero, that object is held in memory for a time, with the dimension properties set to 0. The next item you add will reuse the removed item object, but with dimensions of 0, so it won't be visible.
To resolve this, change the animation's onEnded event and re-scale the item back to dimensions of 1.0 after removal:
onEnded: {
noteItem.ListItem.view.dataModel.remove(noteItem.ListItem.view.dataModel.data(noteItem.ListItem.indexPath));
noteItem.scaleX = 1.0;
noteItem.scaleY = 1.0;
}