delphiwinapiright-to-leftwindows-themesuxtheme

How to draw the element part in right to left (RTL) direction when drawing with DrawThemeBackground?


I'm trying to draw the ttGlyphClosed element of Explorer::Treeview class in right to left direction (like when the BiDiMode would be bdLeftToRight). I have a problem, that I don't know how to make my offscreen bitmap to be transparent. The bitmap's background is always white.

I'm using the following code to mirror the image:

procedure TForm5.FormPaint(Sender: TObject);
var
  bm: TBitmap;
  ARect: TRect;
  Details: TThemedElementDetails;
begin    
  if ExplorerTreeviewhTheme = 0 then
    ExplorerTreeviewhTheme := OpenThemeData(0, 'Explorer::Treeview');

  ARect := Rect(20, 20, 40, 40);
  Details := ThemeServices.GetElementDetails(ttGlyphClosed);
  DrawThemeBackground(ExplorerTreeviewhTheme, Canvas.Handle,
    Details.Part, Details.State, ARect, nil); //Ok

  bm := TBitmap.Create;
  try
    bm.Width := 20;
    bm.Height := 20;

    ARect := Rect(00, 00, 20, 20);
    DrawThemeBackground(ExplorerTreeviewhTheme, bm.Canvas.Handle,
      Details.Part, Details.State, ARect, nil);

    // rendered result has white background
    Canvas.Draw(60, 10, bm);    
    // rendered result is mirrored but has also white background
    StretchBlt(Canvas.Handle, 100, 10, -20, 20, bm.Canvas.Handle, 0, 0, 20, 20, SRCCOPY);
  finally
    bm.Free;
  end;    
end;

The question is how to mirror element drawn by the DrawThemeBackground function (for RTL reading) or how to use this function for RTL (right to left) rendering ?


Solution

  • Use SetLayout as TLama showed in his now deleted answer to switch the layout of the canvas before you draw.

    function SetLayout(hdc: HDC; dwLayout: DWORD): DWORD; stdcall;
      external 'gdi32' name 'SetLayout';
    
    const
      LAYOUT_RTL = $00000001;
    
    procedure TForm1.FormPaint(Sender: TObject);
    var
      ExplorerTreeviewhTheme: HTHEME;
      Details: TThemedElementDetails;
      ARect: TRect;
      Size: TSize;
    begin
      ExplorerTreeviewhTheme := OpenThemeData(Handle, 'Explorer::Treeview');
      Details := ThemeServices.GetElementDetails(ttGlyphClosed);
      GetThemePartSize(ExplorerTreeviewhTheme, Canvas.Handle, Details.Part,
          Details.State, nil, TS_DRAW, Size);
    
      ARect := Rect(20, 30, 20 + Size.cx, 30 + Size.cy);
      
      // normal layout
      DrawThemeBackground(ExplorerTreeviewhTheme, Canvas.Handle,
                          Details.Part, Details.State, ARect, nil);
    
      // switched layout
      SetLayout(Canvas.Handle, LAYOUT_RTL);
      
      // calculate the rectangle for RTL as if it's in LTR
      OffsetRect(ARect, 0, Size.cy); // align to the bottom of the first image so that we can see
      ARect.Left := ClientWidth - ARect.Left - Size.cx;
      ARect.Right := ARect.Left + Size.cx;
    
      DrawThemeBackground(ExplorerTreeviewhTheme, Canvas.Handle,
                          Details.Part, Details.State, ARect, nil);
      
      // restore layout
      SetLayout(Canvas.Handle, 0);
      CloseThemeData(ExplorerTreeviewhTheme);
    end;
    

    Output: enter image description here

    The theme api is drawing a 6px wide triangle in a 16px part size (W7-aero). Since you won't be able to know the placement of the image in the part, you won't be able to align it any better.