Is it possible to set up background color for particular line of TextEdit (for instance when line is clicked)?
I will have TextEdit with width:500px and with 10 lines. I will click on line number 5, which is empty line, but i still want to change the background color of whole line. Is it possible?
I need to know if it is possible to develop fully customized code editor with Qt and Qml.
Here's a solution which relies on text edit's cursor rectangle:
FocusScope {
id: root
property alias font: textEdit.font
property alias text: textEdit.text
Rectangle {
color: "lightyellow"
height: textEdit.cursorRectangle.height
width: root.width
visible: root.focus
y: textEdit.cursorRectangle.y
}
TextEdit {
id: textEdit
anchors.fill: parent
focus: true
}
}
Original Answer:
Here's my proof of concept solution. It is a custom TextEdit component which highlights current line. It lacks proper line height calculation, but it can be either hard coded if only one font size is used or obtained from QFontMetrics.
import QtQuick 2.3
Item {
id: root
property alias font: textEdit.font
property alias text: textEdit.text
Column {
anchors.fill: parent
Repeater {
model: root.height / root.lineHeight
Rectangle {
color: index === textEdit.currentLine ? "lightyellow" : "transparent"
height: root.lineHeight
width: root.width
}
}
}
TextEdit {
id: textEdit
property int currentLine: text.substring(0, cursorPosition).split(/\r\n|\r|\n/).length - 1
// FIXME: Use proper line height (e.g. from QFontMetrics)
property int lineHeight: font.pixelSize + 2
anchors.fill: parent
}
}
If you want to highlight even empty lines, then you need to handle mouse clicks and keypad events and manually change colours of corresponding rectangles.
You can use cursorRectangle property of the TextEdit and FontMetrics for this purpose:
Item {
id: root
height: te.implicitHeight
FontMetrics {
id: fontMetrics
font: te.font
}
Rectangle {
x: 0; y: te.cursorRectangle.y
height: fontMetrics.height
width: te.width
color: "#e7e7e7"
visible: te.activeFocus
}
TextEdit {
id: te
anchors.left: parent.left
anchors.right: parent.right
anchors.top: parent.top
}
}
You can see the result for my commercial project:
Related
I'm trying to get a displayed number to change when a button is clicked. How would I do that?
Here is my QML code
Button {
id:button
x:232
y:250
width:18
height:18
// Makes button have a transparent background
palette {
button: "transparent"
}
Image {
anchors.fill: Button
source:"Images/image.png"
}
// Moves rectangle down, on button click
onClicked: rectangle.y-=10
}
Text{
text: qsTr("12.0")
}
I want the number 12 to increase each time the button is clicked
You have to declare a property of type int, visible for both Text and Button items.
So an example code can be:
import QtQuick 2.0
import QtQuick.Controls 2.0
Window {
id: rootWindow
visible: true
width: 300; height: 300
property int displayValue: 12
Text {
id: displayTextId
anchors.left: addOneButtonId.right
text: displayValue.toString() //more clear if you explicit the parent rootWindow.displayValue.toString()
}
Button {
id: addOneButtonId
text: "Add 1"
onClicked: {
rootWindow.displayValue += 1
}
}
}
Alternatively, the property can be declared local to the Text element (so defined inside it), but pay attention, because a property is visible only for its child.
By the way, your code is full of error. If you want to create a button, that contains an image and a text, the best way is that you create a Rectangle object and define a Mouse area inside it.
The code structure should be like:
Rectangle {
id: root
property int number: 12
width: 100; height: 50
color: "transparent"
border.width: 1
border.color: "black"
Image { id: imageId }
Text { id: textId; text: root.number.toString() }
MouseArea {
anchors.fill: parent
onClicked: {
root.y += 10 // shift the y position down
root.number += 1
}
}
}
In my application window I have two ItemPanels which is orgnized inside a RawLayout.
Item {
RowLayout {
anchors.fill: parent
spacing: 1
ItemPanel {
Layout.preferredWidth: 250
Layout.fillHeight: true
panelHeader: "Components"
DraggableItem {
width: 100; height: 100;
}
}
ItemPanel {
Layout.preferredWidth: 250
Layout.fillHeight: true
panelHeader: "Actions"
}
}
}
The Components panel contains DraggableItems. Droppable area is the Actions panel. But if I tried to drag an DraggableItem to the Actions panel, it will be hidden while I'm dragging it.
DraggableItem
Item {
id: root
property string colorKey: "red"
width: 64; height: 64
MouseArea {
id: mouseArea
width: 64; height: 64
anchors.centerIn: parent
drag.target: tile
onReleased: parent = tile.Drag.target !== null ? tile.Drag.target : root
Rectangle {
id: tile
width: 64; height: 64
anchors.verticalCenter: parent.verticalCenter
anchors.horizontalCenter: parent.horizontalCenter
color: colorKey
Drag.keys: [ colorKey ]
Drag.active: mouseArea.drag.active
Drag.hotSpot.x: 32
Drag.hotSpot.y: 32
states: State {
when: mouseArea.drag.active
ParentChange { target: tile; parent: root }
AnchorChanges { target: tile; anchors.verticalCenter: undefined; anchors.horizontalCenter: undefined }
}
}
}
}
I was thinking adding a transparent layer and change the parent to it while dragging but I have no idea how to add a new layer on top of everything. Any other workarounds for this?
The easiest way would be to change the z-value of the ItemPanel with the panelHeader: "Components" to something greater than the siblings. Then automatically, all the children will be layered above them, too.
Reparenting is also a good solution. If you use ApplicationWindow as root object in your project, you don't even need to create add a new layer - it is already there.
You can use the attached property ApplicationWindow to reparent your draggable to ApplicationWindow.overlay. The only challange left is, that you need to specify the right position. You can use mapToItem() and mapFromItem() for this.
If you don't have ApplicationWindow as root, you can add a regualr Item as a child to your root and set the z-value to be the greates among its siblings. Then you assing it an id. If you don't shadow this id (having the same name for other identifiers, beeing resolved with higher priority) you can adress this Item from wherever. See more on this here.
Please can someone explain me one thing. Suppose I have an item, if I click it, then a drop-down menu appears. How to make that when you hover over menu items, they stand out like that?
Code:
Rectangle {
id: main_window
width: 600
height: 600
property int mrg: 10
Rectangle {
anchors.centerIn: parent
width: 500
height: 500
color: 'green'
Text {
id: field
text: "Click!"
font.pointSize: 20
color: 'white'
anchors.centerIn: parent
MouseArea {
id: ma
anchors.fill: parent
hoverEnabled: true
onClicked: {
menu.x = ma.mouseX
menu.open()
}
}
Menu {
id: menu
y: field.height
MenuItem {
text: "Menu item"
highlighted: true
}
}
}
}
}
In the documentation, I came across the point that the proper highlight is responsible for selecting the appropriate menu item. I installed it in the True, but it did not change anything.
Please tell me what I'm doing wrong. Thanks a lot.
Although it's an old question, I came across this as it was something I was wanting to do myself. I think m7913d's answer can be simplified a little by making use of the hovered property of MenuItem.
MenuItem {
id: control
text: "Menu item"
hoverEnabled: true
background: Item {
implicitWidth: 200
implicitHeight: 40
Rectangle {
anchors.fill: parent
anchors.margins: 1
color: control.hovered ? "blue" : "transparent"
}
}
}
The other thing I did was to retain the control.down handling of the original implementation, so the color expression is slightly more involved than shown here.
The default implementation of MenuItem doesn't include any visual highlighting feature, but you can adapt the graphical representation to your needs as explained in the Qt manuals. So, your MenuItem should look like this:
MenuItem {
id: control
text: "Menu item"
background: Item {
implicitWidth: 200
implicitHeight: 40
Rectangle {
anchors.fill: parent
anchors.margins: 1
color: control.highlighted ? "blue" : "transparent" // blue background if the control is highlighed
MouseArea {
anchors.fill: parent
hoverEnabled: true // enable mouse enter events when no mouse buttons are pressed
onContainsMouseChanged: control.highlighted = containsMouse // set the highlighted flag when the mouse hovers the MenuItem
}
}
}
}
Note that this implementation is based on the default implementation provided by Qt:
background: Item {
implicitWidth: 200
implicitHeight: 40
Rectangle {
x: 1
y: 1
width: parent.width - 2
height: parent.height - 2
color: control.visualFocus || control.down ? Default.delegateColor : "transparent"
}
}
Note that Bob's answer simplifies this solution by eliminating the MouseArea
I have faced an issue with qml which is that when I use anchors to layout items their coordinates are not being set and are equal to zero. Please see code snippets below.
Is this behavior regular for QML anchors or it's just me doing something wrong?
How this can be circumvented?
Rectangle
{
id: background
objectName: "background"
anchors.fill: parent
color: "#06A0D4"
}
Rectangle
{
id: ground
objectName: "ground"
anchors.left: background.left
anchors.right: background.right
anchors.bottom: background.bottom
color: "#D47006"
opacity: 0.4
height: 50;
}
Thanks.
This should work, i.e. when I run the following code in qmlscene, the text displays Yellow rect: x=0, y=350, width=400, height=50, which is exactly what I would have expected
import QtQuick 2.0
Item {
width: 400
height: 400
Rectangle {
id: background
anchors.fill: parent
color: "black"
}
Rectangle {
anchors.left: background.left
anchors.right: background.right
anchors.bottom: background.bottom
height: 50
color: "yellow"
Text {
anchors.centerIn: parent
text: "Yellow rect: x=" + parent.x + ", y=" + parent.y +
", width=" + parent.width + ", height=" + parent.height
}
}
}
It is true, that it is undefined.
I suppose the problem lies within the mapToItem, resp. mapFromItem function, which needs an item as it's first argument.
I can only guess that those functions or a related one is used to calculate the coordinates, if not explicitly set.
A Window object is no Item, but a QQuickWindow, thus those functions won't work.
Wrap everything within an Item should do the trick, as now QML can calculate the positions with it's functions.
I am using a ComboBox in QML and when populated with a lot of data it exceeds my main windows bottom boarder. From googling I have learned that the drop-down list of a ComboBox is put on top of the current application window and therefore it does not respect its boundaries.
Ideally I would want the ComboBox to never exceed the main applications boundary, but I can not find any property in the documentation.
A different approach would be to limit the number of visible items of the drop-down list so that it do not exceed the window limits for a given window geometry. I was not able to find this in the documentation either and I have run out of ideas.
Take a look to the ComboBox source code, the popup is of a Menu type and it doesn't have any property to limit its size. Moreover, the z property of the Menu is infinite, i.e. it's always on top.
If you Find no way but to use the ComboBox of Qt you can create two models one for visual purpose, I will call it visual model, you will show it in your ComboBox and the complete one , it will be the reference model. Items count in your VisualModel wil be equal to some int property maximumComboBoxItemsCount that you declare . you'll need o find a way that onHovered find the index under the mouse in the visualmodel if it's === to maximumComboBoxIemsCount you do visualModel.remove(0) et visualModel.add(referenceModel.get(maximum.. + 1) and you'll need another property minimumComboBoxIemsCount, same logic but for Scroll Up , I dont know if it will work. but it's an idea
I think there is no solution using the built-in component and you should create your own comboBox. You can start from the following code.
ComboBox.qml
import QtQuick 2.0
Item {
id: comboBox
property string initialText
property int maxHeight
property int selectedItem:0
property variant listModel
signal expanded
signal closed
// signal sgnSelectedChoice(var choice)
width: 100
height: 40
ComboBoxButton {
id: comboBoxButton
width: comboBox.width
height: 40
borderColor: "#fff"
radius: 10
margin: 5
borderWidth: 2
text: initialText
textSize: 12
onClicked: {
if (listView.height == 0)
{
listView.height = Math.min(maxHeight, listModel.count*comboBoxButton.height)
comboBox.expanded()
source = "qrc:/Images/iconUp.png"
}
else
{
listView.height = 0
comboBox.closed()
source = "qrc:/Images/iconDown.png"
}
}
}
Component {
id: comboBoxDelegate
Rectangle {
id: delegateRectangle
width: comboBoxButton.width
height: comboBoxButton.height
color: "#00000000"
radius: comboBoxButton.radius
border.width: comboBoxButton.borderWidth
border.color: comboBoxButton.borderColor
Text {
color: index == listView.currentIndex ? "#ffff00" : "#ffffff"
anchors.centerIn: parent
anchors.margins: 3
font.pixelSize: 12
text: value
font.bold: true
}
MouseArea {
anchors.fill: parent
onClicked: {
listView.height = 0
listView.currentIndex = index
comboBox.selectedItem = index
tools.writePersistence(index,5)
comboBoxButton.text = value
comboBox.closed()
}
}
}
}
ListView {
id: listView
anchors.top: comboBoxButton.bottom
anchors.left: comboBoxButton.left
width: parent.width
height: 0
clip: true
model: listModel
delegate: comboBoxDelegate
currentIndex: selectedItem
}
onClosed: comboBoxButton.source = "qrc:/Images/iconDown.png"
Component.onCompleted: {
var cacheChoice = tools.getPersistence(5);
listView.currentIndex = tools.toInt(cacheChoice)
selectedItem = listView.currentIndex
comboBoxButton.text = cacheModel.get(selectedItem).value
}
}
ComboBoxButton.qml
import QtQuick 2.0
Item {
id: container
signal clicked
property string text
property alias source : iconDownUp.source
property string color: "#ffffff"
property int textSize: 12
property string borderColor: "#00000000"
property int borderWidth: 0
property int radius: 0
property int margin: 0
Rectangle {
id: buttonRectangle
anchors.fill: parent
color: "#00000000"
radius: container.radius
border.width: container.borderWidth
border.color: container.borderColor
Image {
id: image
anchors.fill: parent
source: "qrc:/Images/buttonBackground.png"
Image {
id: iconDownUp
source: "qrc:/Images/iconDown.png"
sourceSize.height:20
sourceSize.width: 20
anchors.verticalCenter: parent.verticalCenter
}
}
Text {
id:label
color: container.color
anchors.centerIn: parent
font.pixelSize: 10
text: container.text
font.bold: true
}
MouseArea {
id: mouseArea;
anchors.fill: parent
onClicked: {
container.clicked()
buttonRectangle.state = "pressed"
startTimer.start()
}
}
Timer{
id:startTimer
interval: 200
running: false;
repeat: false
onTriggered: buttonRectangle.state = ""
}
states: State {
name: "pressed"
when: mouseArea.pressed
PropertyChanges { target: image; scale: 0.7 }
PropertyChanges { target: label; scale: 0.7 }
}
transitions: Transition {
NumberAnimation { properties: "scale"; duration: 200; easing.type: Easing.InOutQuad }
}
}
}
I've used it in some software of mine, hence it is possible that It could not work "out of the box". I use it like this:
ComboBox{
id:cacheChoice
initialText: "None"
anchors.top: baseContainer.top
anchors.topMargin: 2
anchors.right: baseContainer.right
maxHeight: 500
listModel: cacheModel
onExpanded: {
cacheChoice.height = 500
}
onClosed: {
cacheChoice.height = 20
}
}
In case you are working with ComboBox from Qt Quick Controls 2, here's the source code for it:
https://github.com/qt/qtquickcontrols2/blob/5.12/src/imports/controls/ComboBox.qml
Based on that, this override of the behavior works to limit the height to something reasonable:
myComboBox.popup.contentItem.implicitHeight = Qt.binding(function () {
return Math.min(250, myComboBox.popup.contentItem.contentHeight);
});
It is possible to access the hidden MenuStyle within the ComboBoxStyle component. There you can use all the things and hidden things you have within a MenuStyle, including its maximum height.
The thing looks roughly like this.
Not pretty but it works well enough.
import QtQuick 2.5
import QtQuick.Controls 1.4
import QtQuick.Controls.Styles 1.3
import QtQuick.Window 2.2
ComboBox {
id: comboBox
style: ComboBoxStyle {
// drop-down customization here
property Component __dropDownStyle: MenuStyle {
__maxPopupHeight: 400
__menuItemType: "comboboxitem" //not 100% sure if this is needed
}
}
As it came up resonantly in our team, here is a updated version of the idea shown above. The new version restricts the size automatically to the size of your application.
ComboBox {
id: root
style: ComboBoxStyle {
id: comboBoxStyle
// drop-down customization here
property Component __dropDownStyle: MenuStyle {
__maxPopupHeight: Math.max(55, //min value to keep it to a functional size even if it would not look nice
Math.min(400,
//limit the max size so the menu is inside the application bounds
comboBoxStyle.control.Window.height
- mapFromItem(comboBoxStyle.control, 0,0).y
- comboBoxStyle.control.height))
__menuItemType: "comboboxitem" //not 100% sure if this is needed
} //Component __dropDownStyle: MenuStyle
} //style: ComboBoxStyle
} //ComboBox