I've got a button style, and I need to add shadow to the text using this style. What I am trying to do is:
struct A: ButtonStyle {
func makeBody(configuration: Configuration) -> some View {
configuration
.label
.shadow()
}
}
But the shadow is not there for some reason, am I doing it wrong?
At the least, you're missing a radius parameter. But, even then, it'll be subtle. If you give it some more information, you can make it more obvious and then adjust it to fit your needs:
.shadow(color: .pink, radius: 4, x: 5, y: 5)
You missed shadow colour and radius
struct FilledButton: ButtonStyle {
func makeBody(configuration: Configuration) -> some View {
configuration
.label
.shadow(color: .green, radius: 1
, x: 1.5, y: 1.5)
}
}
Then, Use it in your body
var body: some View {
Button("Button 1") {}
.buttonStyle(FilledButton())
}
Here is output
Related
I have a Component for an sddm theme. At the moment I use the theme dark sugar as the base theme. The component looks like the following:
Item {
id: hexagon
property color color:"yellow"
property int radius: 30
//layer.enabled: true
//layer.samples: 8
Shape {
//... Here some Positioning and other Stuff
ShapePath {
//... Here some Options and Pathlines
}
}
}
This works fine, but as soon as I uncomment both layer settings the component disappears. Does this happen, because I load the component like this:
Pane {
...
Item {
...
MyComponent {
z: 1
}
}
}
Nor the Pane or the Item use layer but most Components in the Item use the z: 1 property.
As iam_peter says, the default width and height properties of any Item are 0, and layer.enabled sets the size of the offscreen texture to the item size. By default, the scene graph doesn't do any clipping: a child item can populate scene graph nodes outside its parent's bounds. But when you confine the children's rendering to a specific offscreen texture, anything that doesn't fit is clipped. Here's a more interactive example to play with this:
import QtQuick
import QtQuick.Controls
Rectangle {
width: 640
height: 480
Column {
CheckBox {
id: cbLE
text: "layer enabled"
}
Row {
spacing: 6
TextField {
id: widthField
text: layerItem.width
onEditingFinished: layerItem.width = text
}
Label {
text: "x"
anchors.verticalCenter: parent.verticalCenter
}
TextField {
id: heightField
text: layerItem.height
onEditingFinished: layerItem.height = text
}
}
}
Rectangle {
id: layerItem
x: 100; y: 100
border.color: "black"; border.width: 2
layer.enabled: cbLE.checked
Rectangle {
width: 100
height: 100
color: "tomato"
opacity: 0.5
}
Text {
text: "this text will get clipped even when layer size is defined"
}
}
}
You can use renderdoc to see how the rendering is done; for example you can see the texture that is created by enabling the layer.
This is a small reproducible example:
import QtQuick
Window {
width: 640
height: 480
visible: true
title: qsTr("Hello World")
Item {
//width: 200
//height: 200
//layer.enabled: true
Rectangle {
width: 100
height: 100
color: "red"
}
}
}
I suspect that if you don't set a size on the Item on which you want to enable the layer (layer.enabled: true), it will have a size of 0. Hence the offscreen buffer has a size of 0.
As a side note, this works without layer, because the clip property of an Item by default is set to false. So it won't clip to the bounds of its parent.
From this discussion on StackOverflow, I am well able to save an image out of a QML item into a file as png/jpeg.
How can I overlay or merge two different qml layers & merge them into one, to save it into a png/jpeg ?
Note: I am able to save a single QQuickItem. Just need to know how to overlay 2 QQuickItems
Just have the two qml objects be children of a root Item and then grab that root item, it will capture all its content.
Just make sure the root item is big enough to enclose the children, and the children are not in negative space, because it will only capture what's inside of the footprint of the root item.
You can also do manual composition, from C++ or even QML.
The problem described in your comment is you can't move stuff around, so what can you do? Instead of having the original QML objects as parents of the same root, you can have two Image elements, then you capture item A and set the capture result to serve as a source of image A, then do the same for item B, and finally, you capture the root item, which will capture the two images together.
OK, here is a quick example, it looks a little complicated, because grabs are async, and you have to wait for the individual grab results to be completed before you can grab the "final" root item, thus the usage of the timer. In this example, different items are laid out in a row, but you can compose them any way that you like:
ApplicationWindow {
id: window
visible: true
width: 640
height: 480
Rectangle {
id: s1
visible: false
width: 200
height: 200
color: "red"
}
Rectangle {
id: s2
visible: false
width: 200
height: 200
color: "blue"
}
Row {
id: joiner
visible: false
Image { id: t1 }
Image { id: t2 }
}
Image {
id: result
y: 200
}
Timer {
id: finish
interval: 10
onTriggered: joiner.grabToImage(function(res) {result.source = res.url})
}
Component.onCompleted: {
s1.grabToImage(function(res) {t1.source = res.url})
s2.grabToImage(function(res) {t2.source = res.url; finish.start() })
}
}
First the two rectangles are captured and used as sources for the images in joiner, then joiner is captured and displayed in the result image, all objects except the final result image are hidden.
Even easier, you can use this nifty little helper to quickly join any number of items in a single image:
Item {
id: joinHelper
visible: false
property Component ic: Image { }
property var cb: null
Row { id: joiner }
Timer {
id: finish
interval: 100
onTriggered: joiner.grabToImage(joinHelper.cb)
}
function join(callback) {
if (arguments.length < 2) return // no items were passed
var i
if (joiner.children.length) { // clean previous captures
for (i = 0; i < joiner.children.length; ++i) {
joiner.children[i].destroy()
}
}
cb = callback // set callback for later
for (i = 1; i < arguments.length; ++i) { // for every item passed
var img = ic.createObject(joiner) // create empty image
// need to capture img by "value" because of JS scoping rules
// otherwise you end up with only one image - the final one
arguments[i].grabToImage(function(temp){ return function(res){temp.source = res.url}}(img))
}
finish.start() // trigger the finishing step
}
}
And you use it like this:
joinHelper.join(function(res) { result.source = res.url }, s1, s2)
It still uses a row, but you can easily tweak it to do your own layouting. It works by passing the final callback and all items you want to capture, internally it creates an image for every item, puts them in the container, and then triggers the finishing timer.
Note that depending on how fast the system is and how complex the items are and what their count is, you may need to up the timer interval, because the final callback needs to be executed only after all captures were completed, the image sources were assigned and the images were resized to give the row its proper dimensions.
I also annotated most things to make it easier to understand.
This question seems to be closely related to this one
The solution is to render the Items in question into an texture of a second item, that you don't need to render to the screen. You can compose this Item as you want by adding multiple ShaderEffectSources as children, position them relatively to each other as you like, and set their sources to the Items you want to grab to Image.
Then you grab the Item to Image.
A generic example, that exposes a function to grab a list of Items to one Image, where each Item is stacked ontop of each other, with an opacity of 0.2 each:
import QtQuick 2.0
Rectangle {
id: root
visible: false
color: 'white'
function grabMultipleToImage(url, objects) {
imgRep.url = url
width = Math.max.apply(root, objects.map(function(e) { return e.width }))
height = Math.max.apply(root, objects.map(function(e) { return e.height }))
imgRep.ready = 0
imgRep.model = objects
}
Repeater {
id: imgRep
onReadyChanged: {
if (ready > 0 && ready === model.length) {
console.log(root.width, root.height, imgRep.url)
root.grabToImage(function (res) { res.saveToFile(imgRep.url); model = null })
}
}
property int ready: 0
property string url
delegate: ShaderEffectSource {
sourceItem: modelData
width: modelData.width
height: modelData.height
opacity: 0.2
live: false
Component.onCompleted: { imgRep.ready++ }
}
}
}
The usage of this would be like this:
import QtQuick 2.7
import QtQuick.Controls 2.0
ApplicationWindow {
id: myWindow
visible: true
width: 600
height: 600
color: 'white'
Button {
text: 'grab'
onClicked: {
test.grabMultipleToImage('testimg.jpg', [rect1, rect2, rect3])
}
}
ImageGrabber {
id: test
}
Rectangle {
x: 100
y: 205
id: rect1
color: 'blue'
width: 10
height: 20
}
Rectangle {
x: 250
y: 12
id: rect2
color: 'green'
width: 20
height: 30
}
Rectangle {
x: 100
y: 100
id: rect3
color: 'red'
width: 100
height: 5
}
}
But if you have the need for more complex merging, you can also create this object manually and grab it when ever you want.
Without metering it, according to the note from the documentation
Note: This function will render the item to an offscreen surface and copy that surface from the GPU's memory into the CPU's memory, which can be quite costly. For "live" preview, use layers or ShaderEffectSource.
this solution should be more efficient than using Images with ItemGrabResults as sources for it keeps the stuff in the GPU memory until it is grabed and stored.
I am working on the following QML framework for music apps user-interface prototyping:
https://github.com/tiagmoraismorgado/TMM_QML_UI_UX_FRAMEWORK_WIP in the file instiationTest, i need to add a type Button1_1 and then use it to regenerate the whole gui based on combostochasticselector.
i know i have to instantiate the randomPicking() function, from the instantiationtest file, on top of the mouseArea Pressed statement of Button1_1, but i am not being able to do so.
if anyone could provide me any insight i would be pretty thankful
here is the code of the main file
(...)
Window {
id: root
width: Screen.width
height: Screen.height
visible: true
visibility: "FullScreen"
title: qsTr("instantiationTest")
color: "black"
CombosStochasticSelector {}
}
on the combostochasticselector, you can see the following:
Item {
property var model: model1
anchors.fill: parent
id: randomMIDIkeyboardSelector;
property var random: 0;
function randomSelection(min, max) {(...)}
function createMidiKeyboard(itemToBeInstantiated) {
var component = Qt.createComponent(itemToBeInstantiated)
if (component.status === Component.Ready) {
var midiKeyboard = component.createObject(randomMIDIkeyboardSelector, {model: model});
}
else if (component.status === Component.Error) {
}
}
function randomPicking() {
random = parseInt(randomSelection(1, 13));
if(random == 1) {createMidiKeyboard("./../_Combos/Combo1.qml");}
(...)
return random;
}
Component.onCompleted: {
randomPicking();
}
}
on the Button1_1 you can see the following:
import QtQuick 2.6
QMLOpenGLToggleButtonPrimitive {}
It instantiates a press released kind of button made out of Rectangle QML Primitives
kind regards
Tiago
When I tried to get children ID's slightly modifying this http://qmlbook.github.io/en/ch01/index.html example
// animate.qml
import QtQuick 2.0
Item
{
id: root
width: background.width
height: background.height
property string information: "this is root"
property int rotationStep: 45
Image {
id: background
source: "images/background.png"
}
Image {
id: pole
property string information: "this is pole"
x: (root.width - width)/2+2
y: root.height - height
source: "images/pole.png"
}
Image {
id: pinwheel
anchors.centerIn: parent
source: "images/pinwheel.png"
property string information: "this is pinweel"
// visible: false
Behavior on rotation {
NumberAnimation { duration: 125 }
}
}
Image {
id: blur
opacity: 0
anchors.centerIn: parent
source: "images/blur.png"
property string information: "this is blur"
// visible: false
Behavior on rotation {
NumberAnimation { duration: 125 }
}
Behavior on opacity {
NumberAnimation { duration: 125 }
}
}
// M1>>
focus: true
Keys.onLeftPressed: {
blur.opacity = 0.8
pinwheel.rotation -= root.rotationStep
blur.rotation -= root.rotationStep
}
Keys.onRightPressed: {
blur.opacity = 0.5
pinwheel.rotation += root.rotationStep
blur.rotation += root.rotationStep
}
Keys.onReleased: {
blur.opacity = 0
}
Keys.onSpacePressed:
{
for (var i=0; i<root.children.length; ++i)
console.info(root.children[i].information)
}
Keys.onDeletePressed:
{
for (var i=0; i<root.children.length; ++i)
console.info(root.children[i].id)
}
// <<M1
}
Unfortunately pressing Delete key gives me an error:
qml: undefined
qml: undefined
qml: undefined
qml: undefined
as opposed to pressing spacebar:
qml: undefined
qml: this is pole
qml: this is pinweel
qml: this is blur
Why this script returns undefined id's ?
I need to traverse some objects and be able to tell what is what - so I need to know how to traverse root tree to get id's of its childs and their object types.
Unfortunately I was unable to print the most trival id's and had to add some simple property to get it done but this means a lot of work in real life project since every object needs information property :(
So to reiterate:
Why the id's in this example are undefined?
How to traverse object tree using qml and print its id's and types ?
Id is not an ordinary object property, so it is undefined when you try to assess it through js. And qml doesn't provide operators like typeof. So you need to add type or objectname property manually. I would consider subclassing Image and adding type. Ref: How to do a "is_a", "typeof" or instanceof in QML?
Item
{
id: root
Image {
id: background
type: "image"
}
Image {
id: pole
type: "image"
}
function iter(){
for(var i = 0; i < root.children.length; ++i)
if(root.children[i].type==="image"){
//balabala
}
}
}
}
I need a way to have a gray scale image in an ImageView and on mouse moved if the cursor position is in the ImageView bounds to show a colored spotlight on the mouse position.
I have created a sample to help you understand what I need. This sample negates the colors of a colored image on the onMouseMoved event.
package javafxapplication3;
import javafx.scene.effect.BlendMode;
import javafx.scene.Group;
import javafx.scene.image.Image;
import javafx.scene.image.ImageView;
import javafx.scene.input.MouseEvent;
import javafx.scene.paint.Color;
import javafx.scene.paint.RadialGradient;
import javafx.scene.paint.Stop;
import javafx.scene.Scene;
import javafx.scene.shape.Circle;
import javafx.stage.Stage;
var spotlightX = 0.0;
var spotlightY = 0.0;
var visible = false;
var anImage = Image {
url: "{__DIR__}picture1.jpg"
}
Stage {
title: "Spotlighting"
scene: Scene {
fill: Color.WHITE
content: [
Group {
blendMode: BlendMode.EXCLUSION
content: [
ImageView {
image: anImage
onMouseMoved: function (me: MouseEvent) {
if (me.x > anImage.width - 10 or me.x < 10 or me.y > anImage.height - 10 or me.y < 10) {
visible = false;
} else {
visible = true;
}
spotlightX = me.x;
spotlightY = me.y;
}
},
Group {
id: "spotlight"
content: [
Circle {
visible: bind visible
translateX: bind spotlightX
translateY: bind spotlightY
radius: 60
fill: RadialGradient {
centerX: 0.5
centerY: 0.5
stops: [
Stop { offset: 0.1, color: Color.WHITE },
Stop { offset: 0.5, color: Color.BLACK },
]
}
}
]
}
]
},
]
},
}
To be more specific:
I want to display a colored image in grayscale mode
On mouseover I want a spotlight to be colored, in contrast with the rest of the image which is going to be rendered in grayscale mode(as mentioned in requirement no.1 above). The spotlight will move in the same direction as the mouse cursor
Here is how I would do it. Use 2 images, one colour and one grayscale. Use a clip on the grayscale. Below is a sample code
var color:ImageView = ImageView {
image: Image {
url: "{__DIR__}color.jpg"
}
}
var x:Number = 100;
var y:Number = 100;
var grayscale:ImageView = ImageView {
image: Image {
url: "{__DIR__}grayscale.jpg"
}
clip: Circle {
centerX: bind x
centerY: bind y
radius: 40
}
onMouseDragged: function (e: MouseEvent): Void {
x = e.sceneX;
y = e.sceneY;
}
}
Stage {
title: "Application title"
scene: Scene {
width: 500
height: 500
content: [
color, grayscale
]
}
}
Unfortunately I can't offer a direct answer to this, but I don't think you can achieve what you're after purely using Blend modes, hopefully someone will correct me if I'm wrong.
I would suggest exploring having both a grayscale and color version of the image, where the color image is "off-screen", then have the spotlight element refer to the portion of the color image corresponding to the same area as the on-screen grayscale image. That way it would appear as though the moveable spotlight was highlighting a portion of the grayscale image in colour. In other words, the spotlight is actually "over" the colour image, even though it's off-screen.