Recent

Author Topic: [Solved] Custom PopupMenu  (Read 5984 times)

aydın

  • Jr. Member
  • **
  • Posts: 86
[Solved] Custom PopupMenu
« on: August 16, 2023, 09:30:00 pm »
Hi everyone,

I've been working on a Custom Popup, but I couldn't get rid of the border lines.

I'm using OnDrawItem.


The code contains proprietary libraries, I apologize.
Code: Pascal  [Select][+][-]
  1. Procedure Tformstartup.Popupmenu1drawitem(Sender: Tobject; Acanvas: Tcanvas;
  2.   Arect: Trect; Astate: Townerdrawstate);
  3. Var
  4.   TC: jARGB;
  5. Begin
  6.   TC:= GetThemeColor;
  7.  
  8.   Acanvas.Brush.Style:= bsSolid;
  9.  
  10.   if GetDarkTheme then
  11.     Acanvas.Brush.Color:= Colors.Black.Mix(TC, 50)
  12.   Else
  13.     Acanvas.Brush.Color:= Colors.White.Mix(TC, 50);
  14.  
  15.  
  16.   if odSelected in Astate then
  17.     Acanvas.Brush.Color:= TC;
  18.  
  19.   Acanvas.FillRect(Arect);
  20.  
  21.  
  22.   Acanvas.Brush.Style:= bsClear;
  23.   Acanvas.Pen.Color:= Colors.Gray;
  24.   Acanvas.Font.Color:= Colors.White;
  25.  
  26.  
  27.   if TMenuItem(Sender).Caption <> '-' then
  28.     Acanvas.TextOut(5, Arect.Top+Round((22-Acanvas.GetTextHeight(TMenuItem(Sender).Caption))/2), TMenuItem(Sender).Caption)
  29.   Else
  30.     Acanvas.Line(0, Arect.Top+5, Arect.Right ,Arect.Top+5);
  31.  
  32. End;
  33.  

Thanks in advance.
« Last Edit: August 18, 2023, 08:47:11 am by aydın »
Lazarus 4.99, FPC 3.3.1 on Fedora 42

jamie

  • Hero Member
  • *****
  • Posts: 7523
Re: Custom PopupMenu
« Reply #1 on: August 16, 2023, 11:11:40 pm »
Did you try inflating the RECT ?

Inflate(ARect, 2,2);

Before fill... etc..

may not work, but worth a try
The only true wisdom is knowing you know nothing

KodeZwerg

  • Hero Member
  • *****
  • Posts: 2269
  • Fifty shades of code.
    • Delphi & FreePascal
Re: Custom PopupMenu
« Reply #2 on: August 17, 2023, 12:25:34 am »
may not work
True, since this event only handle whats inside of the window.

But my try also failed to hook and filter out lots of messages or I did something wrong, here is my try ... (currently not working since WM_CREATE is never caught by my way...)
Code: Pascal  [Select][+][-]
  1. unit Unit1;
  2.  
  3. {$mode objfpc}{$H+}
  4.  
  5. interface
  6.  
  7. uses
  8.   Windows,
  9.   Classes, SysUtils, Forms, Controls, Graphics, Dialogs, ExtCtrls, Menus, Types, LCLType;
  10.  
  11. type
  12.  
  13.   { TForm1 }
  14.  
  15.   TForm1 = class(TForm)
  16.     Panel1: TPanel;
  17.     procedure FormCreate(Sender: TObject);
  18.     procedure Panel1MouseUp(Sender: TObject; Button: TMouseButton;
  19.       Shift: TShiftState; X, Y: Integer);
  20.   private
  21.     FPopupMenu: TPopupMenu;
  22.     procedure BuildPopup(const AOwner: TComponent);
  23.     procedure DrawPopup(Sender: TObject; ACanvas: TCanvas;
  24.       ARect: TRect; AState: TOwnerDrawState);
  25.     procedure MenuItemClick(Sender: TObject);
  26.   public
  27.  
  28.   end;
  29.  
  30. var
  31.   Form1: TForm1;
  32.  
  33. implementation
  34.  
  35. {$R *.lfm}
  36.  
  37. var
  38.   PrevWndProc: WNDPROC;
  39.  
  40. function WndCallback(Ahwnd: HWND; uMsg: UINT; wParam: WParam; lParam: LParam): LRESULT; stdcall;
  41. var
  42.   ClassNameBuffer: array[0..255] of Char;
  43.   lStyle: LongInt;
  44. begin
  45.   if uMsg = WM_CREATE then
  46.   begin
  47.     Result := Windows.DefWindowProc(Ahwnd, uMsg, wParam, lParam);
  48.     if GetClassName(Ahwnd, @ClassNameBuffer[0], Length(ClassNameBuffer)) > 0 then
  49.     begin
  50.       if AnsiCompareStr(ClassNameBuffer, 'TPopupMenu') = 0 then
  51.       begin
  52.         lStyle := GetWindowLongPtr(Ahwnd, GWL_STYLE);
  53.         lStyle := lStyle and not WS_BORDER;
  54.         SetWindowLongPtr(Ahwnd, GWL_STYLE, lStyle);
  55.       end;
  56.     end;
  57.   end;
  58.   Result := CallWindowProc(PrevWndProc,Ahwnd, uMsg, WParam, LParam);
  59. end;
  60.  
  61. { TForm1 }
  62.  
  63. procedure TForm1.FormCreate(Sender: TObject);
  64. begin
  65.   PrevWndProc := Windows.WNDPROC(SetWindowLongPtr(Self.Handle, GWL_WNDPROC, PtrInt(@WndCallback)));
  66.   FPopupMenu := nil;
  67. end;
  68.  
  69. procedure TForm1.Panel1MouseUp(Sender: TObject; Button: TMouseButton;
  70.   Shift: TShiftState; X, Y: Integer);
  71. begin
  72.   if Button = mbRight then
  73.     begin
  74.       BuildPopup(Self);
  75.       FPopupMenu.PopUp(Mouse.CursorPos.X, Mouse.CursorPos.Y);
  76.     end;
  77. end;
  78.  
  79. procedure TForm1.BuildPopup(const AOwner: TComponent);
  80. var
  81.   MenuItem: TMenuItem;
  82. begin
  83.   if (FPopupMenu = nil) then
  84.     begin
  85.       FPopupMenu := TPopupMenu.Create(AOwner);
  86.       try
  87.         FPopupMenu.OwnerDraw := True;
  88.         FPopupMenu.OnDrawItem := @DrawPopup;
  89.         MenuItem := TMenuItem.Create(Self);
  90.         MenuItem.Caption := 'Item 1';
  91.         MenuItem.OnClick := @MenuItemClick;
  92.         FPopupMenu.Items.Add(MenuItem);
  93.         MenuItem := TMenuItem.Create(Self);
  94.         MenuItem.Caption := '-';
  95.         MenuItem.OnClick := @MenuItemClick;
  96.         FPopupMenu.Items.Add(MenuItem);
  97.         MenuItem := TMenuItem.Create(Self);
  98.         MenuItem.Caption := 'Item 2';
  99.         MenuItem.OnClick := @MenuItemClick;
  100.         FPopupMenu.Items.Add(MenuItem);
  101.       finally
  102.       end;
  103.     end;
  104. end;
  105.  
  106. procedure TForm1.DrawPopup(Sender: TObject; ACanvas: TCanvas; ARect: TRect;
  107.   AState: TOwnerDrawState);
  108. begin
  109.   ACanvas.Brush.Style:= bsSolid;
  110.   // background color
  111.   if odSelected in AState then
  112.     ACanvas.Brush.Color := clHighlight
  113.   else
  114.     ACanvas.Brush.Color := clSkyBlue;
  115.   // line color
  116.   ACanvas.Pen.Color:= clAqua;
  117.   // font color
  118.   ACanvas.Font.Color := clNavy;
  119.   // draw the rectangle
  120.   ACanvas.FillRect(ARect);
  121.   // draw the text
  122.   if (TMenuItem(Sender).Caption <> '-') then
  123.     Acanvas.TextOut(ARect.Left + 5, ARect.Top + 5, TMenuItem(Sender).Caption)
  124.   else
  125.     Acanvas.Line(ARect.Left + 5, ARect.Top + 3, ARect.Right - 5, ARect.Top + 3);
  126. end;
  127.  
  128. procedure TForm1.MenuItemClick(Sender: TObject);
  129. begin
  130. //
  131. end;
  132.  
  133. end.
« Last Edit: Tomorrow at 31:76:97 xm by KodeZwerg »

jamie

  • Hero Member
  • *****
  • Posts: 7523
Re: Custom PopupMenu
« Reply #3 on: August 17, 2023, 02:38:29 am »
If you use the "WindowFromDC" in that event, you can then play with the Regions, reset them to a new size before
painting.

 I don't have time to bang out a demo.
The only true wisdom is knowing you know nothing

aydın

  • Jr. Member
  • **
  • Posts: 86
Re: Custom PopupMenu
« Reply #4 on: August 17, 2023, 09:16:43 am »
If you use the "WindowFromDC"

I also considered this, but the popup screen seems to be entirely related to the Widgetset.
I don't have an idea about how to paint the window.

As another idea, I could paint the screen, but the boundaries of the popup are also a question.
And it could lead to too much flickering.
Lazarus 4.99, FPC 3.3.1 on Fedora 42

lainz

  • Hero Member
  • *****
  • Posts: 4741
  • Web, Desktop & Android developer
    • https://lainz.github.io/
Re: Custom PopupMenu
« Reply #5 on: August 17, 2023, 03:53:40 pm »
If you use the "WindowFromDC"

I also considered this, but the popup screen seems to be entirely related to the Widgetset.
I don't have an idea about how to paint the window.

As another idea, I could paint the screen, but the boundaries of the popup are also a question.
And it could lead to too much flickering.

Try searching for dark theme popup menu from Alexey Torgashin.

aydın

  • Jr. Member
  • **
  • Posts: 86
Re: Custom PopupMenu
« Reply #6 on: August 17, 2023, 04:19:12 pm »
Try searching for dark theme popup menu from Alexey Torgashin.

I couldn't find anything, it would be nice if you could share the link to the topic.
Lazarus 4.99, FPC 3.3.1 on Fedora 42

alpine

  • Hero Member
  • *****
  • Posts: 1412
Re: Custom PopupMenu
« Reply #7 on: August 17, 2023, 05:17:03 pm »
@aydin
Why don't you try mimicking the pop-up menu with a form?  You can paint the form in a way you want.
See the attached project.
"I'm sorry Dave, I'm afraid I can't do that."
—HAL 9000

lainz

  • Hero Member
  • *****
  • Posts: 4741
  • Web, Desktop & Android developer
    • https://lainz.github.io/
Re: Custom PopupMenu
« Reply #8 on: August 17, 2023, 06:17:57 pm »
Try searching for dark theme popup menu from Alexey Torgashin.

I couldn't find anything, it would be nice if you could share the link to the topic.

https://wiki.freepascal.org/Win32MenuStyler

aydın

  • Jr. Member
  • **
  • Posts: 86
Re: Custom PopupMenu
« Reply #9 on: August 18, 2023, 08:46:45 am »
@aydin
Why don't you try mimicking the pop-up menu with a form?  You can paint the form in a way you want.
See the attached project.
I'm truly grateful for this example.
You can be sure that I will use this. This is a perfect alternative for TMainMenu.

https://wiki.freepascal.org/Win32MenuStyler
I have no idea where you found this.
I've done a lot of research on this, but I've never come across it.

Thank you very much, everyone.
Lazarus 4.99, FPC 3.3.1 on Fedora 42

alpine

  • Hero Member
  • *****
  • Posts: 1412
Re: Custom PopupMenu
« Reply #10 on: August 18, 2023, 09:58:32 am »
@aydin
Why don't you try mimicking the pop-up menu with a form?  You can paint the form in a way you want.
See the attached project.
I'm truly grateful for this example.
You can be sure that I will use this. This is a perfect alternative for TMainMenu.
It was just a quick sample with a class I wrote to show a pull-down panels. It needs a bit more work to be a decent menu container, at least finding suitable component to replace TMenuItem with hot-tracking, accelerators, etc.
"I'm sorry Dave, I'm afraid I can't do that."
—HAL 9000

aydın

  • Jr. Member
  • **
  • Posts: 86
Re: [Solved] Custom PopupMenu
« Reply #11 on: August 18, 2023, 10:03:05 am »
The reason I didn't use a form in the popup is because the main form loses its activity.
I think there were a few WinAPI methods for this, but WinAPI doesn't always work properly.
Lazarus 4.99, FPC 3.3.1 on Fedora 42

alpine

  • Hero Member
  • *****
  • Posts: 1412
Re: [Solved] Custom PopupMenu
« Reply #12 on: August 18, 2023, 10:18:57 am »
The reason I didn't use a form in the popup is because the main form loses its activity.
You mean OnDeactivate/OnActivate gets fired? That is so, but since you're calling the pull-down by yourself you can easily flag it and guard against it.
"I'm sorry Dave, I'm afraid I can't do that."
—HAL 9000

KodeZwerg

  • Hero Member
  • *****
  • Posts: 2269
  • Fifty shades of code.
    • Delphi & FreePascal
Re: Custom PopupMenu
« Reply #13 on: August 18, 2023, 01:05:05 pm »
@aydin
Why don't you try mimicking the pop-up menu with a form?  You can paint the form in a way you want.
See the attached project.
I was having same feeling but you was faster so I guess I stop developing, I just prepared yesterday my way of doing for a fully configurable PopupMenu thingy...
Beside preparing it's definition nothing more done yet  :D
Code: Pascal  [Select][+][-]
  1. type
  2.   TImageStyle = (isLeft, isRight, isCentered);
  3.  
  4.   TMenuImage = packed record
  5.     Enabled: TGraphic;
  6.     Selected: TGraphic;
  7.     Disabled: TGraphic;
  8.   end;
  9.  
  10.   TMenuItem = packed record
  11.     Enabled: Boolean;
  12.     Caption: UnicodeString;
  13.     Alignment: TAlignment;
  14.     Font: TFont;
  15.     BackgroundColor: TColor;
  16.     HighlightColor: TColor;
  17.     ImageSpacing: Integer;
  18.     Images: TMenuImage;
  19.     ImageStyle: TImageStyle;
  20.     Proportional: Boolean;
  21.     Stretch: Boolean;
  22.     Canvas: TCanvas;
  23.     Event: TNotifyEvent;
  24.     BorderStyle: TBorderStyle;
  25.     MinHeight: Integer;
  26.     Tag: Integer;
  27.   end;
  28.   TMenuItems = array of TMenuItem;
  29.  
  30.   TPopupEvent = procedure(Sender: TObject; ItemIndex: Integer) of object;
  31.   TPopupMenu = class(TComponent)
  32.     strict private
  33.       FMenuItems: TMenuItems;
  34.       FCount: Integer;
  35.       FItemIndex: Integer;
  36.       FPopupEvent: TPopupEvent;
  37.     private
  38.     strict protected
  39.     protected
  40.     public
  41.     published
  42.       property OnSelect: TPopupEvent read FPopupEvent write FPopupEvent;
  43.   end;
« Last Edit: Tomorrow at 31:76:97 xm by KodeZwerg »

lainz

  • Hero Member
  • *****
  • Posts: 4741
  • Web, Desktop & Android developer
    • https://lainz.github.io/
Re: [Solved] Custom PopupMenu
« Reply #14 on: August 19, 2023, 01:49:29 am »
The reason I didn't use a form in the popup is because the main form loses its activity.
You mean OnDeactivate/OnActivate gets fired? That is so, but since you're calling the pull-down by yourself you can easily flag it and guard against it.

Hi, I'm interested on this, how it can be done?

Because we have a custom combobox in bgracontrols, that has exactly this issue...

We're using a form, with a listbox.
« Last Edit: August 19, 2023, 01:55:10 am by lainz »

 

TinyPortal © 2005-2018