I'm trying to make a widget that has an image at the bottom, and text that populates from bottom-to-top. Here are three ASCII-art examples of the widget in action:
Text Line 3 ^
Text Line 4 |
Text Line 1 Text Line 5 |
Text Line 1 Text Line 2 Text Line 6 V
| ----------- | | ----------- | | ----------- |
| ----------- | | ----------- | | ----------- |
+-----------------+ +-----------------+ +-----------------+
| printer.svg | | printer.svg | | printer.svg |
+-----------------+ +-----------------+ +-----------------+
I'm having problems with the vertical spacing. It should be:
Image at bottom
TextArea should fill the rest of the space.
If there is too much text, a scroll bar should appear.
The objects needed are pretty clear:
ColumnLayout
+-- ScrollView
| +-- TextArea
+-- Image
But what combinations Layout alignments/heights/implicitHeights/contentHeights/anchors/position/policy can make this happen?
The closest I have gotten is below, and the commented-out stuff is what I've been brute-forcing.
import QtQuick 2.0
import QtQuick.Layouts 1.12
import QtQuick.Controls 2.5
ColumnLayout {
width: parent.width
height: parent.height
anchors.top: parent.top
anchors.bottom: parent.bottom
anchors.left: parent.left
anchors.right: parent.right
ScrollView {
Layout.alignment: Qt.AlignBottom
Layout.maximumHeight: parent.height - rect.height
TextArea {
text:
"Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nulla eleifend non leo
a iaculis. Nam at tortor mollis, iaculis justo vel, dictum sapien. Vivamus sed
feugiat tortor, nec tempor nisi. Orci varius natoque penatibus et magnis dis
parturient montes, nascetur ridiculus mus. Quisque vestibulum, eros id vehicula
eleifend, eros tellus iaculis lectus, sit amet molestie justo ex in nulla.
Maecenas at ultrices velit. Vestibulum eu libero tortor. Morbi ipsum lorem,
interdum sed aliquam quis, semper pretium ante. Duis et nibh ac tortor tincidunt
commodo. Vestibulum commodo nibh nisi, in lacinia lectus imperdiet vel. Class
aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos
himenaeos.
Aliquam erat volutpat. Praesent eget erat a nisi vulputate euismod. Donec in
vulputate tellus, eget dapibus eros. Quisque nec lacus iaculis, venenatis ante
quis, tincidunt ligula. Donec aliquet diam sit amet nisl sollicitudin, et varius
arcu lobortis. Aliquam erat volutpat. Duis enim justo, fringilla egestas
volutpat et, efficitur id erat. Duis et ullamcorper leo. Maecenas ornare orci
purus, ac commodo sapien malesuada eget. In dapibus ex nec risus laoreet
fringilla. Donec maximus elit in elit aliquam, eget imperdiet mauris congue.
Nunc libero quam, fringilla et mauris sit amet, consectetur convallis augue. Sed
vitae hendrerit ex, ac sollicitudin nibh. Donec diam mi, placerat vitae
venenatis vitae, lacinia at urna."
}
}
Rectangle { // Image placeholder
id: rect
Layout.alignment: Qt.AlignHCenter | Qt.AlignBottom
width: 300
height: 100
color: "red"
}
}
In this case, I am using anchors to force the ColumnLayout to fill its container. Then, things like Layout.alignment: Qt.AlignBottom behave well. I found that if I don't specify ScrollView{ Layout.maximumHeight: }, then the scroll bar doesn't appear.
It works well in the online sandbox I'm playing in, but in practice it fails miserably That's because this widget is contained in a StackLayout. When I try to include my widget in the project, I get:
QML ColumnLayout: Detected anchors on an item that is managed by a layout. This is undefined behaviour; use Layout.alignment instead.
So I've tried combinations of these lines:
ColumnLayout {
Layout.alignment: Qt.AlignBottom
height: parent.height
But neither line had any effect. I think that's because the parent deduces its size from its children.
I'd prefer not to change the rest of the application, but if I reduce the rest of the application to parent components and properties, I get something that looks like this. Is it even possible for me to write a bottom-to-top widget in an application like this?
ApplicationWindow {
readonly property int base_width: 1920
readonly property int base_height: 1080
id: mainApplication
width: base_width*4/5
height: base_height*4/5
Rectangle {
id: iosMain
anchors.fill: parent
StackLayout
{
id: stackLayout
anchors.left: menuButtonsLayout.right
anchors.right: iosMain.right
anchors.top: iosMain.top
anchors.bottom: iosMain.bottom
Item {
id: pageRoot
Layout.fillWidth: false
Layout.fillHeight: false
transformOrigin: Item.TopLeft
width: childrenRect.width
height: childrenRect.height
RowLayout {
x: Style.pageLeftMargin
y: Style.pageTopMargin
StackLayout {
id: stackLayout
width: parent.width
currentIndex: 0
Layout.fillHeight: true
MyWidget{} // <-- This is where my widget lives
}
}
}
}
}
}
This is the solution I came up with, I hope it fits your needs. I basically wrapped the ScrollView in an Item in order to fill the hole upper ColumnLayout cell via the attached property Layout.fillHeight. Then I use the height of the Item to place the ScrollView inside of it. First it is placed at the bottom until it grows bigger than the Item.
import QtQuick
import QtQuick.Controls
import QtQuick.Layouts
Window {
id: root
width: 480
height: 320
visible: true
color: "white"
Timer {
interval: 500
running: true
repeat: true
onTriggered: {
textArea.text += "\nTip Tip Tip " + Date().toString()
// scroll to bottom when text was added
scrollView.ScrollBar.vertical.position = 1.0 - scrollView.ScrollBar.vertical.size
}
}
StackLayout {
anchors.fill: parent
currentIndex: 0
ColumnLayout {
Layout.fillWidth: true
Layout.fillHeight: true
Item {
id: frame
Layout.minimumWidth: 100
Layout.maximumWidth: 800
Layout.preferredWidth: image.paintedWidth
Layout.alignment: Qt.AlignHCenter
Layout.fillHeight: true
ScrollView {
id: scrollView
anchors.bottom: parent.bottom
width: parent.width
height: frame.height < scrollView.contentHeight ? frame.height : scrollView.contentHeight
TextArea {
id: textArea
color: "black"
text: "Typewriter Tip Tip Tip"
background: Rectangle {
border.color: "black"
}
}
}
}
Image {
id: image
Layout.alignment: Qt.AlignHCenter | Qt.AlignBottom
Layout.fillWidth: true
Layout.minimumHeight: 100
Layout.maximumHeight: 400
source: "https://picsum.photos/300/100"
fillMode: Image.PreserveAspectFit
}
}
}
}
Related
I am trying to create a simple multiple choice like that
'
use strict';
import React, {
Component,
StyleSheet,
Text,
View
} from 'react-native';
import MultipleChoice from 'react-native-multiple-choice'
class Home extends Component {
render() {
return (
<View style={styles.container}>
<MultipleChoice
options={[
'Lorem ipsum dolor sit',
'Lorem ipsum',
'Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua.',
'Lorem ipsum dolor sit amet, consetetur',
'Lorem ipsum dolor'
]}
selectedOptions={['Lorem ipsum']}
maxSelectedOptions={2}
onSelection={(option)=>alert(option + ' was selected!')}
/>
</View>
);
}
}
const styles = StyleSheet.create({
container: {
marginTop: 60,
margin: 20
},
});
export default Home
But it gives me this erorr when I try to run it : TypeError: undefined is not an object (evaluating '_react.default.PropTypes.array')
Somebody has any idea what the problem could be ?
I have a react-native app with a single screen containing a LinearGradient which I am trying to get working as a web app.
When I run the application I have the following error.
./node_modules/react-native-linear-gradient/common.js
C:/myapp/node_modules/react-native-linear-gradient/common.js:6
3 | import type { ElementProps } from 'react';
4 | import { requireNativeComponent, View } from 'react-native';
5 |
> 6 | export default requireNativeComponent('BVLinearGradient');
7 |
8 | export type Point = $Exact<{x: number, y: number}>;
9 | type LinearGradientProps = {
Without the LinearGradient components the app runs perfectly OK. I believe the issue is that that "react-native-linear-gradient" will not work in a Web App.
Note: my version of react-native-web is "0.13.14"
App.js
import React from 'react';
import LinearGradient from 'react-native-linear-gradient';
import { StyleSheet, Text } from 'react-native';
const styles = StyleSheet.create({
drawer: {
flex: 1,
},
});
const GRADIENT_COLORS = ['blue', 'white', 'black'];
export default class App extends React.Component {
render() {
return (
<LinearGradient
colors={GRADIENT_COLORS}
style={styles.drawer}
>
<Text>
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed vestibulum
erat in elit eleifend posuere. Cras interdum sagittis sem non consectetur.
Quisque nec faucibus odio. Phasellus ac ante felis. Nulla facilisis risus nulla,
eget interdum augue pellentesque id. Phasellus pellentesque augue eget porta
fringilla. Vivamus rhoncus scelerisque libero sit amet ullamcorper. Vestibulum
sit amet est ultrices, tristique risus vitae, aliquet ligula. Ut bibendum dignissim
tincidunt. In et tortor ullamcorper, dapibus arcu a, pellentesque velit. Praesent
ornare metus dapibus, tincidunt nulla in, maximus nisl. Ut molestie aliquam mi,
ac molestie purus sagittis eget. Aliquam sit amet lacus quis risus convallis semper.
</Text>
</LinearGradient>
);
}
}
I saw that there is a polyfill for LinearLayout, i.e. "react-native-web-linear-gradient". Therefore it is my understanding I simply install that package and alias the original LinearLayout to this one.
I have installed the following
"react-native-web-linear-gradient": "1.1.1"
"react-app-rewired": "^2.1.6",
"customize-cra": "^1.0.0",
My "config-overrides.js" is :
const {addWebpackAlias} = require('customize-cra');
module.exports = function override(config, env) {
config.module.rules.push(
{
test: /\.js$/,
exclude: /node_modules\/(?!()\/).*/,
use: {
loader: 'babel-loader',
options: {
presets: ['#babel/preset-env', '#babel/preset-react'],
plugins: ['#babel/plugin-proposal-class-properties'],
},
},
},
);
addWebpackAlias({
'react-native-linear-gradient': 'react-native-web-linear-gradient',
});
return config;
};
Here I am aliasing 'react-native-linear-gradient' to 'react-native-web-linear-gradient'. My understanding is that all imports like
import LinearGradient from 'react-native-linear-gradient';
will be converted into
import LinearGradient from 'react-native-web-linear-gradient';
by webpack when building the web app.
However I am still getting the same error, like its not using the alias.
What do I have wrong.
addWebpackAlias is not imported into the config, you should use the build-in override of custom-cra and refer to its api for webpack config
/* config-overrides.js */
const {
override,
addWebpackAlias,
addBabelPlugins,
addBabelPresets,
babelExclude,
path,
} = require('customize-cra');
module.exports = override(
babelExclude([path.resolve("node_modules")]),
...addBabelPlugins('#babel/plugin-proposal-class-properties'),
...addBabelPresets('#babel/preset-env', '#babel/preset-react'),
addWebpackAlias({
'react-native-linear-gradient': 'react-native-web-linear-gradient',
}),
);
Edit: you can use babelExclude to exclude folder you don't want to transpile
I noticed strange behaviour in ListView childrenRect.height when I am removing elements from its model with ListView.onRemove animation. When I remove all elements except the last one, childrenRect.height property is wrong, but contentHeight property is ok. Removing ListView.onRemove animation results the problem disappears. Why childrenRect.height is wrong?
Using this code You will see that after You remove all elements except the last one, You cannot click in some area.
import QtQuick 2.0
import QtQuick.Controls 1.0
MouseArea
{
id: container
height: 400; width: 600
Rectangle { id: point; visible: false; width: 6; height: 6; radius: 3; color: "black" }
onPressed: { point.visible = true; point.x = mouse.x - 3; point.y = mouse.y - 3; }
ListModel {
id: listModel
ListElement { lorem: "Lorem ipsum dolor sit amet, consectetur adipisicing elit. Proin nibh augue, suscipit a, scelerisque sed, lacinia in, mi. Cras vel lorem." }
ListElement { lorem: "Etiam pellentesque aliquet tellus. Phasellus pharetra nulla ac diam." }
ListElement { lorem: "Quisque semper justo at risus. Donec venenatis, turpis vel hendrerit interdum, dui ligula ultricies purus, sed posuere libero dui id orci." }
ListElement { lorem: "Nam congue, pede vitae dapibus aliquet, elit magna vulputate arcu, vel tempus metus leo non est." }
}
ListView {
id: messageListView
model: listModel
anchors.top: container.top; anchors.left: container.left; anchors.right: container.right
height: childrenRect.height
onCountChanged: console.log("count: " + count)
onHeightChanged: console.log("height: " + height) // sometimes wrong
onContentHeightChanged: console.log("contentHeight: " + contentHeight) // rather ok
delegate: Item {
id: messageItem
anchors.left: parent.left; anchors.right: parent.right
height: Math.max(50, messageText.height + 12)
Rectangle { anchors.fill: parent; anchors.margins: 1; opacity: 0.75; color: "green"; radius: 5 }
Text {
id: messageText
anchors.verticalCenter: parent.verticalCenter; anchors.left: parent.left; anchors.right: removeButton.left; anchors.margins: 10
text: lorem
color: "white"; horizontalAlignment: Text.AlignHCenter; wrapMode: Text.WordWrap
font.pixelSize: 16; font.weight: Font.Bold
style: Text.Outline; styleColor: "black"
maximumLineCount: 6; elide: Text.ElideRight
}
Button {
id: removeButton
enabled: (index !== -1) && (messageItem.opacity === 1.0)
anchors.right: parent.right; anchors.margins: 5
anchors.verticalCenter: parent.verticalCenter; implicitHeight: 40; implicitWidth: 40
onClicked: {
console.log("remove: " + index);
listModel.remove(index);
}
}
ListView.onRemove: SequentialAnimation {
PropertyAction { target: messageItem; property: "ListView.delayRemove"; value: true }
/// PROBLEM BEGIN
NumberAnimation { target: messageItem; properties: "opacity"; from: 1.0; to: 0.0; duration: 500 }
/// PROBLEM END
PropertyAction { target: messageItem; property: "ListView.delayRemove"; value: false }
}
}
}
}
Because the ListView.childrenRect may be dynamically changed by itself. For example, try to drag the view when your example code launched. As all four delegates disappeared from the top of the view, childrenRect.height increased (even if you comment out the NumberAnimation).
That means ListView uses it's childrenRect to something internally, like drag animation, thus this property is not reliable to ListView users.
Use contentHeight instead of childrenRect.height so you can always obtain the correct value . Or use contentItem.childrenRect.height that does the same thing according to Flickable.
I need some help with the following scenario: I am using redis to store chat messages sent to various rooms:
for(var i = 0; i < 5; i++){
var message = {
"player": "player " + i,
"message": "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Quisque convallis finibus bibendum. Praesent quis ante vitae est porttitor scelerisque. Vestibulum blandit, urna ac placerat vehicula, orci nunc bibendum sem, vitae condimentum orci purus id nisi.",
"room": "room-1",
"time": new Date()
};
client.lpush(1room-1', JSON.stringify(message), redis.print);
}
I then fetch the last 100 messages:
var args = [ 'room-1', '0', '100' ];
client.lrange(args, function(err, results){
if(err){
console.log(err);
} else {
_.each(results, function(result){
var message = JSON.parse(result);
console.log(message);
});
}
});
I am new to redis and would like to make sure this is the best way of writing and reading for the scenario. I am concerned about the need to stringify the object for write and then parse the reads.
Is this the optimal way of doing this?
I would like a discussion about each user in one place. Is it possible to automatically assign a unique bbPress thread for each user and have a link to that thread placed in the BuddyPress profile?
my current attempt is to add the following code to the functions.php:
function automatic_user_forum( $user_id ) {
if( !$user_id ) return false;
$post = array(
‘post_title’ => ‘My forum’,
‘post_content’ => ‘This is a forum.’,
‘post_name’ => ‘my-forum’,
‘post_status’ => ‘publish’,
‘post_type’ => ‘forum’,
);
$post_id = wp_insert_post($post);
}
add_action( ‘bp_core_activated_user’, ‘automatic_user_forum’ );
However, it is not working and the site crashes. Any help?
Alternatively I tried the following which doesn't crash but also doesnt create any forum. Whats wrong?
<?php
function bbp_insert_girl_forum() {
$forum_data = array(
'post_parent' => 0, // forum ID
'post_content' => 'Nullam est felis, tempor luctus consequat a, aliquam ut dolor. Proin euismod aliquam ante accumsan cursus. Morbi ornare eros magna, eget sollicitudin turpis fringilla quis. Proin vitae vehicula felis. Fusce non lacus consequat, faucibus nisl sed, consequat dolor.',
'post_title' => 'Test Forum: Loads of Topics for topic Pagination',
);
if(function_exists('bbp_insert_forum')) {
$forum_id = bbp_insert_forum( $forum_data );
}else {
print "insert forum not defined";
}
$topic_data = array(
'post_parent' => $forum_id, // forum ID
'post_content' => 'Proin euismod aliquam ante accumsan cursus. Morbi ornare eros magna, eget sollicitudin turpis fringilla quis. Proin vitae vehicula felis. Fusce non lacus consequat, faucibus nisl sed, consequat dolor.',
'post_title' => 'Gravida facilisis eleifend',
);
if(function_exists('bbp_insert_topic')) {
$topic_id = bbp_insert_topic( $topic_data );
}
else {
print "insert topic not defined";
}
}
add_action( ‘bp_core_activated_user’, ‘bbp_insert_girl_forum’ );
?>
function bbp_insert_girl_forum() {
$forum_data = array(
'post_parent' => 0, // forum ID
'post_content' => 'Nullam ...',
'post_title' => 'Test Forum',
);
if(function_exists('bbp_insert_forum')) {
$forum_id = bbp_insert_forum( $forum_data );
}
}
add_action( 'bp_core_activated_user', 'bbp_insert_girl_forum' );