HumbleUI/JWM

API: Customize window titlebar

tonsky opened this issue · 13 comments

Has any further thought been put into this API? I am interested in implementing it for macOS.

Does this include macOS titlebars with toolbars? or full customization of traffic light position like electron has?

Is there any plan to solve the issue of drag regions without a titlebar?

That’s great! It’s a great and important feature for sure.

I only have general idea of how this should look like. I think the main use case here is to do title-less windows. It would require to position traffic lights / windows close buttons and drag regions, as I imagined. This is as far as “cross-platform” part goes. (it could be implemented partially, e.g. only for macOS first, but keeping cross-platform usage in mind).

There could also be macOS APIs that do macOS-specific things, I think (as methods on WindowMac, for example).

I am happy to review API proposal before implementation.

macOS has the simplest way to implement this - you could place any content in the titlebar and offset "traffic lights" to any place you want.

On Windows, you should draw and handle the whole titlebar by yourself (you can notice the Windows 10-styled window control buttons when running modern software with custom titlebar (even Microsoft Office) on Windows 7 or 8). I've tried to do the job and I think it's not hard.
image

On Linux of course the situation is the worst: there're lots of desktop environments. You can possibly to do the job with GTK (there're lots of GTK applications with customized titlebar) but what about KDE users? What about DEs without titlebars (i.e. i3wm)? Looks like the best way on Linux is to turn off custom titlebar because you could not guarantee how does a custom titlebar would look like and even would it exist.

Goods news: all three platforms support custom drag regions.

Drawing a similarly styled titlebar seems out of scope of the project to me, but correct me if I am wrong.

In my mind there would be a way to hide the titlebar and add drag regions on all (or as many as feasible re linux) platforms, and then some platform specific functions for things like positioning the traffic lights on macOS in their respective window classes.

Will come back with an API proposal to review once I work out how drag regions work :)

Sounds good!

Titlebar Settings

Based on my understanding, every platform window can have its titlebar removed completely (as follows on macOS).
Screen Shot 2022-01-09 at 6 54 50 PM
For this, I propose a simple Window#setTitlebarVisible(boolean).

On Macos, there are a few other options (at least I did not see an obvious windows solution).

Hidden title (seems functionally equivalent to setting the title to an empty string)
Screen Shot 2022-01-09 at 6 53 50 PM
(could be done on other platforms by setting title to empty string?)

Full content area / no titlebar background
Screen Shot 2022-01-09 at 6 54 07 PM

These two could be handled simply with WindowMac#setTitleVisible(boolean) and WindowMac#setFullSizeContentView(boolean)

Finally, traffic light position can be set with WindowMac#setTrafficLightPosition(int, int).

It is worth noting that the two unified toolbar modes (small and large, below) allow setting a subtitle so it may be worth allowing this as well.
Screen Shot 2022-01-09 at 7 26 22 PM
Screen Shot 2022-01-09 at 7 25 39 PM

User drag regions

The simplest solution I see is to introduce the following event for users to decide whether a drag should begin:

class EventWindowDragRequested {
    public final int _x;
    public final int _y;
    
    // If true after event listener has handled, start dragging the window
    // on macOS this can simply use NSWindow performWindowDragWithEvent
    public boolean _performDrag = false
}

Notably no other events contain data returned to the dispatcher, let me know if it should be avoided for any reason.

Alternatively the window could track a list of user defined drag rects, but this seems more flexible.

Eager to hear feedback :)

I am concerned that setTitlebarVisible, setTitleVisible and setFullSizeContentView are sometimes mutually exclusive. E.g. if you hide titlebar, setFullSizeContentView will have no effect.

Maybe something like WindowMac#setTitlebarStyle with values like DEFAULT, TRANSPARENT, UNIFIED_SMALL and UNIFIED_LARGE?

Does setFullSizeContentView play with unified toolbars btw? If yes than my suggestion is wrong, and we’ll need a setFullSizeContentView as a separate method.

Another thought is that setTitlebarVisible and setFullSizeContentView kind of do the same thing. In that you can choose to hide titlebar and then add back traffic light (setTrafficLightPosition) and title (setTitle) separately.

I think we can go without setTitleVisible? By just allowing Window#setTitle(null). What do you think?

The simplest solution I see is to introduce the following event for users to decide whether a drag should begin:

Wait, but shouldn’t it be just a mouseDown event? JWM doesn’t know yet if what’s happening will result into a drag or not. It’s left for user to decide?

Notably no other events contain data returned to the dispatcher, let me know if it should be avoided for any reason.

Yeah I’d prefer not to it that way. I find it easier to think about events as immutable data. What if we can call Window#performDrag from event handler? Would that work?

Curious how other platforms handle it though.

Maybe something like WindowMac#setTitlebarStyle with values like DEFAULT, TRANSPARENT, UNIFIED_SMALL and UNIFIED_LARGE?

How do you imagine this working cross platform? Does this imply title style must be handled per platform? (eg WindowX11#setTitlebarStyle(DEFAULT, TRANSPARENT))?

Does setFullSizeContentView play with unified toolbars btw?

Yes. Traffic light position is non-standard, and title/subtitle show in the modified location (left aligned to traffic lights). Below is an example of full size content view/invisible titlebar/unified toolbar/subtitle.
Screen Shot 2022-01-10 at 11 33 38 AM

I think we can go without setTitleVisible? By just allowing Window#setTitle(null). What do you think?

Seems much better.

What if we can call Window#performDrag from event handler? Would that work?

Its possible, the (recommended as far as I can tell) way to do this is NSWindow performDragWithEvent which needs the associated NSEvent. I have not attempted to synthesize the event. I can check how electron handles this, though I suspect they keep track of the rects with the drag tag and handle it in mouseDown.

Edit: Yes, electron creates one completely draggable NSView (using performDragWithEvent), and positions an exclusion zone everywhere that is not draggable. (here)

Does this imply title style must be handled per platform?

Per platform. But since style and setFullSizeContentView are orthogonal, we’ll probably be down to DEFAULT, UNIFIED_SMALL and UNIFIED_LARGE on macOS.

This is the list so far, correct me if something is missing:

  • Window#setTitlebarVisible(boolean) (completely hides everything, rest of the methods don’t work)
  • WindowMac#setFullSizeContentView(boolean) (makes titlebar “transparent”)
  • WindowMac#setTitlebarStyle(WindowMacTitlebarStyle) (switch between different styles of titlebar)
  • WindowMac#setTrafficLightPosition(int, int) (move traffic light. Works with invisible titlebar by returning traffic light)
  • Window#setTitle(null)
  • WindowMac#setSubtitle(String)

Re: drag regions, I think API that operates with rectangles is more declarative and will be easier to implement cross-platform. Something like:

  • Window#addDragRegion(IRect)
  • Window#excludeDragRegion(IRect)
  • Window#resetDragRegions()

?

That all sounds good to me.

I think that registered drag regions is better than a method on Window to begin dragging. It does seem worse in cases where the drag region is dynamic, although I cannot come up with a real world use case.

I will get to work on an implementation PR.

bumfo commented

That all sounds good to me.

I think that registered drag regions is better than a method on Window to begin dragging. It does seem worse in cases where the drag region is dynamic, although I cannot come up with a real world use case.

I will get to work on an implementation PR.

Great to see that coming! And how about the progress with this APIs? I'm glad to help if there are some unfinished works.

Please

I'll start work on the Win-API side of this tomorrow.

I have setTitlebarVisible implemented, will take inventory & code the remaining FNs from #75 (comment) , except for the DragRegion functions which I don't want to touch yet.