QML (Cascades) - insert container at position - qt

Cascades, 10.2 SDK
MyCustomContainer.qml:
import bb.cascades 1.2
Container {
id: rootContainer
Container{
id: childContainer
// some content
}
}
MyPage.qml:
import bb.cascades 1.2
Page {
MyCustomContainer{
Label{
id: label
}
}
}
Label will be added on top of all content inside the rootContainer. I'd like to insert the label(or any other content) below all the content in MyCustomContainer. I've tried creating a property inside MyCustomContainer:
property Container backgroundContent
and add it as first child inside the MyCustomContainer as:
id: rootContainer
backgroundContent{
id: backgroundContent
}
and just set it in MyPage as:
MyCustomContainer{
backgroundContent: Container{
}
but I get
Cannot assign to non-existent property "id"
since the property value has not been set.
Can I somehow insert the content from MyPage.qml at the root of the MyCustomContainer?

It sounds like you want to use a default property to assign any children of the MyCustomContainer to be placed into the childContainer.
import bb.cascades 1.2
Container {
id: rootContainer
default property content: childContainer.children
Container{
id: childContainer
// All children when this control is used will be placed inside this component.
}
}
Then it can be naturally used as:
MyCustomContainer {
Container {
}
This will render the Container within the childContainer of the MyCustomContainer component.

Try using backgroundContent.add()

The solution is rather simple. Predict at which place can components be injected in the root container and place another container there.
Container {
id: rootContainer
Container{
id: injectContainer
}
Container{
id: childContainer
// some content
}
}
injectContainer.add(label)

Related

Proper way to set a child item property in QML

Everyone knows about how to set a background for Buttons, Popups etc via the background property of these elements. But I am wondering, how can I create such a property myself for my own custom elements? I found a way but it looks pretty ugly and I can't seem to find the qml code for Button, Popup etc where said property is defined. So i played a bit and came up with the idea of using Bindings like this:
Item {
id: root
property Item background: Rectangle {
color: "red"
}
Binding {
target: root.background
property: "parent"
value: root
}
Binding {
target: root.background
property: "anchors.fill"
value: root
when: root.background.parent == root
delayed: true
}
}
As mentioned that looks pretty tiresome if you need to declare a lot of properties of the child. So, how does Qt do it or what is the proper way of doing it?
// ItemWithBackground.qml
Item {
property alias background: backgroundLoader.sourceComponent
Loader {
id: backgroundLoader
anchors { fill: parent }
sourceComponent: Rectangle { color: 'red' } // default background implementation
}
}
// Usage example:
ItemWithBackground {
background: Rectangle {
color: 'green'
}
}
If you’re on a recent Qt version, have a look at using inline components. They allow you to create API’s like this easily.

Resize the cursor

I'm building a drawing application (Like paint or Sketchpad) and I need to resize my cursor depending on of the line width of the pencil. The problem is, apparently, you can't resize your cursor. The solution that I found is to use a custom cursor (the normal cursor is changed for an image) and to resize the image. The thing is, I don't know if I need a function to do that or I can directly change the size of the image via SCSS (CSS).
Here's what I've done so far:
private setCursor(cursorType: DrawingCursor): void {
this.Canvas.setAttribute("style", "cursor:url(" + cursorType + "), auto;");}
The cursorType is the url of the custom cursor.
I'm doing this from an angular 8 project (in Typescript).
Thank you !
You can use NgClass binding to implement it. This way the Angular binding takes care of applying the CSS classes so you don't need to set styles manually.
To implement this solution first define the CSS classes for the canvas and the different cursor sizes. For example:
.myCanvas {
width: 400px;
height: 400px;
background-color: green;
}
.brush18px {
cursor: url('brush18px.png'), auto;
}
.brush24px {
cursor: url('brush24px.png'), auto;
}
.brush36px {
cursor: url('brush36px.png'), auto;
}
Then in the component you define a property that will provide the classes for the canvas and a property for the size of the brush. For example:
#Component({
selector: 'my-app',
templateUrl: './app.component.html',
styleUrls: [ './app.component.css' ]
})
export class AppComponent {
name = 'Angular';
#Input() cursorSize = '18';
get canvasClasses() {
return {
myCanvas: true,
brush18px: this.cursorSize === '18',
brush24px: this.cursorSize === '24',
brush36px: this.cursorSize === '36',
};
};
}
The canvasClasses property must return an object that specifies for each CSS class name if it is applied or not by setting the value to true or false. I use a getter property here so the values update automatically when the size changes.
Now you can bind this in the template and the cursor will automatically update based on the cursorSize value.
Here is how the template looks like:
<canvas [ngClass]="canvasClasses"></canvas>
<br/>
<select [(ngModel)]="cursorSize">
<option>18</option>
<option>24</option>
<option>36</option>
</select>
I have created also a working StackBlitz sample so you can see it in action.
Another possible solution based on the comment on the first answer. This is more of a workaround solution where you hide the actual cursor and move around an image instead so it looks like it is the cursor. This was inspired by the second answer on this post about resizing a cursor image.
I implemented this solution in Angular using a directive that you add to the canvas element in the template. The directive takes as the main parameter the image to show as the cursor and has an additional parameter for the size for the image. I recommend an SVG image that resizes well. Still it is possible to use also a standard image.
Here is the implementation of the Directive for an SVG image:
#Directive({
selector: '[svgCursor]'
})
export class SvgCursorDirective {
private cursorSizeValue: number = 16;
#Input('svgCursor') public svgImage: SVGSVGElement;
#Input() public set cursorSize(cursorSize: number) {
this.cursorSizeValue = cursorSize;
this.updateCursorSize();
}
constructor(el: ElementRef) {
el.nativeElement.style.cursor = 'none'; // hides the browser cursor
}
#HostListener('mouseenter') onMouseEnter() {
// makes image visible only when mous enters the element
this.svgImage.style.visibility = 'visible';
}
#HostListener('mousemove', ['$event']) onMouseMove(e: MouseEvent) {
// draws the image at the mouse position
this.svgImage.style.left = e.clientX.toString();
this.svgImage.style.top = (e.clientY - this.cursorSizeValue).toString();
}
#HostListener('mouseleave') onMouseLeave() {
// hides image when the mouse leaves the element
this.svgImage.style.visibility = 'hidden';
}
private updateCursorSize() {
if (this.svgImage != null) {
this.svgImage.style.width = this.cursorSizeValue.toString();
this.svgImage.style.height = this.cursorSizeValue.toString();
}
}
}
Once you have the directive you can use it in the following way inside a component template:
<svg #cursorImage class="cursor" xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><path d="M0 0h24v24H0z" fill="none"/><path d="M7 14c-1.66 0-3 1.34-3 3 0 1.31-1.16 2-2 2 .92 1.22 2.49 2 4 2 2.21 0 4-1.79 4-4 0-1.66-1.34-3-3-3zm13.71-9.37l-1.34-1.34c-.39-.39-1.02-.39-1.41 0L9 12.25 11.75 15l8.96-8.96c.39-.39.39-1.02 0-1.41z"/></svg>
<canvas class="myCanvas" [svgCursor]="cursorImage" [cursorSize]="cursorSize"></canvas>
As you can see you need to add a template reference variable to the image so you can pass it as a parameter to the svgCursor directive.
Also important for this to work you need to set the correct CSS styles to the image to disable things that are not needed. It is also set to invisble so it becomes visible only when the mouse enters te canvas.
This are the styles I used:
.myCanvas {
width: 400px;
height: 400px;
background-color: lightgreen;
}
.cursor {
position: absolute;
cursor: none;
pointer-events: none;
visibility: hidden;
}
I have created also a working StackBlitz sample so you can see how this works.

react-native-navigation showModal Custom Size

I use wix react-native-navigation. Navigation.showModal open fullscreen size. Is it possible to open the custom size? where do I find all the properties list about navigation layouts? Documentation is too meager...
Modal is always full screen. You can control your view's dimensions in jsx.
Instead of flex: 1 - use a predefined width and height according to your needs.
render() {
return (
<View style={{width: '80%', height: 150}}>
{ /* render Modal content */ }
</View.
);
}
Alternatively, if you need to interact with the UI behind the Modal you should use a Navigation.showOverlay
Navigation.showOverlay({
component: {
name: 'example.Overlay',
options: {
overlay: {
interceptTouchOutside: false // this make touch events pass through the invisible parts of the overlay
}
}
}
});
For more examples on Overlay used you can see code examples in the playground app

In QML, how can I create a parent Component that takes child Component instances and puts them in the parent Components hierarchy?

Qt's QML has a number of widgets where you instantiate the widget and then give it an instance of a Component for it to display and use.
For example, the Popup widget can be used like:
Popup { // <-- Parent Component
Rectangle { // <-- Child Component
width: 100;
height: 100;
color: 'red';
}
}
How can I do this myself? How can I write a custom QML Component that will take a child Component instance and put it beneath my tree?
If I have a silly Component like:
// File Parent.qml
Rectangle {
default property var child_inst;
width: child_inst.width + 20;
width: child_inst.width + 20;
color:'red';
child_inst; // <-- how is this done?
}
and used like:
Parent {
Text { text: 'hello world!' }
}
What is the syntax or mechanism by which I can instantiate or move the child_inst to be a child of my Parent?
Wow, that's hella confusing way to put it, but if I understand correctly, you want to have children actually redirected to some other object automatically. This is possible, for example:
// Obj.qml
Rectangle {
width: 50
height: 50
default property alias content: row.children
Row {
id: row
}
}
and then:
Obj {
Rectangle {
width: 10
height: 10
color: "red"
}
Rectangle {
width: 10
height: 10
color: "green"
}
Rectangle {
width: 10
height: 10
color: "blue"
}
}
The result is that the rgb rectangles are displayed in a row, because even though they are nested in Obj, they are internally delegated to the row.
Using a default property means that you don't have to specify it manually. Of course, if you don't want that, you can even use multiple different placeholders, for example, you can have:
property alias content: contentItem.children // this is a column of contents
property alias control: buttons.children // this is a row of buttons
Keep in mind that if you don't use the default property, you will have to specify the objects as a comma separated list in the case they are multiple:
Obj {
content : [
Rectangle {
width: 10
height: 10
color: "red"
},
Rectangle {
width: 10
height: 10
color: "green"
},
Rectangle {
width: 10
height: 10
color: "blue"
}
]
}
This is not necessary if it is a single object, it can be created like this:
Obj {
content : Rectangle {
width: 10
height: 10
color: "green"
}
}
But you can also use a single target and have your row nest the objects externally, which can save you having to bother with the array notation and gives you more flexibility:
Obj {
content : Row {
// bunch of stuff here
}
}
In this case, the content can be a simple positioned Item, that will serve as a placeholder, as it is the case for the popup component.
And finally, you can also use a Loader if you want to specify an inline component, that is one that is not defined in a dedicated QML file but using the Component element.

Angular 6: How to change page background-color dynamically

I work on an Angular 6 app (with Bootstrap 4) and need to change the page background color depending on which page the user enters. Default is white, but for login and registration screen the page color needs to be blue.
What I found so far:
in ngAfterViewInit() using
this.elementRef.nativeElement.ownerDocument: this approach makes the
app more vulnerable to XSS attacks and I want to avoid that.
Set View Encapsulation to None in app.component.ts: this way I can
set the body color in the app.component, that is 1 step forward.
So, now I have in my app.component.css:
body {
background-color: blue;
}
Question:
How can I change that color value (in app.component) using a variable?
With [ngStyle] I can not reach the body background-color.
Maybe using a css variable? but how can I change the value of that css variable dynamically?
I'm new to Sass, but might this offer a solution?
My question is different from the other question on this subject as I need to be able tochange the color value dynamically.
use render2 and set class to body using document object
app.component.ts
constructor(private renderer: Renderer2) {
this.renderer.addClass(document.body, 'body-class');
}
Note: if you are toggling classes, just remove previous class before assigning new class
The way I would do it is based on the routes. When defining the routes, you can add extra data, for example a class name.
When the route changes (i.e. via navigation), the data from the active route can be used to set the class on the body tag.
This is how you can achieve this
Update the styles.css to add different classes for body:
body {
...
}
body.red {
background-color: #ff8181;
}
body.blue {
background-color: #a0c3ee;
}
Update the routes to add extra data, specifying the body class name. Add an extra data property, like bodyClass:
const routes: Routes = [
{ path: '', component: DefaultComponent },
{ path: 'red', component: RedComponent, data: { bodyClass: 'red' } },
{ path: 'blue', component: BlueComponent, data: { bodyClass: 'blue' } }
];
Write the code to read the bodyClass and set the class to the body element when navigation occurs. This can be done in the app.component.ts:
#Component({
selector: 'app-root',
template: `
<div>
<router-outlet></router-outlet>
<app-menu></app-menu>
</div>
`
})
export class AppComponent implements OnInit {
constructor(
#Inject(DOCUMENT) private document,
private renderer: Renderer2,
private router: Router,
private activatedRoute: ActivatedRoute) {
}
ngOnInit() {
this.router.events
.pipe(filter((event) => event instanceof NavigationEnd))
.pipe(map(() => this.activatedRoute))
.pipe(map((route) => {
while (route.firstChild) {
route = route.firstChild;
}
return route;
}))
.pipe(filter((route) => route.outlet === 'primary'))
.pipe(mergeMap((route) => route.data))
.subscribe((event) => this.updateBodyClass(event.bodyClass));
}
private updateBodyClass(customBodyClass?: string) {
this.renderer.setAttribute(this.document?.body, 'class', '');
if (customBodyClass) {
this.renderer.addClass(this.document?.body, customBodyClass);
}
}
}
Here is a demo on StackBlitz: https://stackblitz.com/edit/angular-ivy-rs1tai
Why not just define a separate class based on different background-color? For instance:
.blue {
background: blue
}
.green {
background: green
}
.grey {
background: grey
}
And then set these classes on the body using ng-class or ngClass whatever convention you use based on the page. This should be fairly easy to implement.
My favourite approach for doing stuff like this is to add a class to html tag depending on the route. For example, we have some code in our basic layout component (you could put it in your root component) that does this inside ngOnInit:
let wrapper = ''
const path = this.activatedRoute.snapshot.routeConfig.path
wrapper += this.tidyPath(path)
if (wrapper !== '') wrapper += '-'
const childPath = this.activatedRoute.snapshot.firstChild.routeConfig.path
wrapper += this.tidyPath(childPath)
this.routeWrapperCssClass = wrapper
$('html').addClass(this.routeWrapperCssClass)
This will add a class to your html tag to make it look like this (although you may have to tweak this code to suit your app):
<html class="registration">
....
</html>
The class will be instantly updated whenever you change route.
Now you can do this in your main stylesheet:
body {
background-color: pink;
}
html.registration body {
background-color: yellow;
}
You could also do things like hide elements based on the class added to the html tag like so:
.navbar {
display: block;
}
html.registration .navbar {
display: none;
}
Because you know what route you are on at all times you have total control via CSS.
PS you may want to use render2 instead of jQuery to do the DOM manipulation - see this article ... https://alligator.io/angular/using-renderer2 ... never used it myself before but almost identical to jQuery syntax - thanks to Pratap A.K answer for this
Personally i replace :
<body>
<app-root></app-root>
</body>
to
<app-root></app-root>
and then i add all the time the body on components or if i have multi
router-outlets i add it on app.component.css

Resources