Targeting specific component elements in QML - javascript

CSS makes it super easy to have on hover effects for specific buttons/elements in yhour DOM. I'm trying to create a landing page of sorts for a desktop app, but I'm trying to get around having to copy paste specific states for each object I create in my main.qml.
Heres my main.qml
import QtQuick 2.9
import QtQuick.Window 2.2
import QtQuick.Controls 1.4
Window
{
visible: true
width: 640
height: 480
title: qsTr("Hello World")
NavBar
{
id: mainNavigation
}
}
NavButton.qml
Rectangle
{
id: navButton
height: parent.height
width: parent.width / 3 - 10
color: "orange"
MouseArea
{
state: "unhovered"
hoverEnabled: true
height: parent.height
width: parent.width
onHoveredChanged:
{
console.log("hovered")
if(this.state == "hovered")
{
parent.state = "unhovered"
}
else
{
parent.state = "hovered"
}
}
} // end mouse area
states:
[
State
{
name: "hovered"
PropertyChanges
{
target: navButton
width: parent.width
color: "red"
}
},
State
{
name: "unhovered"
PropertyChanges
{
target: navButton
width: parent.width
color: "orange"
}
}
]
}
NavBar.qml
import QtQuick 2.0
import QtQuick.Controls 1.4
Row
{
id: navigationBar
height: parent.height / 3
width: parent.width
spacing: 15
anchors.centerIn : parent
NavButton
{
id: selectDevice
}
NavButton
{
id: deviceConfig
}
NavButton
{
id: deviceInfo
}
}
right now when I hover over one of these navButtons all three of them expand in size, as apposed to having just the one I hovered over expand and change color. I'm hoping that like with CSS and Javascript, there's a way to target the element that is the subject of the hover event.
I've already tried used parent as the target of the states, but that doesn't seem to work. I've done a bit of hunting for a solution and I don't see anything obvious that allows me to accomplish this. the only alternative I see is to add states to each individual navButton inside my navBar file.
I really hope that isn't the case, as that would be a really verbose solution to a simple problem.
What I really need is a way to target just the element the event is occurring on.

You can simply scrap all those states and whatnot, which are really not needed for such a trivial scenario:
Rectangle {
id: navButton
height: parent.height
width: parent.width / 3 - 10
color: ma.containsMouse ? "red" : "orange"
property alias hoverEnabled: ma.hoverEnabled
MouseArea {
id: ma
anchors.fill: parent
}
}
And you save yourself a bunch of code too. Then simply have hoverEnabled: true for all buttons you want to hover.
Or you can create an auxiliary type called HoverButton.qml which is just a NavButton { hoverEnabled: true } if you want to avoid setting it for each and every button.
I would also recommend to switch the nesting order of your button:
MouseArea {
id: navButton
height: parent.height
width: parent.width / 3 - 10
Rectangle {
anchors.fill: parent
color: navButton.containsMouse ? "red" : "orange"
}
}
This avoids the need to use a property alias, saves you one id and allows you to bind to the mouse area handler hooks directly, something you cannot do in your original code without redirecting the signal with an additional handler and signal.
Finally, I don't think it will be productive to look at QML as if it is HTML, QML is not HTML, and it comes with its own usage paradigms.

Related

how to add qml file in swipeview

I was trying to use swipe view
In swipe view i need to add qml file into it
so every swipe i need to get one qml file to show
i need to get 7 qml file in 7 swipe and i need add global property in every swipe view
I try to loader to get qml file but i am not able to add global property
Because the qml file are already used in another swipe view i need to use global property and change the value
import QtQuick 2.15
import QtQuick.Controls 2.15
import QtQuick.Layouts 1.15
Page
{
id:mfcscreens
Rectangle{
height:wavescreen.height
width: wavescreen.width
SwipeView{
id:swipeview
anchors.fill:parent
currentIndex: 0
spacing: 4
Item {
id:page1
Loader{
id:mainwavescreen
source: "MfcWavefoam.qml"
}
}
Item {
id:page2
Loader{
id:leads12
source: "Leads12.qml"
Item{
property int speed: 5;
property int gain: 10;
property int xValue: 976
property int degreeValue: 143
}
}
}
Item {
id:page3
Loader{
id:leads3of1
source: "Leads3of1.qml"
}
}
Item {
id:page4
Loader{
id:leads3of2
source: "Leads3of2.qml"
}
}
Item {
id:page5
Loader{
id:leads3of3
source: "Leads3of3.qml"
}
}
Item {
id:page6
Loader{
id:leads3of4
source: "Leads3of4.qml"
}
}
Item {
id:page7
Loader{
id:leads6of1
source: "Leads6of1.qml"
}
}
Item {
id:page8
Loader{
id:leads6of2
source: "Leads6of2.qml"
}
}
}
}
}
At this stage, from what you convey in your use case, Loader isn't required. Particularly if the number of Pages in your SwipeView is static. Then, you definitely do not need to use Loader.
The following demonstrates:
A SwipeView with 3 pages
Each page in their own qml file
The parent page has a global speed, gain, xValue and degreeValue property defined which all the children Pages can view and modify
import QtQuick
import QtQuick.Controls
Page {
property int speed: 5
property int gain: 10
property int xValue: 976
property int degreeValue: 143
SwipeView {
anchors.fill: parent
MfcWaveform { }
Leads12 { }
Leads3of1 { }
}
}
// MfcWaveform.qml
import QtQuick
import QtQuick.Controls
import QtQuick.Layouts
Page {
ColumnLayout {
anchors.centerIn: parent
Text {
text: qsTr("MfcWaveform")
}
Button {
text: qsTr("Speed: %1").arg(speed)
onClicked: speed = speed + 1
}
Button {
text: qsTr("Gain: %1").arg(gain)
onClicked: gain = gain + 1
}
Button {
text: qsTr("xValue: %1").arg(xValue)
onClicked: xValue = xValue + 1
}
Button {
text: qsTr("degreeValue: %1").arg(degreeValue)
onClicked: degreeValue = degreeValue + 1
}
}
}
// Leads12.qml
import QtQuick
import QtQuick.Controls
import QtQuick.Layouts
Page {
ColumnLayout {
anchors.centerIn: parent
Text {
text: qsTr("Leads12")
}
Button {
text: qsTr("Speed: %1").arg(speed)
onClicked: speed = speed + 1
}
Button {
text: qsTr("Gain: %1").arg(gain)
onClicked: gain = gain + 1
}
Button {
text: qsTr("xValue: %1").arg(xValue)
onClicked: xValue = xValue + 1
}
Button {
text: qsTr("degreeValue: %1").arg(degreeValue)
onClicked: degreeValue = degreeValue + 1
}
}
}
// Leads3of1.qml
import QtQuick
import QtQuick.Controls
import QtQuick.Layouts
Page {
ColumnLayout {
anchors.centerIn: parent
Text {
text: qsTr("Leads3of1")
}
Button {
text: qsTr("Speed: %1").arg(speed)
onClicked: speed = speed + 1
}
Button {
text: qsTr("Gain: %1").arg(gain)
onClicked: gain = gain + 1
}
Button {
text: qsTr("xValue: %1").arg(xValue)
onClicked: xValue = xValue + 1
}
Button {
text: qsTr("degreeValue: %1").arg(degreeValue)
onClicked: degreeValue = degreeValue + 1
}
}
}
You can Try it Online!
I think, you have several problems. So start with the first one. For your SwipeView to work you should provide a PageIndicator. So add something like that below our view:
PageIndicator {
id: indicator
count: swipeView.count
currentIndex: swipeView.currentIndex
anchors.bottom: swipeView.bottom
anchors.horizontalCenter: parent.horizontalCenter
}
You don't need to use loaders for your pages. You could just add the components directly to your items:
Item {
id:page8
// if your file name is called Leads6of2.qml and is part of your project
Leads6of2 {}
}
The part with the global property is unclear to me. Please rephrase this problem!

Rotate a MUI LinearProgress

I'm trying to make a graph chart with MUI using the LinearProgress component with some styling the basic idea is to get this to rotate 90deg
const BorderLinearProgressBottom = withStyles((theme) => ({
root: {
height: 50,
borderRadius: 5,
},
colorPrimary: {
backgroundColor:
theme.palette.grey[theme.palette.type === "light" ? 200 : 700],
},
bar: {
borderRadius: 5,
backgroundColor: "#00A99E",
},
transform: [{ rotate: "90deg" }],
}))(LinearProgress);
gets my
<BorderLinearProgressBottom
variant="determinate"
value={22}
/>
to look like this
How can I get it to rotate by 90deg?
I tried putting in the BorderLinearProgressBottom transform: [{ rotate: "90deg" }], but that did not work.
Code Sandbox
Please don't use rotate(-90deg) if you want to display the LinearProgress vertically, it will break your layout because transform only scales the element visually without changing the size, so a rotated LinearProgress still occupies the space as if it's laid out horizontally. To rotate both its appearance and size, you should have a look at the implementation of Slider for reference. But I'll write it down for you now to save time.
First off you need to swap the height and width of the ProgressBar container:
// before
height: 50, // restrict the height
width: 'auto', // expand as long as you want (inside container)
// after
width: 50, // restrict the width
height: '100%', // expand as high as you want (inside container)
Then rotate the progress bar inside. This transform works because it only transforms 'locally' (the bar position is absolute inside the container).
bar: {
// the default style uses translateX, so we need to switch the axis
transform: ({ value }) => {
return `translateY(${value}%) !important`;
}
}
And that's it. You're done. Not it will look like a vertical Slider.
Live Demo

How to override global border color styles for disabled Material UI TextField?

I'm trying to override the global style for the Mui TextField components that are disabled, but I can't get the border color to change.
I've managed to change the label color if the field is disabled, but not the border color. Here is what I have so far:
export const theme = createMuiTheme({
overrides: {
// For label
MuiInputLabel: {
root: {
'&$disabled': {
color: '#000000',
},
},
},
// For border color of field (doesn't work)
MuiTextField: {
root: {
'&$disabled': {
borderColor: '#FFFFFF'
},
},
},
},
});
I've tried all kinds of variations, including,
MuiOutlinedInput: {
root: {
fieldset: {
borderColor: '#FFFFFF',
},
}
}
But this only changes the border color of non-disabled fields. What am I doing wrong here?
Here is how it looks:
The border is getting from the fieldset element. You can style if based on your needs:
MuiInputBase: {
root: {
"&$disabled": {
'& fieldset.MuiOutlinedInput-notchedOutline': {
borderColor: "blue",
background: "grey"
}
}
}
}
You can find a working example here: https://codesandbox.io/s/material-styling-disabled-textfield-ckp14?file=/demo.js
Here is a "walkthrough" for how to do it yourself:
Check the html output:
Open the image in new tab to check the marks.
As you can see - the label is actually not a parent of the fieldset tag, which is the one that draws the border.
The fieldset is inside the div.MuiInputBase, which is also disabled, so I had to add the &$disabled on the root of the MuiInputBase.
As for the & fieldset - the fieldset element is a child-element fo the MuiInputBase - so the space between the & and the tag-name means this is a child-element.
The fieldset element has the MuiOutlinedInput-notchedOutline so I used it as the relevant selector. You could probably just use .MuiOutlinedInput-root.Mui-disabled fieldset without the last-selector. Try it :-).

How to custom color text and icon in TableSortText component of Material-ui?

What I'm trying to do:
I am trying to provide the user the option to provide custom styling to my EnhancedTable component by passing in a styles object containing properties such as headCellColor, headCellBackgroundColor, bodyCellColor, bodyCellBackgroundColor etc which can be used to color the cells in TableHead and TableBody.
In the TableHead component, I use a TableSortLabel in a way similar to what they've done in this material-ui docs example: https://material-ui.com/components/tables/#sorting-amp-selecting
I wish to custom color the text and the arrow icons on hover and when active based on the props provided by the user.
Let's see the colors of TableSortLabel in different situations:
The color of the text is grey initially and there is no arrow. When mouse is hovered over it, a grey arrow appears and the text turns black. On clicking it, active state is set, the grey arrow turns black and the text turns black permanently until active state is removed.
What I've tried so far:
const useStyles = makeStyles({
tableSortLabel: props => ({
backgroundColor: "blue",
color: props.headCellColor,
fill: props.headCellColor,
"&:hover": {
backgroundColor: "blue"
}
})
});
function EnhancedTableHeadCell(props) {
const { isActive, onHoverSortState, clickHandler, ...otherProps } = props;
const classes = useStyles(props.styles);
return (
<FancyTableCell styles={props.styles} {...otherProps}>
<TableSortLabel
active={isActive}
classes={{
icon: classes.tableSortLabel,
active: classes.tableSortLabel
}}
direction={onHoverSortState}
onClick={clickHandler}
>
{props.children}
</TableSortLabel>
</FancyTableCell>
);
}
This is what it looks like in the browser:
https://i.postimg.cc/fW7W2MRB/c1.jpg
The first one is a normal header, the second is on hover and the third is when clicked (active state).
From what we can observe, the text color is totally unaffected by the color css property in all the three cases (normal, hover, active). On hover, backgroundColor only affects the icon and not the text. However, we can see that backgroundColor affects the text when it is active. Everything is going as expected with the icon. The only issue is with the text.
What could I be possible doing wrong? How can I solve my problem?
What worked for me is:
const StyledTableSortLabel = withStyles((theme: Theme) =>
createStyles({
root: {
color: 'white',
"&:hover": {
color: 'white',
},
'&$active': {
color: 'white',
},
},
active: {},
icon: {
color: 'inherit !important'
},
})
)(TableSortLabel);
You can reference the following for increasing css specificity:
https://material-ui.com/customization/components/#pseudo-classes
Solution for your problem is following:
MuiTableSortLabel: {
root: {
color: textPrimary,
// if you want to have icons visible permanently
// '& $icon': {
// opacity: 1,
// color: primaryMain
// },
"&:hover": {
color: primaryMain,
'&& $icon': {
opacity: 1,
color: primaryMain
},
},
"&$active": {
color: primaryMain,
// && instead of & is a workaround for https://github.com/cssinjs/jss/issues/1045
'&& $icon': {
opacity: 1,
color: primaryMain
},
},
},
}
This restyling I use globally via my ThemeProvider, but you can of course use it individually in your single component by using "withStyles" HOC (see "BootstrapButton" in example)
I could'nt find a proper way to do it so I came up with a temporary solution overriding the material ui css.
I added this to my global css:
.MuiTableSortLabel-root.MuiTableSortLabel-active,
.MuiTableSortLabel-root:hover,
.MuiTableSortLabel-icon {
color: inherit !important;
}
Worked for me with Mui5:
sx = {
{
'&.MuiTableSortLabel-root': {
color: 'white',
},
'&.MuiTableSortLabel-root:hover': {
color: 'blue',
},
'&.Mui-active': {
color: 'blue',
},
'& .MuiTableSortLabel-icon': {
color: 'blue !important',
},
}
}
'&.MuiTableSortLabel-root' <-- no space &.
'&.Mui-active' <-- no space &.
'& .MuiTableSortLabel-icon' <-- space

Overriding CSS styles in Material UI Stepper with CSS API

I would like to change text color (which is actually a SVG Icon) in Material UI StepIcon only for active and completed steps. At the moment, I successfully changed color of an icon for those steps. That's how my MuiTheme looks like now.
export default createMuiTheme({
overrides: {
MuiStepIcon: {
root: {
'&$active': {
color: styles.myGreen,
},
'&$completed': {
color: styles.myGreen,
},
},
}
},
});
And whole stepper looks like:
Assumings, I would like to change color of tick to gray (which represents completed steps) and color of number two to gray as well (which represents currently active step), while keeping inactive step not changed (white fill).
Changing fill property for text like in official documentation does not give any results, in developer inspector still shows fill equal white.
I want to apply that styling for whole app.
Any tips or solution for this one?
you need to override the text class too
export default createMuiTheme({
overrides: {
MuiStepIcon: {
root: {
'&$active': {
color: styles.myGreen,
},
'&$completed': {
color: styles.myGreen,
},
},
text: {
fill: <YOUR_DESIRED_COLOR>
},
},
}
},
});

Categories

Resources