214 karma · joined October 21, 2021
Will share source code if someone is interested, but key bits:
- had a small JS glue layer using CanvasRenderingContext2D to render
- made a `wasm32-freestanding` build to lower the wasm bundle size, which meant shimming the bits of the libc microui uses
I see why people might hate Lua. Especially for game dev!
making a move should automatically copy the new url to your clipboard. you can still keep the ascii charm by server side rendering the ascii chess board as an og description.
1. It's really nice that it's so easy to make a `createSignal`
2. I didn't realize that in Solid you had to update all the usage sites too, so now I'd rather stick to the Svelte usage. Nested reactivity is not as effortless as I thought in Solid.
get done() { return done },
set done(value) { done = value },
get text() { return text },
set text(value) { text = value }
Looks a bit boilerplate-y maybe, but keeping your preoptimized call sites is totally worth it.This might be common enough that some syntax sugar might be worth it?
The short hand for:
todos = [...todos, {
get done() { return done },
set done(value) { done = value },
get text() { return text },
set text(value) { text = value }
}];
Could be: todos = [...todos, {
$done,
$set
}];You can do it project wide or per component: https://svelte-5-preview.vercel.app/docs/runes
This is exactly what SolidJS already does, and SolidJS is #1 in almost all performance benchmarks.
See this for a real example: https://svelte-5-preview.vercel.app/docs/fine-grained-reacti...
Here is the same thing in SolidJS with more explanation: https://www.solidjs.com/tutorial/stores_nested_reactivity
Let's compare Svelte's and Solid's approach to nested reactivity. Both of them implement the same nested reactivity todo example:
Svelte: https://svelte-5-preview.vercel.app/docs/fine-grained-reacti...
Solid: https://www.solidjs.com/tutorial/stores_nested_reactivity?so...
In Solid, converting something to use nested reactivity is one step. In Svelte, it is two steps. And that second step is really verbose and annoying:
todos = [...todos, {
get done() { return done },
set done(value) { done = value },
get text() { return text },
set text(value) { text = value }
}];
Solid makes read and write segregation very simple and obvious. You don't need to manually make all these getters and setters.It is nice that runes allows nested reactivity in Svelte, but it feels nicer to use in Solid.
Example: https://www.solidjs.com/examples/counter
`onMount` and `onDestroy` feel like really useful, dependable callbacks. `$effect` is scary because if you add a reference to state in it, you can't depend on it being called like `onMount`.
Crazy theory: esrap will be part of a transpiler that converts Svelte 4 code to Svelte 5 code. I'm not sure if that's actually the case or if it's even technically possible. But it would be really cool!
Yeahh, looks like you'd have to connect to all these signals and be able to access sizing/position from C++. Not sure how possible this is, you probably know better than me.
On the web it's common to animate the size of flexbox items with CSS animations. These animations are entirely implemented by the browser, and I imagine much of it is even GPU accelerated. No JavaScript is executed for this on the browser. But with this library, you would be running the `Flex.qml`'s `updatePositions` function every frame of the animation. Isn't that wasteful when compared to how flexbox works in the browser? It seems like a browser would be much faster at computing layout for flexbox elements than this, especially when that flexbox's size is being animated with CSS animations.
[1] https://github.com/tripolskypetr/qml-flexbox/blob/master/qml...
This behavior exists without inheritance as well! Let's imagine we want to use our existing `HelloText` component. But like you suggested, let's subscribe to the `onclick` signal and add a `bolded` property. When the user clicks the text, it will toggle the bolded property. We will use React because it's famous for not using inheritance.
```jsx function HelloText(props) { return <Text {...props}>Hello World!</Text>; }
export default function App() { const [bolded, setBolded] = React.useState(false); return <HelloText onClick={() => setBolded(bold => !bold) bold={bolded}} /> } ```
And just like you wanted, even children of `<Text>` could reference `bold` and `onClick`. I'll assume you know what that would look like. If not, let me know and I'm happy to make examples for that too. You can provide default prop values that can be overridden. You can accept functions. You can expose signals.
If you are being attentive, you might have noticed the code that enables this behavior: `{...props}` is the explicit way to pass all the props from the parent `HelloText` to its child `Text`. Unlike React, exposing props is implicit and automatic in QML. Though its trivial to make every component expose all their props to their children, the docs argue that you should avoid this. They say that if you find yourself doing this a lot, you are probably designing things poorly [1].
Here's how I understand this: If you are going back and wishing you exposed more properties or methods all the time, then you are probably developing things top down instead of bottom up. When building UI, it's easier and better if you start by building the smallest possible pieces of your UI. Then you can compose them into bigger, more complex pieces. The other way around is dangerous because you would be designing inherently inflexible abstractions. Even the most elaborate and beautiful castle made of yarn will never be as flexible as a castle made of legos.
For example, when and why would you want this `CustomRowLayout` component? Instead, simply start with the smallest easiest parts of your app. Build the buttons inside that row. Maybe the row has a dropdown item, so you can build that. Compose those parts using `RowLayout`, `ColumnLayout`, `GridLayout`, and `Flow`. Maybe make yourself yourself a `Spacer`. While you build your apps' custom rows in this pattern, you will 1. naturally develop a great toolbox of composable components that are flexible and useful and 2. have a much easier time than reimplementing some method on a `CustonRowLayout` component! Previously you asked me for an example where inheritance is a footgun in UI development. A `CustomRowLayout` is an example as building new row layouts bottom up is clearly superior.
But also, React and other modern UI frameworks won't stop you from building `CustomRowLayout` if that's what you really want. Hopefully now it's clear that you can build something like this using React, Svelte, or any other modern UI framework without inheritance. And also, if you find yourself doing this often, maybe that's a sign you are approaching problems from the wrong direction.
> In QML composition is when one places children elements within a component and expose relevant properties and signals to the parent. So I disagree on this part
Honestly, I think this is a fair distinction and definition. Though, then things get a bit hairy on the other side. Clearly React and Svelte support the alternative behavior that you call inheritance in QML. What do you call that now? Did React and Svelte suddenly become OOP frameworks too?
I think the important conclusion is that building complex UI using composition in a bottom up fashion is 1. possible and 2. optimal. QML, Svelte, and React all allow developing top down and bottom up, which is great. Svelte and React simply make top down development explicit.
I will leave things with a message from React's composition vs inheritance page: "At Facebook, we use React in thousands of components, and we haven’t found any use cases where we would recommend creating component inheritance hierarchies.
Props and composition give you all the flexibility you need to customize a component’s look and behavior in an explicit and safe way. Remember that components may accept arbitrary props, including primitive values, React elements, or functions." [2]
[1] https://react.dev/learn/passing-props-to-a-component#forward... [2] https://legacy.reactjs.org/docs/composition-vs-inheritance.h...
However, wouldn't the performance for this be very optimal? Check out the `Flex.qml` code[1]. It's all very unoptimized and dynamic QMLScript. None of the function arguments are typed. There is no way the QML compiler is turning any of this into efficient, statically typed C++ code. The beauty of using flexbox in the browser is that all the layout calculations are done natively in the browser without ever touching JavaScript.
[1] https://github.com/tripolskypetr/qml-flexbox/blob/master/qml...
`gap: 10px`
> put elements on top of it in the bottom left corner, bottom right corner and align margins of the content within the pane anchoring elements manually is so much easier
These instructions seem a bit more vague. If you make this visually or with QML I'm sure I can replicate it in an intuitive way with flexbox.
Flexbox has `align-items`, `justify-content`, `align-content` which covers all the needs of aligning to centers or corners. `padding` and `margin` work exactly like you'd expect. Unlike QML's layouts, you can also enable `flex-wrap` so content automatically wraps when it overflows the allocated space.
Maybe you're also wondering, how do I memorize all these flexbox properties? I don't! Chrome dev tools give you a GUI for editing a flexbox[1], isn't that really nice?
With all these tools, hopefully it becomes clear that you never need to manually define anchors. You can compose these layouts to build whatever complex layouts your heart desires. But, if you are still really attached to the anchor workflow, you can do that with CSS as well. For example, `position: absolute; bottom: 100%; right: 100%` would anchor a child element to the bottom right of its parent.
> The beauty of QML is that I can treat RowLayout and Row as elements written in QML itself.
I totally agree here. My Svelte example from above would become:
<RowLayout>
<p>pane 1</p>
<p>pane 2</p>
</RowLayout>
What if I told you it's totally trivial to make this RowLayout component yourself in Svelte? This could totally be valid Svelte code! If this sounds interesting, this example[2] is a great starting point. And it would be even more trivial to pull in a great component library like Skeleton UI[3] which does stuff like this for you.> If I recall accurately it is possible to loop through child elements and assign properties to them such as anchors with respect to sibiling elements. The abstraction thus is more fundamental.
Hopefully this RowLayout example makes it clear that Svelte can also loop through child elements and assign properties.
> Inheritance is the right abstraction for the UI elements
How are you so sure about this? React is one of the most popular and successful tools for building UI elements, and it avoids inheritance entirely. Do you think React's approach is totally offbase? Similarly, Rust and Go do not have inheritance at all. Do you disagree with their design?
> For instance I don’t need to predict all properties in advance for my custom bottom implemention when used in the code. Assuming that all of them are available is quite powerful and allows to experiment in place.
You can expose properties with composition based elements too! You don't need inheritance to support this workflow.
> Nevertheless, can you come up with `footguns of inheritance` to which QML is susceptible to?
I think the composition over inheritance wiki page[4] explains things much more elegantly than I ever could. Though I do think I made a mistake. Does inheritance actually exists in QML land? I don't think so.
It feels like you compose things very similarly to Svelte or React. Maybe it's a just a semantics thing, but imagine HelloText.qml:
Text {
text: "hello world!"
}
Does HelloText inherit from from "Text"? I'd say no. HelloText simply instantiates Text and sets its' text property to "hello world". Though I imagine you would say HelloText inherits from Text and overrides the text property?Compare this to the Qt Widget world where the difference between inheritance and composition are much more clear. Sometimes a Qt Widget doesn't expose the properties or functionality you want. The best scenario is when Qt Widgets do expose the properties you need. Then you can instantiate them and change those properties. Notice that even Qt Widgets sometimes expose properties without inheritance! When defining bigger fancier widgets, why require inheritance? Break them into smaller parts and expose useful properties. Then you and other programmers can compose custom behavior without going through the sludge of inheriting and overriding!
Perhaps we already agree on that part and it was just a semantics issue about whether QML is actually using composition or inheritance?
> This sounds interesting. I will give it a try on someday when I will need to make an UI in HTML.
Yay, I'm interested to hear what you think!
[1] https://developer.chrome.com/docs/devtools/css/flexbox/
[2] https://svelte.dev/examples/slots
[4] https://en.wikipedia.org/wiki/Composition_over_inheritance
Gotcha, this makes sense. To be fair, browsers write HTML elements like <input> and <textarea> using C++ too. Though I admit the core Qt Quick components cover more functionality use cases than HTML elements.
> And JS list? There's this[2].
Ooh whoops, good catch on the typed lists. I seen now you can do stuff like `property list<int> intList: [1, 2, 3, 4]`. Looks like I missed this because it was added sometime inbetween 6.2 and 6.4?
Still I'm a bit skeptical, what about lists of lists like `list<list<int>>`? And what about lists of objects? I wish I could quickly test Qt 6.5 myself without setting up the whole dev environment, but qmlonline only supports 5.15.
> JS object (like JSON?)? I'm quite sure QML supports object property.
Whoops, objects are a tricky overloaded word. But yeah, something like a struct in C. For example, TypeScript ensures this code will be totally typesafe at runtime:
type Person = {
age: number,
name: string
}
let people: Person[] = []
people.push({
age: 24,
name: "jeff"
})
In QML it seems you need to go to C++ to define a type like `Person` and expose that to QML?> Although there is qmllint that warns you of many such things before compilation even[3]. It's great.
Ohh, that would definitely be an improvement. It doesn't look like it does any type checking, but atleast checking for syntax and anti-patterns is something. If Qt gave QML good static typechecking like TypeScript, it would make my QML experience sooo much better. Then the big remaining problem is how unsafe C++ is lol.
> First - using Qt Quick - native performance.
I'm still a bit skeptical about all that QMLScript getting compiled to native code, and it's not like there's zero overhead in Qt's OOP code. It feels like native is a spectrum where C or Rust would be more native, right? If you use native components with C, that should be considered "true" native performance, right? I'm sure Qt/QML is more performant than something like TypeScript with Svelte. But I'm really curious to see by how much.
> But talking about the language itself - I can't get over how amazing property bindings and signal and slots are for state changes. Just these alone would make me quit any web framework.
I totally agree property bindings are great. In the same way properties are reactive by default in QML, any variable is reactive in Svelte. Signals and slots are interesting. I always thought its better to avoid them and find a better way to structure things. Signals and slots are component events[3] in Svelte, but people seem to rarely use them. I'm curious to see where you use signals and slots in QML. Maybe I'm missing out here.
Properties, signals, and slots exist in the C++ side too and I have some nightmares about these. The fact that this is all implemented using the Q_OBJECT macro means that when you make any mistakes you get some really gnarly errors. It feels like humans were never meant to see these errors. Maybe I'm just too spoiled by Rust and TypeScript error messages.
Callbacks are really useful in UI programming. They make it possible to do event driven programming. But it feels so rough using callbacks in C++ because functions aren't first class citiziens. In Rust or TypeScript callbacks feel much more sane to use. And when you make inevitably make mistakes, you get much more sane errors.
> They are so simple yet incredibly versatile and powerful. It just makes sense to work with them than any Hooks or whatever in React
I definitely agree that QML bindings are much nicer to work with than React hooks lol.
> Then the Model/View paradigm of Qt fits so well into QML is just awesome.
Ooh interesting. It seems older frameworks were more model/view oriented, but now new frameworks interconnect these more. It's interesting you enjoy this. I find the model and view rarely get changed seperately. New features tend to require changes to both the model and view, so it seems weird to seperate them.
> Well, you can easily grab a binary from our GitHub Actions and try it out[4] or compile it from source
Awesome thanks! I'm looking forward to trying this out and checking out the code. It will be interesting to see if Svelte can compete on any of these things.
> Why is that? Qt is using the underlying accelerated graphics APIs to render its content[5]. Even if a component isn't native we say it's native-like because it still uses the native graphic API just not the native component.
If we call QML/Qt components native-like, then we should also call HTML and CSS native-like. Browsers are built with C++ and render HTML+CSS with accelerated graphics APIs too. There are tons of powerful, interactive HTML elements[1] and when combined with CSS animations you can achieve a lot without ever touching JS.
Browsers just keep getting better. For example, the new View Transitions API[2] means that the browser is exposing hardware accelerated page change animations. Even with current CSS animations, there are lots of GPU accelerated animations that are either 1. not possible with Qt/QML or 2. less performant in Qt/QML.
Maybe you find some built in GPU accelerated feature QML has that browsers do not expose. No big deal, the web has a WebGL API, and the even more powerful WebGPU API is rolling out! We can write our own hardware acceleration using TypeScript!
However, I admit reality is messier than this. With QML its tempting to quickly hack together functionality with QMLScript that should have probably been done through different means. On the web it's the same, but TypeScript seems like a much more sane and safe language than QMLScript.
> I have plans to create an ecosystem of open-source cross-platform apps, so I've been deliberating for the longest time on which framework to choose.
This is so awesome, and I'm excited to see how your journey continues.
I'll definitely stay in touch and let you know how my experiments with Svelte, Tauri, and Rust go.
[1] https://developer.mozilla.org/en-US/docs/Web/HTML/Element
[2] https://developer.mozilla.org/en-US/docs/Web/API/View_Transi...
> The flexbox does not seem to match the power of QML. For instance, I can have a two-pane view as simple as:
I'm a bit confused by this example. When you make the other pane wouldn't you need to define all of its anchors too? It seems like the more standard QML approach to a two-pane is using RowLayout[1].
RowLayout {
anchors.fill: parent
Rectangle {
Layout.fillWidth: true
Text {
text: "pane 1"
}
}
Rectangle {
Layout.fillWidth: true
Text {
text: "pane 2"
}
}
}
I agree this is pretty nice and intuitive, but flexbox makes this problem easy too. <div style="
width: 100%;
height: 100%;
display: flex;
">
<p style="flex: 1">pane 1</p>
<p style="flex: 1">pane 2</p>
</div>
Yes inline css has problems, but I didn't want to scare you away with Tailwind[2] and I think inlined CSS makes this example clearer.> It is easy to reference an anchor of a sibling and other parent elements when needed.
It seems like by composing layouts in QML you can avoid a lot of manual anchor setting. Though layouts do have their limits. Is that why you resort to setting anchors manually? Flexbox is much less limited and flexbox layouts compose really well.
Do you have examples of UI layouts you feel QML solves intuitively that flexbox can't solve intuitively?
> Also, making new components is easy; I can inherit properties of a parent element, add my properties and put them in a file for later reuse is something I use extensively
Svelte works the same exact way. Well, except for the part that new components do not inherit from existing components. Svelte and other modern UI frameworks prefer composition over inheritance[3]. Composition allows you to be just as expressive, but without the footguns of inheritance. If you have a specific UI problem you think inheritance solves well, I'd be happy to try and create a matching composition based solution.
> Building each component carelessly using whatever mockery to get the look I need and then isolating it with its own namespace is relieving.
Svelte uses file based components as well. Each file is its own module in TypeScript/JavaScript.
If you have rebuttals or other additional things to add I'd love to hear it!
[1] https://doc.qt.io/qt-6/qml-qtquick-layouts-rowlayout.html
[3] https://en.wikipedia.org/wiki/Composition_over_inheritance
Totally fair points here. Though slight nitpicks, these breaking changes were probably entirely NextJS changes and not React changes. NextJS solves a much more complicated problem, full stack web apps. I think it's more fair to compare QML with React as they both focus on simply describing UI.
> I appreciate the native performance I get from compiled C++ code (Most QML code is compiled to C++)[1]
I read the article and I'm skeptical that most QML code is compiled to C++. My understanding from the article is that they are able to compile VERY basic QML code that you manually specify all the types for. The problem still remains that QMLScript is a very dynamic language. QML doesn't even allow you to type your list or object properties! How much of your QML code doesn't touch lists or objects?
> But I'm not sure how QML is unsafe.
QML is unsafe because it's a dynamically typed langauges. Many errors will not be discovered until runtime! I brought your example over to qmlonline[1] and modified it.
Rectangle {
property int count: 0
Button { onClicked: { count += "1"; } }
Text { text: count.toString(); }
}
This "compiles" fine, runs fine. In fact, it perceives this as fine behavior and continues on without even giving a runtime error when I click the button??Let's try something even more clearly bad. Let's do `count = "test"`. This "compiles" and starts just fine too?? Finally you get a runtime error when you actually press the button.
I'm really skeptical about how or what C++ this all compiles too, because this is all very much dynamic behavior, and it's not giving us any useful feedback at compile time. These are really simple errors that this "QML compiler" should be catching and presenting to the developer.
These kind of errors are impossible with TypeScript. TypeScript will tell you immediately in your editor when you type this mistake, so you will never get runtime type errors. Going from TypeScript to QMLScript or regular JavaScript is probably the biggest downgrade of all to me. C++ is statically typed thankfully, but what good is having only half of your code statically typed?
> I like React for small projects but as it gets bigger I find it quite cumbersome.
Reading this and "developing in React always felt hacky like using duct tape and glue", I wonder how much of this is actually React and how much is using dynamic JavaScript. If your whole app was dynamic QML code, you'd probably feel like that was hacky too, right? If you have specific examples of things that felt hacky and hard in bigger projects, I'd love to hear that. My hypothesis is that you'd feel like your React projects were much more "solid" and "proper" if you used TypeScript. The facts are that your users would run into much less runtime errors with TypeScript code than C++/QMLScript code. Also, TypeScript provides much more immediate feedback while developing.
I'm curious about what you think QML really excels at. It sounds like it was perfect for quickly making a Kanban prototype? What all did the prototype include? Maybe I should try recreating some portion of it with Svelte. I think it could be interesting to have a direct comparison. If QML has useful tools for building UI that Svelte doesn't, I'd find that really interesting. I'm constantly looking for the best way to build UIs, and right now Svelte seems like the best.
If you remember the specific problem with Tauri, I'd be interested in hearing that. I'm curious about the performance differences between Tauri and Qt apps. There are probably some pros and cons with Tauri's web view approach. I imagine OS native web views might be better at rendering certain UIs than Qt's rendering.
It's cool that you're looking into Rust things too. Rust gives even better compile time feedback than TypeScript which excites me. It's super interesting hearing all your thoughts, and would love to hear more.
Felgo is what I was hoping for, good find! That covers one pain point, but there's still many, many more. QML does have a simple and great list of components to get started with, where as HTML and CSS have accumulated many years of cruft. But, that being said, aligning elements into rows and columns is easy using CSS flexbox[1]. Flexbox has the benefit of being much more versatile than QMLs layout tools as well. I imagine using flexbox you could make the Kanban UI just as easily, and you'll probably run into less limitations.
[1] https://developer.mozilla.org/en-US/docs/Learn/CSS/CSS_layou...
> Regarding the timer, so in your Svelte code a new timer is being created each time?
`setTimeout`[1] isn't even a Svelte thing, it's a JavaScript API that QML would have access to if it was real JavaScript ;).
[1] https://developer.mozilla.org/en-US/docs/Web/API/setTimeout
That reloading tool is cool, though its not quite as useful as modern dev servers. It seems like this simply restarts the app on every change, but modern dev servers remember your state too.
I'm curious to hear more about your React experiences. I don't love React, but I still prefer React+TypeScript over Qt+QML. With TypeScript on the strictest setting, programs are much safer than with QML or Qt. We both know QML is very unsafe. And after using TypeScript and Rust, I'm realizing C++ is a very unsafe language too. It has unexpected nulls, memory leaks, seg faults, etc. I find C++ such a rough language to use, and that could be it's own mega rant. Qt's crazy macro system makes things even worse and generates some crazy errors.
There are so many options for building great cross platform UIs now. Tauri, Flutter, React Native, etc. Those all seem to have a better dev experience than Qt/QML. If performance and safety are really important, then you can use Tauri and write your critical code in Rust. There are also UI frameworks for Rust popping up like Dioxus so you can write everything in Rust.
Your "Better Notes" app is amazing and that Kanban is looking amazing. Though, I feel like you're a great dev making great stuff albeit Qt/QML, not because of Qt/QML.