Unable to dynamically change KDE Plasma 5 Widget Expandable FullRepresentation Component size outside of Component.onCompleted - qml

I'm coding a widget that can be expanded to its FullRepresentation by being clicked on:
onClicked: {
plasmoid.expanded = !plasmoid.expanded
}
I have a function that updates its contents and puts them into a Gridview. The update function is first called from Component.onCompleted, but then I call it every time the user updates the data. The problem is the data I feed into the expandable FullRepresentation can contain a varying number of elements that I put into the gridview and calculate its AND the FullRepresentation's sizes based on this number.
However, I find I'm only able to specify the Representation's size from the Component.onCompleted block. When I call the update function outside it, it doesn't change the size, no matter whether the size is defined in the item properties like so:
Item
{
id: fullRepresentation
width: no_items > 2 ? 465 : no_items * 155
height: Math.ceil(no_items / 3)* 155
property int no_items
...and I only try to change the no_items property from the UpdateFunction, or just try to run the two equations from it.
Is there any way around this? I've also tried the implicitHeight and implicitWidth properties. I really would like to be able to dynamically adjust the size of the main expandable representation, it feels bad to have to hardcode it and then have a lot of unfilled space.
EDIT:
Here is the requested example:
Root.qml
import org.kde.plasma.plasmoid 2.0
import QtQuick 2.2
Item
{
id: root
width: 185; height: 185
Plasmoid.compactRepresentation: Compact {}
Plasmoid.fullRepresentation: Full {}
Plasmoid.preferredRepresentation: Plasmoid.compactRepresentation
signal addItems()
}
Compact.qml
import QtQuick 2.2
Item
{
Rectangle
{
width: root.width; height: root.height
MouseArea
{
anchors.fill: parent
acceptedButtons: Qt.LeftButton | Qt.RightButton
onClicked: if (mouse.button == Qt.RightButton) root.addItems()
else plasmoid.expanded = !plasmoid.expanded
}
}
}
Full.qml
import QtQuick 2.2
Item
{
width: no_items > 2 ? 465 : no_items * 155
height: Math.ceil(no_items / 3)* 155
property int no_items : 0
function redrawGridView()
{
readingListModel.clear()
var i
for (i = 0; i < no_items; i++) readingListModel.append({})
}
function addItems()
{
no_items = 6
width = no_items > 2 ? 465 : no_items * 155
height = Math.ceil(no_items / 3)* 155
redrawGridView()
}
ListModel
{
id: readingListModel
}
GridView
{
id: readingGridView
width: count > 2 ? 465 : count * 155
height: Math.ceil(count/3) * 155
cellWidth: 155; cellHeight: 155
model: readingListModel
delegate: Rectangle
{
width: 150; height: 150;
}
}
Component.onCompleted:
{
root.addItems.connect(addItems)
no_items = 3
redrawGridView()
}
}

Well, I got no answer but I've figured this out myself.
Indeed, this doesn't seem to be possible using the plasmoid.expanded method with the Full Representation, and produces other bugs, as I mentioned in my comment.
But you can dynamically resize a Window item size from anywhere in your code, so this works just fine:
in Compact.qml
Full
{
id: fullRep
}
MouseArea
{
anchors.fill: parent
onClicked:
{
if (!fullRep.visible) fullRep.show()
else fullRep.close()
}
}
start of Full.qml
Window
{
x: 100;y: 100
width: 465;height: 195
color: theme.backgroundColor
flags: Qt.FramelessWindowHint //You can make it frameless like the expandable
//representation is when using the previous method

Related

How to rotate only the right or left part of an image

I want to simulate the closure of one page of a book.
Any suggestions on how to do that in qml?
Thanks in advance
Perhaps Flipable, in combination with the fillMode property of Image:
import QtQuick 2.0
import QtQuick.Window 2.0
Window {
id: window
width: 640
height: 480
visible: true
Image {
id: backgroundImage
source: "http://www.thebookdesigner.com/wp-content/uploads/2013/04/pages-vs-spreads.png"
anchors.centerIn: parent
Flipable {
id: flipable
anchors.fill: parent
anchors.leftMargin: parent.width / 2
property bool flipped: false
front: Image {
id: foldImage
source: backgroundImage.source
fillMode: Image.Pad
width: foldImage.implicitWidth / 2
horizontalAlignment: Image.AlignRight
}
back: Image {
source: backgroundImage.source
width: foldImage.implicitWidth / 2
fillMode: Image.Pad
horizontalAlignment: Image.AlignLeft
}
transform: Rotation {
id: rotation
origin.x: 0
origin.y: flipable.height / 2
axis.x: 0; axis.y: 1; axis.z: 0 // set axis.y to 1 to rotate around y-axis
angle: 0 // the default angle
}
states: State {
name: "back"
PropertyChanges {
target: rotation;
angle: -180
}
when: flipable.flipped
}
transitions: Transition {
NumberAnimation {
target: rotation
property: "angle"
duration: 1000
easing.type: Easing.InCubic
}
}
MouseArea {
anchors.fill: parent
onClicked: flipable.flipped = !flipable.flipped
}
}
}
}
Flipable does what its name suggests, and the fillMode property in combination with a width that is too small for the entire image allows you to "reposition" the contents of the image within the bounds of the item.
So, the front item contains the right side of the image, and the back item contains the left side. With an actual book that has many pages, you'd have to use the relevant pages instead of the same one.

(How) can i access relative position of qml element to main window

I have a qml element and want to show a (own) tooltip element as a new window right above this element. for this i need the absolute screen position to place the new window (AFAIK).
i got as far that the regular approach is to use "mapToItem" to get the relative position, but i cannot get to the "main window" - because the element in question is located within a "Loader" (which in this case is again located in another Loader).
So my question is: Is it possible to access the mainWindow from inside the dynamically loaded component, or is there maybe another easier way to anchor a new (tooltip) window right above an element ?
EDIT
mapToGlobal would probably work too, but i have to use qt 5.6.
i finally got it to work by setting the main window as a context property in c++:
this->qmlEngine->rootContext()->setContextProperty("mainWindow", this->root);
and in qml i can then access the main window position (on screen) and add the relative position the item has to the shown window like that:
tooltipWindow.setX(mainWindow.x +item1.mapToItem(item2,0,0).x )
The Window item has contentItem especially for that
[read-only] contentItem : Item
The invisible root item of the scene.
So you can refer to Window.contentItem as if it was Window:
import QtQuick 2.7
import QtQuick.Window 2.2
Window {
id: mainWindow
visible: true
width: 600
height: 300
Component {
id: testElement
Rectangle {
id: rect
width: 100
height: 100
color: "orange"
border { width: 1; color: "#999" }
MouseArea {
anchors.fill: parent
hoverEnabled: true
onEntered: tooltip.show(true);
onExited: tooltip.show(false);
onPositionChanged: tooltip.setPosition(mapToItem(mainWindow.contentItem,mouse.x, mouse.y));
}
}
}
Item {
x: 40
y: 50
Item {
x: 80
y: 60
Loader {
sourceComponent: testElement
}
}
}
Rectangle {
id: tooltip
visible: false
width: 100
height: 20
color: "lightgreen"
border { width: 1; color: "#999" }
Text {
anchors.centerIn: parent
text: "I'm here"
}
function show(isShow) {
tooltip.visible = isShow;
}
function setPosition(point) {
tooltip.x = point.x - tooltip.width / 2;
tooltip.y = point.y - tooltip.height;
}
}
}
As for me I would reparent tooltip Item to hovered item itself at MouseArea.onEntered and so you can avoid position recalculation etc.:
onEntered: tooltip.show(true, rect);
onExited: tooltip.show(false);
onPositionChanged: tooltip.setPosition(mouse.x, mouse.y);
...
function show(isShow, obj) {
obj = (typeof obj !== 'undefined' ? obj : null);
if(obj !== null) {
tooltip.parent = obj;
}
tooltip.visible = isShow;
}
function setPosition(x, y) {
tooltip.x = x - tooltip.width / 2;
tooltip.y = y - tooltip.height;
}

Change calendar style on button click

I need to change the Calendar style when clicking a Button. Currently, in the code below, the style change only works when the object is created for the first time but I need to do style change manually whenever the Button is clicked.
Below is the QML code:
import QtQuick 2.0
import QtQuick 2.2
import QtQuick.Controls 1.2
import QtQuick.Controls.Private 1.0
import QtQuick.Controls.Styles 1.1
ApplicationWindow {
visible: true
width: 640
height: 400
minimumWidth: 400
minimumHeight: 300
color: "#f4f4f4"
id: root
Calendar {
id: cal_panel
anchors.topMargin: 10
anchors.horizontalCenter: parent.horizontalCenter;
frameVisible:false
style: CalendarStyle {
gridVisible: false
dayDelegate: Rectangle {
color: styleData.selected ? "#FF2E7BD2" : (styleData.visibleMonth && styleData.valid ? "#191919" : "#191919");
Text {
id:day_txt
text: styleData.date.getDate()
font.bold: true
anchors.centerIn: parent
color: {
var color = "#dddddd";
if (styleData.valid) {
color = styleData.visibleMonth ? "#bbb" : "#444";
var sel = root.getHiglightDates();
for(var i=0;i<sel.length;i++){
if(sel[i]===Qt.formatDateTime(styleData.date,"dd:MM:yyyy"))
color="red"
}
if (styleData.selected) {
color = "black";
}
}
color;
}
}
}
}
}
Button{
anchors.top:cal_panel.bottom
anchors.topMargin: 10
anchors.horizontalCenter: parent.horizontalCenter
text:"Higlight"
onClicked: {
console.log("Higlight here....")
}
}
function getHighlightDates(){
var sel = ["10:11:2015","12:11:2015","11:11:2015","08:11:2015","09:11:2015"];
return sel;
}
}
Edit:
The return value of the function getHighlightDates() changes each time. In the snippet above I've just returned a predefined array for testing. In that case I am conduced how to edit style element which is already created.
Here is the screen shot:
As a simple solution, you can reassign the style on click event, forcing an under the hood refresh of the Calendar item.
To do that you can use
cal_panel.style=cal_panel.style
Be aware that this solution is not exactly performance friendly. :-)
Based on the comments in the question and in #folibis's answer, it looks the question might just revolve around how to get the calendar style to reflect the updated list of selected dates (from getHiglightDates()) after a user has updated the list by clicking a button.
What about just adding a new property selectedDates to store the selected dates (previously held in getHighlightDates()) like in the code below. By making use of property binding, the appearance of selected dates will automatically be updated whenever selectedDates changes. In the code below, the color of the "day_txt" Text is updated when selectedData is updated (which in turn is updated when selectedDates is updated).
import QtQuick 2.0
import QtQuick 2.2
import QtQuick.Controls 1.2
import QtQuick.Controls.Styles 1.1
ApplicationWindow {
visible: true
width: 640
height: 400
minimumWidth: 400
minimumHeight: 300
color: "#f4f4f4"
id: root
property variant selectedDates : ["10:11:2015","12:11:2015","11:11:2015","08:11:2015","09:11:2015"]
Calendar {
id: cal_panel
anchors.topMargin: 10
anchors.horizontalCenter: parent.horizontalCenter;
frameVisible:false
style: CalendarStyle {
gridVisible: false
dayDelegate: Rectangle {
property bool selectedDate: selectedDates.indexOf(Qt.formatDateTime(styleData.date,"dd:MM:yyyy")) > -1
color: styleData.selected ? "#FF2E7BD2" : (styleData.visibleMonth && styleData.valid ? "#191919" : "#191919");
Text {
id:day_txt
text: styleData.date.getDate()
font.bold: true
anchors.centerIn: parent
color: selectedDate ? "red" : (styleData.selected ? "black" : (styleData.visibleMonth ? "#bbb" : "#444"));
}
}
}
}
Button{
anchors.top:cal_panel.bottom
anchors.topMargin: 10
anchors.horizontalCenter: parent.horizontalCenter
text:"Higlight"
onClicked: {
var updatedDates = selectedDates
updatedDates.push(Qt.formatDateTime(cal_panel.selectedDate,"dd:MM:yyyy"))
selectedDates = updatedDates
# See http://stackoverflow.com/questions/19583234/qml-binding-to-an-array-element for why its done this way...
}
}
}
as #skypjack already suggested, you just can assign a new style on click. The style property is a Component so there is no problem to do something like this:
Component {
id: style1
CalendarStyle {
background: Rectangle { color: "lightyellow" }
}
}
Component {
id: style2
CalendarStyle {
background: Rectangle { color: "orange" }
}
}
Calendar {
id: calendar
anchors.fill: parent
style: style1
onClicked: {
calendar.style = style2;
}
}

Why does this QML code have poor performance?

If QML rendering is hardware accelerated, shouldn't this simple example outperform the equivalent implementation in Qt classic?
import QtQuick 2.3
import QtQuick.Controls 1.2
ApplicationWindow {
id: app
visible: true
width: 640
height: 480
Column {
id: cc
property real val: 0
Row {
spacing: 10
TextField {
id: numRows
text: "1"
property int value: parseInt(text);
validator: IntValidator { bottom: 1; top: 100; }
}
TextField {
id: numCols
text: "1"
property int value: parseInt(text);
validator: IntValidator { bottom: 1; top: 100; }
}
}
Repeater {
model: numRows.value
Row {
Repeater {
model: numCols.value
Slider {
width: app.width / numCols.value
height: 18.5
value: cc.val
onValueChanged: cc.val = value
}
}
}
}
}
}
I idea is to fill the screen with rows and columns of sliders, and have each slider connect to every other slider. For my screen I use 55 rows and 20 columns. As I move a slider, I expect to see a fluid motion of all the sliders on the screen, yet the frame rate is very low (I would guess 5 to 10 fps). I have a very beefy GPU, and I was expecting much better performance. What could be wrong?
Looking at this under a (CPU, not QML) profiler, the majority of the time spent is actually in rendering the Slider items using native styling. If performance is an important issue, and native desktop styling is not, I would suggest not using QtQuickControls 1, as the styling has a significant amount of overhead.
If I update your example to use QtQuickControls 2, it is significantly smoother:
import QtQuick 2.3
import QtQuick.Controls 2.0
ApplicationWindow {
id: app
visible: true
width: 640
height: 480
Column {
id: cc
property real val: 0
Row {
spacing: 10
TextField {
id: numRows
text: "1"
property int value: parseInt(text);
validator: IntValidator { bottom: 1; top: 100; }
}
TextField {
id: numCols
text: "1"
property int value: parseInt(text);
validator: IntValidator { bottom: 1; top: 100; }
}
}
Repeater {
model: numRows.value
Row {
Repeater {
model: numCols.value
Slider {
width: app.width / numCols.value
height: 18.5
value: cc.val
onPositionChanged: cc.val = position
}
}
}
}
}
}
If you get yourself 1100 sliders, you are asking to continuously update 1100 sliders. That is a lot of signals being sent. I will not ask you what you need 1100 synchronized sliders for ...
Although you can inch out a few cycles by only updating cc.val when the slider is pressed, this will not change very much.
All in all, you can only seriously reduce the work being done by choosing a stepSize or setting updateValueWhileDragging: false. You will still see a delay when you release a slider, but the frame rate will not disturb the experience as much.

Can child elements evenly fill Grid?

I'm wondering if it is possible to create a grid of elements in qml which completely and evenly fill the width and height of the grid/parent:
Grid {
columns: 2
Repeater {
model: 4
Rectangle {
width: /* ??? */
height: / * ??? */
}
}
}
I think a have to set absolute values here. Using anchors.fill: parent won't work, as well as something like width: parent.width / 2.
I think you forgot to set parent's (Grid element) anchors. Also you can always reference an element by it' id.
Grid {
anchors.fill: parent
columns: 2
rows: 3
Repeater {
model: 6
Rectangle {
width: parent.width / parent.columns
height: parent.height / parent.rows
}
}
}