Skip to content

Linux and Overlay Scrollbars

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.

Overlay1.png

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:

OverlayPlacement.pngIf 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:

NoOverlay.png