How to show cursor on TextInput using forwardTo in QML - javascript

I have two elements on screen, one Rectangle and one TextInput.When the activefocus is set on the rectangle and I type anything, I need to get the typed input to the TextInput
For this, I have used curRect.Keys.forwardTo = [curTextbox];
It works properly, but i also want to see the cursor while the focus is on the rectangle.
http://qt-project.org/doc/qt-4.8/qml-textinput.html#cursorVisible-prop
In the above link by QT, it says
that It can be set directly in script, for example if a KeyProxy
might forward keys to it and you desire it to look active when this
happens (but without actually giving it active focus).
But that does not happen in my case.
Can anyone explain to me why that is and how I can achieve my goal, i.e. to show the cursor even when the activeFocus is on the Rectangle?
Even if the cursor is shown at the time of entering the text it would be great.
UPDATE:
import QtQuick 1.0
FocusScope{
width: 400
height: 400
TextInput {Rectangle{anchors.fill: parent;color:"#66ff0000"}
id: mytext
anchors.top:parent.top
cursorVisible: true
width: rect.width
MouseArea{
anchors.fill: parent;
onClicked: mytext.forceActiveFocus()
}
Keys.onDownPressed: {
mytext2.forceActiveFocus();
}
}
TextInput {Rectangle{anchors.fill: parent;color:"#66ff0000"}
id: mytext2
anchors.top:mytext.bottom
cursorVisible: true
width: rect.width
MouseArea{
anchors.fill: parent;
onClicked: mytext2.forceActiveFocus()
}
Keys.onUpPressed: {
mytext.forceActiveFocus();
}
}
Rectangle {
id:rect
width: 360
height: 360
color:rect.activeFocus?"#6600ff00":"#660000ff"
focus: true
Keys.forwardTo: mytext
MouseArea{
anchors.fill: parent;
onClicked: rect.forceActiveFocus()
}
}
Component.onCompleted: rect.forceActiveFocus();
}
Thanks in advance.

The change of focus resets the cursorVisible property... so, when you click rect, and the focus is transferred from mytext to it via forceActiveRecord, mytext.cursorVisible is automatically set to false. You should reset it to true if you want to keep the cursor visible. This can be done by changing your rect.MouseArea.onClicked to this:
onClicked: {
rect.forceActiveFocus();
mytext.cursorVisible = true;
}
If you want the cursor on mytext to be always visible, you can also use the onCursorVisibleChanged handler to force it to be true. Use this inside your mytext object:
onCursorVisibleChanged: {
if (!cursorVisible)
cursorVisible = true;
}
PS: I'm posting a new answer, since added details have changed it a lot... I might delete the previous one later.

Related

How To Place Children Items of my Custom Item at my desired Location? (QML)

Assuming I want to create my CustomItem
//MyCustomItem.qml
RowLayout{
spacing: 0
Image{
id: divider
source: "qrc:/screen_header/vertical_divider_screen_header.png"
}
Item{ //THIS PART HERE SHOULD ALWAYS BE THE CHILDREN ITEMS
id:mid_item_TEXT_OR_IMAGE
Layout.alignment: Qt.AlignHCenter
}
Image{
id: divider2
source: "qrc:/screen_header/vertical_divider_screen_header.png"
}
}
I want to use this CustomItem and every children should be automatically be placed inside "Item" but the Layout.alignment should persist.
For Example
Placing an image inside would result in
//main.qml
MyCustomItem{
Image{
source: "qrc:/test.png"
}
}
Placing a Text inside would result in
MyCustomItem{
Text{
text: "AUTO"
}
}
How can I achieve this?
You can use a default property to automagically direct the Image/Text into mid_item_TEXT_OR_IMAGE with the following code:
default property alias children: mid_item_TEXT_OR_IMAGE.data
Note that this allows multiple elements inside MyCustomItem (including non-visual ones). If you don't want this, you can use a Control (Qt doc) instead of mid_item_TEXT_OR_IMAGE and alias to contentItem.
Btw, the Layout.alignment: Qt.AlignHCenter that you really want will not work, since the RowLayout will assign the Item a place on the horizontal axis based on the items. It does work for Qt.AlignVCenter since that is perpendicular to the layout direction.
With the following code you can center all contents in the middle like in your images:
RowLayout {
default property alias data: mid_item_TEXT_OR_IMAGE.data
Rectangle { //replace with your Divider.png
width: 1
Layout.fillHeight: true
color: "white"
}
Item {
Layout.fillWidth: true
implicitWidth: mid_item_TEXT_OR_IMAGE.childrenRect.width
implicitHeight: mid_item_TEXT_OR_IMAGE.childrenRect.height
Item{ //THIS PART HERE SHOULD ALWAYS BE THE CHILDREN ITEMS
id:mid_item_TEXT_OR_IMAGE
width: childrenRect.width
height: childrenRect.height
anchors.centerIn: parent
}
}
Rectangle {
width: 1
Layout.fillHeight: true
color: "white"
}
}
By using the implicitWidth and implicitHeight, the RowLayout will also grow according to the content that you put in. The other way around also works, you can give width/height for MyCustomItem on the place where you use it
I think what you're looking for is a default property. If you define your default property to be the data field of your Item, then by default all child objects will become children of that Item.
//MyCustomItem.qml
RowLayout{
default property alias contents: mid_item_TEXT_OR_IMAGE.data
Item{
id:mid_item_TEXT_OR_IMAGE
Layout.alignment: Qt.AlignHCenter
}
}

What is the most efficient way to get in-sync transform updates for an HTML5 DIV or CANVAS element

I have a Canvas element (inside a DIV). I want to get updates whenever it's transform state changes for the below transforms:
canvas to page, canvas to window, and canvas to screen(pixels).
Ideally in a more convenient and more efficient way than setting up an animation task and traversing up the tree on each frame, saving the old values and comparing with the current state on every node from child to root and on every frame.
Since you want to have an event when anything changes your Element's, the best is to poll every rendering frame (we can hook to these frames with the requestAnimationFrame method).
Such a polling would anyway be necessary to determine if the window has been moved in screen, since no built-in event does fire. (Note that browsers even seem to update it at a very low rate).
The basic is then to store an object holding your current Element's position (its bounding box should be enough), along with the window's position (Window's screenXand screenY will let us know this), and to check every frame if the values did change.
One caveat is that the bounding box is relative to the Element's owner document only. That is, in a case like StackOverflow StackSnippets iframe, if we scroll the top most document, our element's bounding rect inside its iframe will not change. To overcome this, we can add an IntersectionObserver, which will be bale to let us know when the parent window is scrolled when the Element is visible.
function listenForMove(elem) {
if (!(elem instanceof Element))
throw new TypeError('Expected an Element');
const state = {
width: null,
height: null,
left: null,
top: null,
screenX: null,
screenY: null
};
let changed = false;
function loop() {
const rect = elem.getBoundingClientRect();
Object.keys(state).forEach(checkKeys.bind(rect));
['screenX', 'screenY'].forEach(checkKeys.bind(window));
if (changed) {
elem.dispatchEvent(new Event('move'));
}
// lower the flag
changed = false;
// check again next frame
requestAnimationFrame(loop);
function checkKeys(key) {
if (key in this) {
if (state[key] !== null && state[key] !== this[key]) {
changed = true;
}
state[key] = this[key];
}
}
}
loop();
// if inside a frame, our bounding box doesn't change
// so we use an intersection observer
// works only when the Element is visible though
var obs = new IntersectionObserver(onintersectionchange, {
root: null,
margin: '100%',
threshold: Array.from({
length: 100
}).map((_, i) => i / 100)
})
obs.observe(elem);
function onintersectionchange(entries) {
changed = true; // simply raise the flag
// `loop` will be responsible of firing the event
}
}
// initialise our poll loop
listenForMove(canvas);
canvas.addEventListener('move', e => console.log('moved'));
canvas {
border: 1px solid;
}
body {
height: 500vh;
}
canvas:hover {
transform: translateX(20px);
transition: .2s linear;
}
<canvas id="canvas"></canvas>

Can't change element's height

I'm trying to resize element by dragging (like so). I wrote a simple directive (will handle ghostbar later):
#Directive({ selector: '[drag-resize]' })
export class DragResizeDirective {
private dragging: boolean;
constructor(el: ElementRef, renderer: Renderer2) {
renderer.listen('window', 'mouseup', (e) => {
if (this.dragging) {
el.nativeElement.style.backgroundColor = 'red'; // Works fine.
el.nativeElement.style.height = `${e.pageY + 2}px`; // Not so much.
this.dragging = false;
}
});
}
#HostListener('mousedown') onMouseDown() {
this.dragging = true;
}
}
The problem is I can't change height of the element. Other styles work fine. I'm using Angular 4.0.3.
Computed attributes:
display: block;
height: 244.781px;
left: 0px;
overflow-x: hidden;
overflow-y: scroll;
position: absolute;
top: 655.422px;
width: 1793px;
*renderer.setStyle() doesn't work either.
** Element I'm trying to resize is a grid tile (md-grid-tile from Angular Material).
*** Directive works on other elements.
Edit 2:
I've dug into the md-grid-list implementation. The row height is recalculated everytime that ngAfterContentChecked is triggered (code). This happens after every mouse event and is probably the reason why setting the height has no effect.
From the md-grid-list documentation I can see that you can also pass a rowHeight (e.g. 200px) input parameter to the `md-grid-list. This seems to be cleanest way to set the row height, but will scale all rows to the same size.
If this is not the effect you want to achieve, you can try setting the height in the ngAfterViewChecked lifecycle hook.
Edit:
In case your code is trying to resize a display: inline element, you first have to apply a e.g. display: inline-block to it. Otherwise it will ignore the height value.
The style.height attribute expects numeric values to have a unit (e.g. %, px, rem, em, vh).
This should work:
el.nativeElement.style.height = `${e.pageX + 2}px`;

Can you set the size of an element using getBoundingClientRect

I have a button that gets resized on the mouse enter event. I am wondering if its possible to resize it based on its height received from getBoundingClientRect().height.
This is what I've tried so far with variations of this but nothing seems to work
btn.onmouseover = function(){
let h = this.getBoundingClientRect().height;
this.style.width = "100%";
if(h != this.getBoundingClientRect().height)
{
this.getBoundingClientRect().height = h;
}
this.style.right = "4%";
}
What happens is the button resizes when the mouse is over the button due to the text inside of it being moved from two lines to one so I was wondering if there is a function call similar to getBoundingClientRect() where i can set the height of the button based on the height I get from h. This way the text inside the button being moved from 2 lines to one doesn't resize the whole button on mouseover.
Also setting the height from h to the style height of the button creates a weird offset so that's not really a viable solution for me in this scenario
I'm confused by this code. You are checking if the height you just obtained from getBoundingClientRect is equal to the height from getBoundingClientRect, and of course it will always be. And no, you can't possibly change the size of something by updating the information in the size you got from calling getBoundingClientRect. Anyway, if you really want to do something in JS relating to hovering, you need to use mouseenter and mouseleave, not mouseover.
But actually, you should be able to do this entirely in CSS.
button { width: 200px; height: 3em; }
button:hover { width: 400px; }
<button>Hi, I'm a button with some pretty long text</button>
If you really want to handle the mouse events yourself, then
const button = document.querySelector('button');
button.addEventListener('mouseenter', () => {
button.style.height = button.offsetHeight + 'px';
button.style.width = '600px';
});
button.addEventListener('mouseleave', () => {
button.style.width = button.style.height = '';
});
button { width: 240px; }
<button>Button with some pretty long text which will wrap</button>

Remember scroll position in QML element

I have some qml that acts as the output from the application (kind of like a read-only console). However, it's annoying to use because as it prints information it scrolls back to the top.
For example, lets say I print a line to my TextArea every second, after a minute or so, I'll have enough lines that I now have a scroll bar. I scroll to the bottom, a second later, a line of text is printed causing it to scroll back to the top.
The desired functionality I would like is to automatically scroll to the bottom (much like how a console is when it prints stuff out) unless the user overrides this by scrolling up, then the text should stay put. I hope that made sense, here is some code:
ScrollArea {
id: helpTextScrollArea
anchors.left: parent.left
anchors.right: parent.right
anchors.top: myButton.bottom
anchors.topMargin: 5
anchors.bottom: parent.bottom
visible: false
horizontalScrollBar.visible: false
onVisibleChanged: {
helpText.visible = visible
}
HelpTextArea {
id: helpText
width: parent.parent.width - helpTextScrollArea.verticalScrollBar.width
text: "Oops, no documentation."
onVisibleChanged: {
if(helpTextScrollArea.visible != visible) {
helpTextScrollArea.visible = visible
}
}
}
}
Flickable
{
id: flick
anchors.left: parent.left
anchors.right: parent.right
anchors.top: runStopButton.bottom
anchors.bottom: parent.bottom
anchors.topMargin: 5
TextArea{
id: messageArea
anchors.fill: parent
focus: true
readOnly: true
visible: !helpTextScrollArea.visible
wrapMode: TextEdit.Wrap
function addText(newText) {
text += newText + "\n"
}
}
}
Note: I don't think my Flickable does anything, it was part of my experimenting to fix the problem. Also, I use the function addText to print to the text area. I hardly know anything about qml and most of this code was written by someone else and I'm trying to work with it. Thank you!
You can bind contentY property of Flickable to an expression that will calculate proper position.
Flickable {
id: flick
states: State {
name: "autoscroll"
PropertyChanges {
target: flick
contentY: messageArea.height - height
}
}
onMovementEnded: {
if (contentY === messageArea.height - height) {
state = "autoscroll"
}
else {
state = "" // default state
}
}
// ...
}
Flickable has got 4 readonly properties in called visibleArea.* (you can read their meaning in the QML documentation) :
visibleArea.xPosition : xPosition = contentX / contentWidth
visibleArea.yPosition : yPosition = contentY / contentHeight
visibleArea.widthRatio : widthRatio = width / contentWidth
visibleArea.heightRatio : heightRatio = height / contentHeight
If you are using GridView instead of Flickable, you can use these methods.
positionViewAtBeginning()
positionViewAtEnd()
positionViewAtIndex(int index, PositionMode mode)
I found these to be really helpful, as when the line of text is being added, just call the positionViewAtEnd() method inside of the GridView.
Hope it helps

Categories

Resources