Writing a blatant Telegram clone using Qt, QML and Rust. And C++
56 points by yokljo
56 points by yokljo
I haven't really done much writing in the last 10 years, but I enjoyed writing about the thing I did last weekend, so maybe you'll like reading about it :)
Oh apparently this got posted on Hacker News and it's on the front page, neat. I didn't post it there myself because the commenters hardly ever talk about the article itself lol
Thank you for this post. It was a blast to see how you would do certain effects in QT/QML/Inkscape and how easy it actually can be. I never expected this to be that straightforward.
How did you adjust the Inkscape path for length ? Also I'm curious is the decision just for left/right side ? From what I can see in the source you simply scale it - but wouldn't that mess with the proportions ?
Hey I really appreciate the kind words! Thank you for reading it :)
I meant to write about this in the article, but I'll write it here instead (EDIT: I added it to the article):
The secret to pulling off a fancy synchronised animation without having to set up some complicated animation framework, is to just use a NumberAnimation and some maths.
I might have a bit of state like property bool expand, and I want stuff to animate whenever expand changes.
real property with "ness" on the end, that is bound to the target property: property real expandness: expand ? 1 : 0
Behavior on expandness { NumberAnimation { duration: 500; easing.type: Easing.InOutQuad } }
opacity: 1 - expandness, height: lerp(0, topSectionContent.height, expandness), radius: lerp(barHeight / 2, 5, expandness), etc. etc.Once I figured out those simple steps, I started using it everywhere haha.
Web dev tangent: You can kinda do that in CSS with calc(), but it feels extremely limited in comparison. You can also directly animate stuff from JS, but that causes performance issues really quickly. It's freeing to use QML and just animate stuff directly the way I want to animate stuff.
I'm not 100% sure what you mean about adjusting the path for length sorry. The little tail thing can appear in the bottom left or bottom right of the message bubble. I basically just grid-aligned the path so the tail is a predictable 1/3 of the width of the whole path, then in QML I scale it by the desired 30/48 (48 is the original width of the path). Now I know that the tail will be exactly 10 pixels wide (1/3 of 30), so I can adjust the message bubble's margins accordingly.
I meant the Border around single chat messages, what you designed in Inkscape. Simply stretching it to the size of a message would also stretch the special corner indicating "left" or "right" as the source of the message, wouldn't it ?
This does look like fun... It's very tempting to port it to a single language (either C, Go, or Zig for my tastes) and without QML of course. Between the shader effects and custom paths, it would certainly be quite the learning experience. It could also be a great playground project for some interesting modules...
I would equate writing a capable UI library to writing a game engine, just to put it into perspective. You have to solve many of the same problems. With that in mind, I think the language is much less important than the UI libraries available for that language. I used QtQuick/QML which basically gets me 99% of the way there already, so if you choose one of those other languages, it's worth doing some research into what libraries are the most pragmatic and help you to solve UI problems rather than get in your way.
I think some of the stuff from this article is a good litmus test for seeing if you can make whatever you want: Just pick a sufficiently complex custom widget with fancy animations, scroll views, editable text boxes, complex property bindings etc etc and see if you can recreate it as accurately as possible.
Tangent: I think of a web browser as a very complex and locked-down UI framework that a lot of people are basically forced to understand because it's the only choice. Unfortunately I find it still gets in my way way too often. I believe there's a very boring war happening between people that want to write "websites" and people who want to write desktop apps... in a browser.
I would equate writing a capable UI library to writing a game engine, just to put it into perspective.
Writing a capable UI library vs binding to one (depending on the language) vs writing a game engine are all massive endeavors with their own twists in complexity. There's so many different and fascinating angles to low level development to be had.
With that in mind, I think the language is much less important than the UI libraries available for that language. I used QtQuick/QML which basically gets me 99% of the way there already, so if you choose one of those other languages, it's worth doing some research into what libraries are the most pragmatic and help you to solve UI problems rather than get in your way.
All three of the linked projects are straightforward Qt bindings meaning that Qt's upstream documentation is available as an additional resource in addition to being able to leverage Qt Creator/Designer. I find that the single-language focus simplifies the developer experience by minimizing context switching and toolchain requirements to avoid in potential distractions during application development. Take a look at the examples for C, Go, or Zig. They meet the language developer where they are (though Zig's toolchain is still somewhat novel in the C ecosystem).
I think some of the stuff from this article is a good litmus test for seeing if you can make whatever you want: Just pick a sufficiently complex custom widget with fancy animations, scroll views, editable text boxes, complex property bindings etc etc and see if you can recreate it as accurately as possible.
I've actually done most of this already in the process of developing tooling for the aforementioned libraries and the results are very good so far. I've only touched a little bit on QEasingCurve and the like. It's an area I want to dabble in further but I'm currently digging into OpenGL via Qt too. Thinking about things from the aspect of a client like Telegram also suggests some additional modules that would make sense to bind. There's never a shortage of options. 😅
Things like QGuiApplication::setOverrideCursor are available along with full support for QSystemTrayIcon. I actually want to do a full example with the latter on a cross-platform basis. Resisting the temptation of doing a clone of your client where time could be spent on carving out the equivalent functionality via separate examples is probably a better use of time in the long run. It's usually easy enough to combine working concepts in the Qt world.
Ah I'm so sorry, I didn't realise those were actually links to Qt libraries. That greatly changes the way I read your comment!
I might come back to this comment tomorrow. I do like a good chat about UI libraries.
I do like a good chat about UI libraries.
👋🏼 I'm still somewhat new to this aspect of the space but enjoying it quite a bit.
First off all, huge thank you for writing this! I'm an avid Qt programmer so it's always great to come by a great blog post like yours. I wrote a blog post about my block editor written in QML and C++ if you're curious: https://rubymamistvalove.com/block-editor
I got many things to say - first, that I also absolutely LOVE writing UI in QML - and the separation of logic to a compiled language is amazing. Although, I do wish Qt wouldn't choose something like Javascript for the scripting language (although they have improved type checking quite a bit).
Second, I also work on a chat app in QML! Although it's a chat app for LLMs - here's a short video showcasing it: https://rubymamistvalove.com/vox-demo.mp4. So easy to create slick animations - my favorite is the iMessage like chat animation where the input animates when the message is sent to its position in the chat view.
Third, I learned a new thing! I was always frustrated how popups in Qt will only render inside the window's screen, but Popup.Native seems to be answer to this. Thanks!
Fourth, I'm also a web developer in my day job, I'm using React tho, and the superiority of QML for UI is very evident every day to me.
These days I'm working on a mobile version of my note-taking app in Qt, and while going through many roadblocks during its development and solving many issues, I decided I might as well create a component library that other developers can license that will accelerate their development in QML and Qt C++ and deal with many edge cases that Qt with its built-in components doesn't do well. I will call it Daino Qt, based on my company's name.
Thanks again for sharing!
Hey I really like your block editor. I really feel like Notion is painfully glitchy, and the browser is definitely not helping. I actually really like reading about people putting real TLC into UI development. There's not enough of it in the world.
I wrote a lot of TypeScript over recent years, and I agree that the type checking is pretty disappointing in QML's JS. The choice of JS also implies a pretty huge dependency, making both the min disk and ram usage quite high. I'm not too fussed personally though. I'm think I'm glad they didn't make a bespoke language for it.
Yeah Popup.Native is pretty much the dream for popup design. It just puts the window where you want it to be and you don't have to fuss around with positioning it yourself. So useful.
As for your React job, I'm sorry to hear that 😛 Really React ain't so bad but it's just kinda trash compared to the best alternatives.
A component library sounds good. There's definitely customers out there if you can find them. Best of luck.
At a job I had like 8 years ago I spent ages making a QML Tree View widget capable of doing everything that QTreeView can while being very performance. It was actually an insane undertaking because it was very unexpectedly hard. There's so many details around QAbstractItemModel's implementation, spawning delegates, drag and drop, expanding and collapsing, selection, lazy loading on expand, etc etc.. I got a very deep understanding of how QtQuick works under the hood by doing that. 'tis a shame that it was proprietary and no longer exists...
Anyway on that note, thanks for reading and happy coding!
Please continue working on it. The official telegram client looks nice but has horrible performance, wayland bugs and memory leaks. If you manage to avoid that, you can easily overtake it (or sell your code to telegram)
The official telegram client looks nice but has horrible performance
interesting, for me it's the only "modern" chat client that doesn't work like shit and doesn't have constant UI lags and latencies, unlike Signal, Whatsapp, Discord (and the worst offenders, Slack and Teams).
Haha I'll think about it! I promise I'm not a Telegram shill: I actually have personally had basically no issues with Telegram performance or Wayland bugs, and leaks not so much either (although when I first started using Telegram it was like 50mb, and these days it's more like 150mb or more at all times. I guess it got more features...). Video calling is probably its weakest buggiest feature, but I don't use it much. Always funny how different people have such contrasting experiences with the same product.