Customizing the Title bar of an Application Window

Custom Decorated Desktop App Window With Default Windows Standard Behavior

Kalkidan Betre
The Startup
10 min readFeb 24, 2020

--

Recently, most modern windows desktop applications have a custom framed window by ditching or modifying the default windows title bar. These arguably enhances the look of the GUI. Such apps include popular IDEs like Intellij IDEA, Visual Studio, VS Code and also Office apps.

intellij-community IDE with a customized title bar
intellij-community IDE with a customized title bar

If you are like me obsessed with these great looking applications and wondered how to do it, well keep on reading.

The method that we will see below might not be exactly how the above desktop applications did it, but at least it will give you the exact behavior and full control of your desktop application window.

Some basics about Desktop Application Windows

Before we dig in to it, lets have some basic understanding of how Windows renders application window.

An application window has two components: a Non-Client Area and a Client Area. The Non-Client area is composed of the title bar, icon, window border and the caption buttons and the rest is the client area where you add your application user controls (buttons, test boxes ….).

Another important point to note is that an application window has several attributes including a class name, window name, window handle, window style, extended window style … etc..

These attributes help us to determine how the window is displayed and how it will interact with the user, which is very curial to our goal. Additionally, every window has a window procedure, a function that processes all message sent or posted to all the windows of the class.

These messages determine how the application window is displayed and how it interacts with inputs. Hence, by intercepting these messages we can get full control of our window (at least that is the idea).

I highly recommend you to go through the above links to get the full picture.

Custom Decorations in Desktop Application Windows

The techniques that we are going to discuss here work for various programing languages that can load native libraries (of Windows of course).

Implementing this in C++ is relatively easy because we are dealing with native libraries assuming that we have a basic understanding of how applications windows are displayed and how to handle window messages.

But here, we will be doing this using Java, specifically SWING. But I am sure once you get the hang of it you can implement custom decorations for JavaFX and also other programing languages such as C# using the same techniques.

Custom decorated desktop application windows in Java can be achieved in two ways.

  1. The first is just setting the JFrame to be undecorated (with the setUndecrated function) and then implement everything (resizing, moving and snapping).
  2. The second method is setting the JFrame to be undecorated and then use the getRootPane function on the JFrame object to get its RootPane and call the setWindowDecorationStyle(JRootPane.FRAME) on the RootPane itself. This will give you the implemention of resizing and dragging, all you need to add is the snap behavior. By default the title bar and border you get is Java’s default look and feel so you will need to change that. Also you will have to deal with adding user controls to the title bar of the JRootPane.
  3. The other method is using Java Native Access (JNA). In this method native libraries are loaded and default behaviors for the application window are implemented by the Window itself through integration of custom window procedures. Hence, all the default windows behaviors are reserved plus we will have a custom decorated window.

Custom Decorated Window Using Undecorated JFrame

When we set the JFrame to be undecorated, the Non-Client area will not be rendered and the Client area occupies the whole application window.

The major pitfall of using this method would be it requires a lot of work to get everything working (I have done this for JavaFX and if you are interested let me know on the comments and I will share it).

Let me go through the basic points on how to do it. Once you make your JFrame undecorated, you will have a blank canvas (or panel) to put everything from title bar to your application controls.

First you can implement the moving by detecting your mouse location and setting your window position accordingly. Then to implement the resizing feature, you need to think of the “hot spots” on the edge of the application window. There are 8 : N, S, E, W, NW, NE, SW & SE.

You can calculate the location of the mouse relative to the application window or you can add thin JPanles at each location and detect whether the mouse is Hovered (to change the curser) or Dragged (to actually resize the window).

Next step is to implement the window snap feature. For these you’ll need to have an additional transparent window to act like the one you see when you move a window close to the edges of the screen and you can show or hide the transparent window accordingly.

Windows Snap Imitation Using Transparent Window
Windows Snap Imitation Using Transparent Window

Then when the user lets go of the mouse you can change the size and location of the your window to be the same as the transparent window, if the application window is snapping to the edges.

But be reminded that this is just imitation, and snap assist of windows will not detect your application window.

Now the good part; let’s see how to do this so that we don’t have to implement all the default behaviors of application windows.

Custom Decorated Window Using Java Native Access (JNA)

When using this method, we will be entering the realm of windows native libraries. This is made possible by the use of JNA.

The first thing that we need to do is to download jna and jna-platform jar files. You can download the latest files from Java Native Access (JNA) GitHub Repository . Scroll down to the Download Section of the README.md file and you will find the jar files.

I will be using Intellij IDEA Community to create the project, but you can use any IDE you like. Once you have created your project and setup the dependencies (jna and jna-platform) we are good to go.

The first thing that we need to do is to create an interface that extends the User32 interface provided by jna.platform.win32.User32. We are doing this to add few functions that are not provided by jna User32 interface.

User32Ex Interface for Setting and Calling Window Procedure Functions
User32Ex Interface for Setting and Calling Window Procedure Functions

The constant GWLP_WNDPROC is an offset value that is used set a new address for the window procedure.

The SetWindowLongPtr function will allow us to set our custom window procedure for our application window, so that we can determine how to handle the selected messages that we will be intercepting.

The CallWindowProc function will allow has to call the default window procedure, this is useful because we are not going to handle all the messages (we will only handle few messages and let the rest of the messages be handled by windows).

Next, we will create a custom window decoration procedure class that extends from WinUser.WindowProc of jna. This class will have a custom callback function and a hit testing function.

Custom Decration Window Procedure Class

The first two constants WM_NCCALCSIZE and WM_NCHITTEST are message values that get sent by windows.

WM_NCCALCSIZE is called by windows before painting the Non-Client area (the border and the title bar) and WM_NCHITTEST is sent by windows to determine which area is currently receiving mouse events there by enabling us to implement resizing and moving.

HWND variable hwnd will be used to store the window handle for the application and the LONG_PTR variable defWndProc will be used to store the pointer to the default window procedure so that we can call it whenever necessary.

The Constructor and the init function will be used to load the native library and also initialize the variables defined above. As you can see below i am loading the native library in the constructor and initializing the rest of the variables on the init function.

I am doing this to make sure the native library is loaded before the window is visible and then initialize the class after the window is visible (because the handle HWND of the window can only be determined after the JFrame is visible in java)

Constructor and Initialization functions for the Custom Decoration Class
Constructor and Initialization functions for the Custom Decoration Class

Then, we will define the callback function that actually processes our selected messages: WM_NCCALCSIZE, WM_NCHITTEST and WM_DESTROY.

Custom callback function to process selected messages
Custom callback function to process selected messages

From the above code snippet you can see that I am returning 0 for WM_NCCALCSIZE, this will cause windows not to draw the Non-Client area and thereby effectively making all the window our client area.

For the case of WM_NCHITTEST, we have a BorderLessHitTest function that determines which area is used for resize and which area is used for dragging (including windows snap).

The CallWindowProc function is used to pass messages that are not handled by our custom callback function to the default windows procedure.

For the case of WM_DESTROY, we set the default window procedure back to its default. The defWndProc variable is set when we call SetWindowLongPtr to assign our custom window procedure, because this function returns a pointer to the default window procedure.

So as you can realize by now, the most important line here is the line of code that calls the BorderLessHitTest() function. This function is written based on Custom Window Frame Using DWM Appendix C: HitTestNCA.

Custom Hit testing for the custom decorated window

This function has a return value of the various locations on the application window such as HTTOPLEFT, HTTOP … etc. You can determine the location of the mouse and based on that return these values so that windows can take care of the resizing.

Next you can determine the location of your custom caption and return HTCAPTION and for other areas we return HTNOWHERE.

You can also return HTSYSMENU to display the system menu shown when a desktop application icon (at the top-left corner) is clicked.

By the way these constants are integers you can checkout the documentation about WM_NCHITTEST messages.

The hit testing algorithm depends on what you consider to be a caption area and a resize area. Especially when you place user controls on an area that you consider to be a caption area you must return HTNOWHERE when the mouse is over the area that your controls are so that the default behavior of your controls is kept.

For our case, to help me with this calculation, I have defined a CustomDecorationParameters class to hold by custom values. Which can be modified when the windows gets updated.

Custom Decoration Parameters
Custom Decoration Parameters

For example, the iconWidth will be used to determine how wide the application icon is from the top-left corner and the extraLeftReservedArea variable is used to exclude a specified area from the left of the top-left corner, so that we can place user controls such as a Menu bar.

application window title bar sections
application window title bar sections

You can update these AtomicInteger variables according to the behavior of your application window. For instance, if you dynamically add user controls on the caption area, then you need to update these variables.

Implementation of the Custom Decoration

The reset of the code would not require any native interaction. First of all, let’s make a CustomJFrame class that extends JFrame to make it easier to deal with the theme of the JFrame as well as hide the implementation of Custom Decoration on the JFrame.

CustomJFrame to apply the custom decorations
CustomJFrame to apply the custom decorations

If you are wondering about the first Theme variable ‘theme’ it is just a simple interface that defines getter methods so that various themes can be created and applied to the JFrame easily (Please refer the project code base on GitHub).

For our example, I have made a DarkTheme (check out the DarkTheme.java). The rest of the code I think is easier to understand.

Just one reminder is that the handle of the window can only be determined after the JFrame is set visible.

Demo App

I have made a simple demo on the project to demonstrate the usage of the custom decoration. You can find the code on Github.

First, I initialized the CustomJFrame with a theme and a title. Then make I made my own custom Minimize, Maximize / Restore and Close button.

I have made a separate class ControlBoxJButton and overridden paintComponent function to draw the icons. You can also use an icon (PNG, bmp, svg …)

Next, we can place anything anywhere as long as we update the hit-testing algorithm when we place user-controls on the top caption as I have mentioned earlier.

Checkout the Demo on GitHub

https://github.com/kalbetredev/CustomDecoratedJFrame

What’s Next

Well, that is about it. Of course, there is a bunch of code that I haven’t posted here but you can refer the project code on Github (CustomDecoratedJFrame).

You can apply any customization to the project to suite your GUI design. Keep in mind that, if you are making a cross platform application make sure to check whether the platform is windows before you use the custom decoration, you can do this easily using JNA.

Well I had fun making this project and writing this blog as well. I hope it’s useful for someone out there. You are more than welcome to leave me a comment or any question you might have.

I think I will also be making a YouTube video tutorial, but I will see how it goes.

Happy Coding!

--

--

Kalkidan Betre
The Startup

Currently Studying Computer Science, I am Tech Enthusiast and Civil Engineer. I love coding and learning new stuff as well as sharing it.