Linux, especially Ubuntu, has quickly evolved, introducing core UI changes such as the global menu bar (akin to OS X’s single menubar system) as well as Overlay Scrollbars. Overlay Scrollbars were meant to reduce the amount of clutter needed around content areas by showing up only when hovered over, and always outside the content area. If you’re developing any kind of serious application on Linux, especially if you are targeting Ubuntu, you want to be able to detect whether or not the system supports Overlay Scrollbars.
As you can see in the screenshots above, this application has not properly detected that the system supports Overlay Scrollbars and subsequently has not positioned the scrollbar properly. Thankfully there’s an easy to way to detect whether or not a system supports Overlay Scrollbars:
Dim usesOverlayScrollbars As Boolean // Look at what additionaly GTK Modules have been loaded If Instr(System.EnvironmentVariable("GTK_MODULES"), _ "overlay-scrollbar") > 0 Then // Even though a system supports Overlay Scrollbars, // a user can still disable it If System.EnvironmentVariable("LIBOVERLAY_SCROLLBAR") <> "0" Then usesOverlayScrollbars = True End If End If If usesOverlayScrollbars Then // Position the scrollbar off the window and the // scrollbar thumb will magically appear when you hover! ScrollBar1.Left = Self.Width ScrollBar1.Width = 0 End If
This is what the application would look like after the repositioning:
If for some reason you don’t trust environment variables or prefer added complexity, there’s another solution that will work, but be warned it has added drawbacks and dependencies (see comments in the code). Here It is, implemented as an extension method for the ScrollBar:
Function UsesOverlay(extends ctl As ScrollBar) As Boolean #If TargetLinux // We really don't need to detect this more than once, if we find // that one scrollbar uses overlays we can assume they all do. // The problem is that if the control isn't actually visible yet, // we don't get the correct information so we'll have to keep // calling this until we do Static detectedOverlay As Boolean Static initialized As Boolean If initialized Then Return detectedOverlay Declare Sub gtk_widget_realize Lib "libgtk-x11-2.0.so" ( widget As Integer ) Dim widget As Integer = ctl.Handle gtk_widget_realize( widget ) Dim mb as new MemoryBlock(4) mb.long(0) = widget mb = mb.ptr(0) // We want the GtkAllocation structure which is located 36 bytes // down the GtkWidget structure // Warning: this code assumes Xojo's Linux framework uses GTK+ 2, // it will need updating if we switch to GTK+ 3 Dim allocation As GtkAllocation allocation.StringValue(True) = mb.StringValue(36, allocation.Size) If allocation.x = -1 _ And allocation.y = -1 _ And allocation.Width = 1 _ And allocation.Height = 1 Then // This means the widget probably isn't visible yet so we don't get the // right information back Return False End If // At this point we have enough information to know whether or not the // scrollbar is indeed an overlay scrollbar or not initialized = True // We'll make a blanket assumption that we'll never actually // create a 0 width or height scrollbar, in which case we don't // need to know if we're dealing with a horizontal or vertical // scrollbar, we'll take either width or height being 0 as enough info If allocation.width = 0 or allocation.height = 0 Then detectedOverlay = True Return True End If #Endif Return False End Function
Finally, you should always test both cases (i.e. with or without Overlay Scrollbars) to make sure your UI looks correct. As mentioned above, you can set the LIBOVERLAY_SCROLLBAR environment variable to 0 to temporarily disable Overlay Scrollbars. This can be done easily inside a Terminal, just type:
LIBOVERLAY_SCROLLBAR=0 ./MyApplication
If done properly your code will not have moved the scrollbar: