buserror / libmui

Classic MacOS & GS/OS widget library for linux (and other?)
MIT License
373 stars 6 forks source link

What the hell is this?

This is a contender for the World Record for Feature Creep Side Project. It is pretty high in the contender list as it's a bolt on to another contender for the World Record for Feature Creep Side Project (the MII Apple //e emulator).

It is a library that duplicate a lot of a Macintosh Classic "Toolbox" APIs. It is not a complete implementation, but it is enough to make a few simple applications, also, all the bits I needed for the MII emulator.

10 seconds demo 10 seconds demo

Why?

Well I wanted a UI library for MII -- something without tons of dependencies, and I didn't want the typical 'game like' style with "Arrow Keys + Return + Escape" sort of menus.

I started with Nuklear immediate mode UI, but it both look super ugly, AND is very limited as soon as you want to do something 'custom', and I didn't see myself hacking into that codebase. One thing I particularly dislike is the 'layout' engine that decide to put stuff where it wants, and it's never in the 'right' place, like a hard case of 'computer say so' -- typicaly result into something like Programmer's Art, just worse. That's why Linux On The Deskop is famous for it's Pixel Perfection polished UIs. Cough.

The other things I don't like with the trendy IM UIs is that they promise you that you don't have to keep a separate state around blah blah, however they forget to mention that there IS a state kept for you, based on hash values, and if you are unlucky enough to have a hash clash, you are screwed. I've seen that happen in real life, and it's not pretty to debug.

I miss the days were UIs were /crafted/ not just decided for you bad a bad 'layouting' engine with huge rectangular flat buttons and no sense whatsoever of 'design' or usability.

Also, I realized I had quite a few bits that I could use to make my own library anyway, so I started to do that.

Also, I like making my own toys. There, happy now?

Basic Controls Control Demo

What can it do?

Well, it has a few of the classic 'managers' that the Macintosh Toolbox had. Or the later GS/OS. However it behaves more like a modern system, it's not 'synchronous' like the old toolbox. Stuff gets redrawn even if you are clicking/dragging etc without having to 'do it yourself'.

It is designed to draw into a 'screen' that is an ARGB buffer. You can then copy that to wherever you fancy.

One small drawback is that the output has to be ARGB -- so if you want to render say on a 16 bit framebuffer, you'll have to do the conversion yourself, luckily, you only have to draw/convert the 'dirty' regions, so it's not too bad.

It could be possible to 'vectorize' the rendering to vertice buffers and stuff, but really, it's not needed, it's fast enough as it is and it would fall back the 'lets redraw everything' behaviour of the IMmediate UI libraries.

Basic text boxes Some basic text boxes

How does it differ from the original?

Well, in terms of looks, it's kinda like I started with MacOS8/9, but removed all the grayscale bits. Bizarelly, I think System 7 'flat' looks has aged better than the 'grayscale' look of 8/9, so I went with that. Most of the 'visible' difference are really related to the dialog's popup menus, which are a lot more 'OS8' than 'OS7'. The Scrollbar is definitely more GS/OS though, never understood why it took so long for MacOS to adopt that.

In terms of of the API, one massive change is that it is fully asynchronous, and you can't just spinloop and draw things in a window or GrafPort whenever you feel like it, like on the original. Instead, you change the state of the UI, and it will redraw itself when it needs to. This is a lot more like modern UI libraries in that respect.

Many things will look familiar, if like me you were a developer back then, I tried to use most of the names of things as is; and I even have all the elements drawn by WDEF, CDEF, LDEF functions, which is fun. Also pretty easy to customize. by adding your own, like in the old days.

Also, for the API, it's all 'callback' based. No more polling. You don't have to 'ask' the UI if something happened, it will tell you when it does. This is also a lot more like modern UI libraries.

It is also a lot simpler than the original in concept; everything is either a mui_window (Well, windows, menubars and menus) OR a mui_control (Menu titles, menu items, everything in windows, even separator lines).

Window Manager

It can create windows, and it can draw into them. Has up to 15 'layers', and can do clipping and stuff. Got the typical 'BringToFront' behaviour, and you can drag windows around.

I deliberately limited the number of coordinate systems to 2 -- like the old one; so you get the 'screen coordinates' and the 'window content coordinates'. I was half tempted to create a fully hierarchical system, but realized it was really not neeeded, and would just make things more complicated.

It's a 'smart' window manager, it keeps track of an 'invalid' list of rectangles, and clips to that when redrawing, so it doesn't redraw the whole window every time, yeah, like the original. None of that 'lets redraw absolutely everything every frame' stuff like Immediate Mode UIs.

Menu Manager

Menubar, menus, checkmarks, keyboard shortcuts, all that stuff. Made to looks like System 7/8, or GS/OS. This was the most complicated bit to do, and it's still not perfect -- turns out the original had quite a few features that are not obvious at first glance.

Control Manager

Buttons, checkboxes, radio buttons, scrollbars (vertical), wrapping textboxes, all that stuff.

Standard File

It has the classic 'Open' a file dialog. Haven't needed the other one. yet. This one one of the primary goal of the UI library to start with, so I spent quite a bit of time getting it 'right', quite happy about how it turned out.

Standard Get File Standard Get File

Resource Manager

Nope! Not there; I'd need some sort of ResEdit and stuff -- and now that is ONE Feature Creep Too Far thank you very much. I have a vague idea of making some sort of MessagePack format for resources, but that's for another day.

So, what are the dependencies?

Well, external dependencies are just libpixman -- that's it. It's a library that does pixel pushing, it's used everywhere, it's massively optimized, and it has 'regions' which are super useful for clipping -- they aren't as good as the regions in QuickDraw, but they are good enough.

Other bits I used to make it are rolled into the source code anyway, so it's not like you need to install them.

That's it, all the other bits I already had 'in stock' -- the 2D geometry bits I made 25+ years ago, they were rolled in libc3 as well.

How do I build it?

It's a simple Makefile, so you just need to do a 'make' in the root directory. It will build the library, and the tests/demos/samples. To build the tests/demos/samples, you'll need:

How do I use it?

Well the best way is to hack around mui_shell.c and mui_widgets_demo.c. It's a simple window with a few buttons and stuff, and it's a good place to start.

The cool thing about ui_mui_shell is that it loads mui_widgets_demo.so as a plugin and auto-reload it if it detects a change. So you can hack around mui_widgets_demo.c, and it will reload it and run it again! You can code a new dialog insanely fast with that, pretty must as fast as you would with a resource editor.

A good trick is to use 'make watch' on the libmui directory in a terminal tab, and it will rebuild the library and the mui_shell automatically when you change something, that with the 'auto save' of your editor, and you will have a constantly building/running mui_shell as you hack around.

Have fun!

FAQ!