# VBForums CodeBank > CodeBank - Visual Basic 6 and earlier >  VB6 Dynamic Menu-, Popup- and Toolbar-Demo (vbRichClient-based)

## Schmidt

As the title says, just an example for dynamic Menu and ToolBar-handling,
based on the Graphics-Classes (the Widget- and Form-Engine) of vbRichClient5.dll:
http://vbrichclient.com/#/en/Downloads.htm

The contained Modules of the Demo:

modMenuResources.bas


```
Option Explicit

'this function returns a dynamically created Menu as a JSON-String (which could be stored in a DB, or elsewhere)
Public Function ExampleMenuAsJSONString() As String
Dim Root As cMenuItem
  Set Root = Cairo.CreateMenuItemRoot("MenuBar", "MenuBar")
 
  AddFileMenuEntriesTo Root.AddSubItem("File", "&File")
  AddEditMenuEntriesTo Root.AddSubItem("Edit", "&Edit")
  AddEditMenuEntriesTo Root.AddSubItem("Disabled", "&Disabled", , False)  'just to demonstrate a disabled entry
  AddExtrMenuEntriesTo Root.AddSubItem("Extras", "E&xtras")
  AddHelpMenuEntriesTo Root.AddSubItem("Help", "&Help")

  ExampleMenuAsJSONString = Root.ToJSONString
End Function

Public Sub AddFileMenuEntriesTo(MI As cMenuItem)
  MI.AddSubItem "New", "&New", "Document-New"
  MI.AddSubItem "Sep", "-"
  MI.AddSubItem "Open", "&Open...", "Document-Open"
  MI.AddSubItem "Save", "&Save", "Document-Save"
  MI.AddSubItem "SaveAs", "&Save as...", "Document-Save-As"
  MI.AddSubItem "Sep2", "-"
  MI.AddSubItem "ExitApp", "E&xit Application", "Application-Exit"
End Sub

Public Sub AddEditMenuEntriesTo(MI As cMenuItem)
  MI.AddSubItem "Cut", "C&ut", "Edit-Cut"
  MI.AddSubItem "Copy", "&Copy", "Edit-Copy"
  MI.AddSubItem "Paste", "&Paste", "Edit-Paste", CBool(Len(New_c.Clipboard.GetText))
  MI.AddSubItem "Delete", "&Delete", "Edit-Delete"
  MI.AddSubItem "Sep", "-" '<- a Menu-Separatorline-Definiton
  MI.AddSubItem "Select all", "&Select all", "Edit-Select-All"
End Sub

Public Sub AddExtrMenuEntriesTo(MI As cMenuItem)
Dim SubMenuPar As cMenuItem, SubSubMenuPar As cMenuItem
 
  MI.AddSubItem "Item1", "Menu-Item&1", "MenuIconKey1"
  MI.AddSubItem "Item2", "Menu-Item&2", "MenuIconKey3", False
  MI.AddSubItem "Item3", "-" '<- a Menu-Separatorline-Definiton
  MI.AddSubItem "Item4", "&Menu-Item2 disabled", "MenuIconKey1", , True
  Set SubMenuPar = MI.AddSubItem("Item5", "This pops up a &SubMenu", "MenuIconKey2")
 
    'two entries into the SubMenu (as children of 'Item5' of the above Code-Block)
    SubMenuPar.AddSubItem "SubItem1", "Caption SubItem1", "MenuIconKey1"
    Set SubSubMenuPar = SubMenuPar.AddSubItem("SubItem2", "Caption SubItem2", "MenuIconKey2")
  
      'and just 1 entry into the SubSubMenu (children of 'SubItem2' of the above Code-Block)
      SubSubMenuPar.AddSubItem "SubSubItem1", "Caption SubSubItem1", "MenuIconKey1"
End Sub
 
Public Sub AddHelpMenuEntriesTo(MI As cMenuItem)
  MI.AddSubItem "About", "&About", "About-Hint"
  MI.AddSubItem "Sep", "-"
  MI.AddSubItem "Index", "&Index...", "Help-Contents"
  MI.AddSubItem "Find", "&Find...", "Edit-Find"
End Sub
```

and modToolBarResources.bas


```
Option Explicit

Public Sub CreateToolBarEntriesOn(ToolBar As cwToolBar)
  ToolBar.AddItem "Home", "go-home", , , "normal Icon with 'IsCheckable = True'", , True
  ToolBar.AddItem "Undo", "go-previous", , , "normal Icon"
  ToolBar.AddItem "Redo", "go-next", , , "disabled Icon", False
  ToolBar.AddItem "Search", "page-zoom", , ddDropDown, "Icon with DropDownArrow"
  ToolBar.AddItem "Sep", , "-", , "Separator-Line"
  ToolBar.AddItem "TxtItem1", , "TxtItem1", , "plain Text-Item"
  ToolBar.AddItem "TxtItem2", "Document-Save-As", "TxtItem2", , "Text-Item with Icon"
  ToolBar.AddItem "Sep2", , "-", , "Separator-Line"
  ToolBar.AddItem "TxtItem3", , "TxtItem3", ddDropDown, "Text-Item with DropDown"
  ToolBar.AddItem "TxtItem4", "Edit-Find", "TxtItem4", ddDropDown, "Text-Item with Icon and DropDown"
  ToolBar.AddItem "Sep3", , "-", , "Separator-Line"
  ToolBar.AddItem "TxtItem5", "Document-Open", "TxtItem5", ddCrumbBar, "Text-Item with Icon and CrumbBar-Style-DropDown"
  ToolBar.AddItem "TxtItem6", , "TxtItem6", ddCrumbBar, "Text-Item with CrumbBar-Style-DropDown"
  ToolBar.AddItem "TxtItem7", , "TxtItem7", , "plain Text-Item"
End Sub
```

... contain the lines of code which are needed, to construct and achieve the following output:

MenuBar-DropDown:


ToolBar-DropDown as the result of a DropArrow-Click (showing a dynamic PopUp-Menu):


The constructed Menus use String-Keys to refer to previously loaded Icon and Image-Resources - 
and they can be serialized to JSON-Strings (storable in a DB for example).

Any imaginable modern Alpha-Image-Resource can be used, as e.g. *.png, *.ico - but also 
(as shown in the Demo) *.svg and *.svgz Vector-Images.

The example is completely ownerdrawn and truly independent from any MS-Menu-APIs, so one
can adapt *anything* as needed (e.g. the shape of the dropdown-form, colors, fonts, etc.) - 
though the Demo  as it is tries for a moderate style, mimicking a Win7-look roughly (with some
slight differences I personally like, but the whole thing is adaptable as said).

The code which implements this Menu-System is contained in one 'cf'-prefixed cWidgetForm-class
(cfPopUp for the hWnd-based Popups) - accompanied by 6 additional 'cw'-prefixed cWidgetBase-derived Classes:

cwMenuBar + cwMenuBarItem for the Menu-Strip
cwMenu + cwMenuItem for the DropDown-menus
cwToolBar + cwToolBarItem for the simple ToolBar-Implementation

I post this example more with regards to those, who want to learn how to program Widgets using 
the vbRichClient-lib...
The above mentioned cwSomething Classes are programmable very similar to a VB-UserControl
(internally the same Event-Set is provided with KeyDown, MouseMove, MouseWheel, MouseEnter/MouseLeave etc.)

E.g. the cwToolBar-WidgetClass has only 100 lines of code - and the cwToolBarItem only 130 - 
that's quite lean for what it does and offer, when you compare that with the efforts needed, 
when "fighting" with SubClassing and SendMessage Calls against e.g. the MS-CommonControls.  :Wink: 

There's not a single Win-API-call throughout the implementation - but that's normal 
for any framework, since they usually try to abstract from the underlying system.
The Menu- and ToolBar-Textrendering is Unicode-capable.

Have fun with it - here's the Zip-Download-Link: http://vbRichClient.com/Downloads/Me...oolbarDemo.zip

Olaf

----------


## ColinE66

Nice, Olaf. Quick 'behavioural' report; when one presses ALT the first menu item is activated (as it should be) but the arrow-left/right keys do not do anything (like they should).

However, if one presses ALT, followed by 'ESC', the arrow-key behaviour functions correctly. Haven't had time to trouble-shoot the code for this as yet so thought I'd post in case you were aware of a quick fix, being the author....

----------


## Schmidt

> Nice, Olaf. Quick 'behavioural' report; when one presses ALT the first menu item is activated (as it should be) but the arrow-left/right keys do not do anything (like they should).
> 
> However, if one presses ALT, followed by 'ESC', the arrow-key behaviour functions correctly. Haven't had time to trouble-shoot the code for this as yet so thought I'd post in case you were aware of a quick fix, being the author....


Ok, thanks for the report... "quick-fix-applied now"...  :Smilie:   (just re-download).

The behaviour is only seen, because I wrote this Demo using cWidgetForm-Panels, and then
placed them on a normal VB-TopLevel-Form as the Host. 

In case a cWidgetForm is used as the TopLevel-HostForm, then the mis-behaviour you've encountered is not happening.

Olaf

----------


## ColinE66

Working as expected, though I have observed some other quirks which I can report if you're interested? 

For example, press ALT, and then press ALT-X. Notice that you now appear to have two 'active' menus (though, of course, only one actually is) i.e. two top-level menus now have the 'active' outline effect.

----------


## Schmidt

> Working as expected, though I have observed some other quirks which I can report if you're interested? 
> 
> For example, press ALT, and then press ALT-X. Notice that you now appear to have two 'active' menus (though, of course, only one actually is) i.e. two top-level menus now have the 'active' outline effect.


Ah thanks - fixed now as well - also encountered that whilst a Bar-Menu is already in dropped-down-state
(e.g. when the File-Menu is dropped down), that you couldn't activate another Top-Menu per accelerator-keys
(e.g.per <Alt>+<x>, to switch over from the dropped-down-FileMenu to the Extras-Menu).

Download with these newest fixes again from the same link...

Olaf

----------


## ColinE66

I'll check it out...

I also noticed that when a sub-menu pops-up, the first item has the mouse-over effect even though the mouse isn't yet (and may never be) over that item. Looking at the code, I can see why this is; the line (from memory) about W.Focused or W.MouseOver is true because the first sub-menu item is focused as soon as the sub-menu's window is created, correct? Anyhow, perhaps the sub-menu window should remain un-activated until a mouse-over or key-press activates it? Just a thought. 

More of a quirk in behaviour than something that creates a real problem although it could be argued that it is unclear to the user what will happen when they press the enter key since the mouse is over one menu item but the focus is on another. Hope that makes sense.

----------


## Schmidt

> I'll check it out...
> 
> I also noticed that when a sub-menu pops-up, the first item has the mouse-over effect even though the mouse isn't yet (and may never be) over that item. Looking at the code, I can see why this is; the line (from memory) about W.Focused or W.MouseOver is true because the first sub-menu item is focused as soon as the sub-menu's window is created, correct? Anyhow, perhaps the sub-menu window should remain un-activated until a mouse-over or key-press activates it? Just a thought. 
> 
> More of a quirk in behaviour than something that creates a real problem although it could be argued that it is unclear to the user what will happen when they press the enter key since the mouse is over one menu item but the focus is on another. Hope that makes sense.


Makes sense perfectly - though not really a problem (IMO) - since when using the Mouse,
the Enter-Key will (with high probability) not be used - whilst when using the Keyboard for 
Menu-Interaction, the behaviour could almost be considered "a feature" <g>.

Olaf

----------


## ColinE66

Indeed. When using the keyboard, the current behaviour is as expected since the pop-up doesn't appear until the user presses the right arrow key and, in that case, it is natural for the first sub-menu item to become 'active' since it's parent window is also active. Just thought I'd mention it anyway even though I know that you do not necessarily subscribe to the idea that Windows UI behaviour should be meticulously mimicked.

btw, not to side-track the thread but any news on that DirectDraw issue?  :Wink:

----------


## ColinE66

Gah! Just noticed that latest version crashes when using the keyboard;
'path key has to start with the key of this item, which is; Menubar'

----------


## Schmidt

> Gah! Just noticed that latest version crashes when using the keyboard;
> 'path key has to start with the key of this item, which is; Menubar'


Oops, sorry - this is due to an enhancement on the cMenuItem.SubItemByPathKey method, 
which now understands also PathKeys, which do not include the Key of the cMenuItem itself
(e.g. the Key of the RootObject doesn't have to be given anmyore in the newest, not yet 
uploaded Version of the RichClient, which now supports both, the old mode and this new,
shorter notation)... the problem was, that I didn't switch back the Demo-Sources to the 
older (longer) path-Keys after my tests.

I've re-uploaded the Demo with the older Path-Key-Notations, which the older RC5-version supports.

Olaf

----------


## ColinE66

Cool. Loving it that you can describe a hard crash as the result of an enhancement, btw; ever considered a career in politics  :Wink:

----------


## Schmidt

> Cool. Loving it that you can describe a hard crash as the result of an enhancement, btw; ever considered a career in politics


Nah, an active SubClassing (as in cwMenu) can cause IDE-Crashes when unhandled COM-Errors are involved - 
if you'd use the cf and cw-Classes from a Dll-Binary (I've copied the Classes as Private ones into this Demo, 
to avoid vbWidgets.dll) - then you would have got a clean (debuggable) Error for the  non-resolvable
(too short) Key, your current RC5 doesn't support yet.

Olaf

----------


## ColinE66

Actually, it was trying its best to allow me to debug the error (the IDE debug dialog did appear) but it crashed hard if I tried to take up its offer!

----------


## Schmidt

> Actually, it was trying its best to allow me to debug the error (the IDE debug dialog did appear) but it crashed hard if I tried to take up its offer!


Just an attempt to explain the "why"...

... I get the same "hard freeze" here, when I cause an unhandled error in a routine 
which in IDE-mode would jump to the line in question - but that attempt of the IDE will then 
interact with the SubClassing in a way which is not healthy...

The SubClasser within RC5 (cSubClass) is already hardened for userplaced IDE-Breakpoints, 
which it allows and survives  - and it also has code in it, which survives IDE-StopButton presses - 
where it doesn't offer a safety-nets yet, is direct jumping of the IDE to the place where an 
unhandled Error "just happened" (in a context which involved an Event on a hWnd which was
subclassed).

And this latter problem becomes only apparent in (IDE-reachable) classes which use cSubClass -  
and are not (yet) contained and used from a Dll. In our case this class is cwMenu, which is using 
cSubClass to detect Non-Client-Area Mouseclicks (namely the Hosting-Forms Caption),
to be able to immediately hide the Popup-Window in this case...

Ok, but since cwMenu is the only WidgetClass in the whole vbWidgets-Project which uses internal 
SubClassing - and since what it subclasses is not "mission-critical" - I've now built-in a switch which 
simply disables the SubClassing when cwMenu is "exposed" as sourcecode in the IDE.

Uploaded a new version.

Olaf

----------


## ColinE66

Thanks for taking the time to explain.  :Smilie:

----------


## ColinE66

Just took another look at this as I (and it's a personal thing) found that mouse-over behaviour on the sub-menus a little annoying. Anyway, I made these code changes to get the effect I prefer.

cwMenu


```
Private Sub ShowSubMenu(ByVal MenuItem As cwMenuItem, ByVal WithFocus As Boolean)
  .
  .
  .
  End If
  Set mSubMenu = New cwMenu
      mSubMenu.Widget.FontSize = MenuItem.Widget.FontSize
      mSubMenu.InitAndShow MenuItem.Widget, WithFocus, MenuItem.SubMenuDS
End Sub

Public Sub InitAndShow(InitiatorWidget As cWidgetBase, WithFocus As Boolean, Optional DataSource As cMenuItem, Optional ByVal DirectionBehaviour As enmMenuPopUpPosition)
Dim MenuItem As cwMenuItem, PopupPosX As Single, PopupPosY As Single, MousePosX As Long, MousePosY As Long
 .
 .
 . 
  If Not TypeOf mInitiatorWidget.object Is cwMenu Then
    mInitiatorWidget.Root.BlockKeyEvents True, PopUp.Form
    Set SC = New_c.SubClass
    If App.LogMode Then SC.Hook mInitiatorWidget.Root.DialogFormHwnd
  Else 'a SubMenu
    If WithFocus And Widgets.Count Then Widgets(1).Widget.SetFocus
  End If
End Sub

Private Sub W_BubblingEvent(Sender As Object, EventName As String, P1 As Variant, P2 As Variant, P3 As Variant, P4 As Variant, P5 As Variant, P6 As Variant, P7 As Variant)
Dim SW As cWidgetBase
  .
  .
  .
    Case "ShowSubMenu"
      Set mActiveMenuItem = Sender
      ShowSubMenu P1, False
 
  End Select
'  Debug.Print "W_BubblingEvent", MenuItemKeyPath
End Sub

Public Sub KeyDown(KeyCode As Integer, Shift As Integer)
  . 
  .
  .
     Case vbKeyRight
      If Not MItem Is Nothing Then
        If MItem.Widget.Enabled And Not MItem.SubMenuDS Is Nothing Then
          ShowSubMenu MItem, True
          Exit Sub
        End If
      End If
  .
  .
  .
```

cwMenuBarItem


```
Public Sub ShowPopUp()
  .
  .
  .
  MenuBar.CurPopUp.Widget.FontSize = W.FontSize
  MenuBar.CurPopUp.InitAndShow W, True, mDataSource, PopupBehaviourDropDownLeftAligned
End Sub
```

fTest


```
Private Sub ToolBar_ArrowClick(Sender As cwToolBarItem)
Dim PopupRoot As cMenuItem
  Set PopupRoot = Cairo.CreateMenuItemRoot("PopUp", "PopUp")
  AddEditMenuEntriesTo PopupRoot
  
  Set PopUp = New cwMenu
      PopUp.InitAndShow Sender.Widget, True, PopupRoot, PopupBehaviourDropDownRightAligned
End Sub
```

I think that's all the changes - if I missed one here and there I'm pretty sure that it's obvious what I've done. And maybe you won't even like it anyway!  :Wink:

----------


## Schmidt

Thanks Colin, 

the behaviour makes more sense this way, so I've updated the Demo-project in this thread appropriately
(just re-download again).

Though I've changed the position of the 'WithFocus' Param to the end of the Public Methods (as an optional
Param), to not break applications which might already use the Menu-Classes from vbWidgets.dll.

(I keep the cwMenu-stuff from this Demo here in sync with vbWidgets.dll).

Olaf

----------


## ColinE66

> Though I've changed the position of the 'WithFocus' Param to the end of the Public Methods (as an optional
> Param), to not break applications which might already use the Menu-Classes from vbWidgets.dll.


Oops, yes, should've thought of that...

btw, just noticed that it is still a little imperfect; when moving the mouse off of a sub-menu and returning it to it's parent menu, the mouse effect remains on the sub-menu (which it shouldn't). Easily fixed if it's possible to make the 'just-departed' cwMenuItem lose its 'focused' state?

----------


## ColinE66

Been playing around a bit more with this to get more 'compliance' with conventional menus mouse behaviour; just a few small changes and I think it's there:

cwMenuItem:


```
'New module-level declaration
Private mDeactivatedViaMouseLeave As Boolean

'New Property
Public Property Get IsActive() As Boolean
   IsActive = W.MouseOver Or (W.Focused And Not mSubMenuDS Is Nothing) Or (W.Focused And Not mDeactivatedViaMouseLeave)
End Property

'New event handler
Private Sub W_GotFocus()
   mDeactivatedViaMouseLeave = False
End Sub

'modified event handler
Private Sub W_MouseLeave(ByVal MouseEnterWidget As cWidgetBase)
  Set tmrSubMenuHover = Nothing
  mDeactivatedViaMouseLeave = True
  W.Refresh
End Sub

'change to Draw sub
Private Sub Draw(CC As cCairoContext)
.
.
.
  If mCaption = "-" Then
    CC.DrawLine IcoOffsX, 4, dx - 2, 4, True, 1, &HD0D0D0, Alpha
    CC.DrawLine IcoOffsX, 5, dx - 2, 5, True, 1, vbWhite, Alpha
  Else
    If IsActive Then
      W.Alpha = 0.6
      CC.SetLineWidth 1, True
      Cairo.Theme.DrawTo CC, W, thmTypeListSelection, 0, 2, 0, dx - 4, dy, 3
    End If
.
.
.
End Sub
```

cwMenu



```
'change to event handler
Public Property Get MenuItemKeyPath() As String
  If Not mActiveMenuItem Is Nothing And Not mActiveMenuItem.IsActive Then Exit Property
  If mDataSource Is Nothing Then Exit Property
  MenuItemKeyPath = MenuKeyPath
  If Not mActiveMenuItem Is Nothing Then MenuItemKeyPath = MenuItemKeyPath & ">" & mActiveMenuItem.Widget.Key
End Property
```

Now, when the mouse leaves the menu form, nothing is highlighted and an enter key press will not do anything (standard behaviour)

----------


## ColinE66

Actually, IsActive can be reduced to 


```
Public Property Get IsActive() As Boolean
   IsActive = W.Focused And (Not mSubMenuDS Is Nothing Or Not mDeactivatedViaMouseLeave)
End Property
```

Since MouseEnter always sets focus to the widget

Also, need another line in here to handle cases where a widget already has focus
cwMenuItem


```
Private Sub W_MouseEnter(ByVal MouseLeaveWidget As cWidgetBase)
  If Not mSubMenuDS Is Nothing Then
   Set tmrSubMenuHover = New_c.Timer(350, True)
   Debug.Print mSubMenuDS.Caption, W.Parent.Active
End If
   mDeactivatedViaMouseLeave = False
  W.SetFocus
  W.Refresh
End Sub
```

Hopefully, that's it!

----------


## ColinE66

Throwing another one in - discard if you don't like! This one makes the up/down keys loop through the menu items so that, when the last item is bypassed the first is selected (or vice verse). If these are becoming unwelcome for any reason just say the word!  :Wink: 

cwMenu


```
Public Sub KeyDown(KeyCode As Integer, Shift As Integer)
  If Not mSubMenu Is Nothing Then mSubMenu.KeyDown KeyCode, Shift: Exit Sub
  
  Dim i As Long, j As Long, MItem As cwMenuItem, MItemIdx As Long, RM As cwMenu
.
.
.
    Case vbKeyUp
      For i = 1 To Widgets.Count
         j = MItemIdx - i
         If j < 1 Then j = Widgets.Count
        If Widgets(j).Widget.Enabled Then Widgets(j).Widget.SetFocus: Exit For
      Next i
    Case vbKeyDown
      For i = 1 To Widgets.Count
         j = MItemIdx + i
         If j > Widgets.Count Then j = 1
        If Widgets(j).Widget.Enabled Then Widgets(j).Widget.SetFocus: Exit For
      Next i
.
.
.
End Sub
```

----------


## Schmidt

Thanks Colin, 

re-uploaded the example in post #1 with your changes (also updated vbWidgets.dll).
Edit: now also with your new Menu-cycling from your post above

Any further differences always preferrably from that Zip, to not get "out of sync" in 
our efforts - thanks again.

Olaf

----------


## ColinE66

Thanks. Just the change in #21 if you want that behaviour (I changed the code form the previous zip but no impact either way)

----------


## Schmidt

Yep, #21 is now also in the Zip.

Olaf

----------


## ColinE66

Last few for now - sorry to bombard you. This time, just refining the pWithFocus param I introduced in the first of my changes back in #16:

cwMenuBar [ensure first menu item is active as it was triggered via keyboard]


```
Private Sub W_KeyDown(KeyCode As Integer, Shift As Integer)
.
.
.
    Case vbKeyDown:
      If Not MenuBarItem Is Nothing Then
        MenuBarItem.Widget.MouseLeave Nothing
        MenuBarItem.ShowPopUp True
        MenuBarItem.Widget.Refresh
      End If
.
.
.
End Sub

Private Sub CurPopUp_MenuBarEntryShift(ByVal ShiftLeft As Boolean)
.
.
.  
  mBlockDestroy = True
  MenuBarItem.ShowPopUp True
  MenuBarItem.Widget.Refresh
  W.Refresh
  mBlockDestroy = False
.
.
.
End Sub
```

cwMenuBarItem [ensure first menu item is active as it was triggered via keyboard]


```
Private Sub W_AccessKeyPress(KeyAscii As Integer)
  If W.Root.IsHidden Then Exit Sub
 
  If InStr(1, W.AccessKeys, Chr$(KeyAscii), vbTextCompare) Then
    W.Parent.object.CleanupActiveState W
    W.SetFocus
    ShowPopUp True
    W.Refresh
    W.Parent.Refresh
  End If
End Sub

Public Sub ShowPopUp(Optional pWithFocus As Boolean)
Dim MenuBar As cwMenuBar
  If Not W.Enabled Then Exit Sub

  Set MenuBar = W.Parent.object
  If Not MenuBar.CurPopUp Is Nothing Then
    If Not MenuBar.CurPopUp.DataSource Is mDataSource Then MenuBar.CurPopUp.DestroyPopup
  End If
  Set MenuBar.CurPopUp = New cwMenu
  MenuBar.CurPopUp.Widget.FontSize = W.FontSize
  MenuBar.CurPopUp.InitAndShow W, mDataSource, PopupBehaviourDropDownLeftAligned, pWithFocus
End Sub
```

cwMenu


```
Public Sub InitAndShow(InitiatorWidget As cWidgetBase, Optional DataSource As cMenuItem, Optional ByVal DirectionBehaviour As enmMenuPopUpPosition, Optional ByVal WithFocus As Boolean)
.
.
.
  If Not TypeOf mInitiatorWidget.object Is cwMenu Then
    mInitiatorWidget.Root.BlockKeyEvents True, PopUp.Form
    Set SC = New_c.SubClass
    If App.LogMode Then SC.Hook mInitiatorWidget.Root.DialogFormHwnd
'  Else 'a SubMenu 'Remove this line
'    If WithFocus And Widgets.Count > 0 Then Widgets(1).Widget.SetFocus 'Remove this line
  End If
    If WithFocus And Widgets.Count > 0 Then Widgets(1).Widget.SetFocus
End Sub
```


If I got that right, the only time the first menu item is ever activated is when the menu was triggered to appear via the keyboard; no mouse-triggered menu appearance should have any menu item active.

----------


## Schmidt

Ok, updated and re-uploaded with the changes in #25 now...

Olaf

----------


## ColinE66

HI again. I've successfully added keyboard shortcuts to menu items (Ctrl+V) for paste, etc. Currently, I am adding a suffix to a menu's caption to implement this

e.g. MI.AddSubItem "Save", "&Save|Ctrl+Shift+V", "Document-Save"

and then, when setting the DataSource for a cwMenuBar, I recursively walk down the menu, populating a cCollection with the MenuItem path. Each member of this collection has a key representing the required Ctrl-Shift Modifier + Keycode) 

i.e. 3+86 = Ctrl(1) + Shift(2) + Keycode for letter V (86) contains Edit>Copy

Whilst doing this, I build up a string for the KeyWatcher containing all the KeyCodes to be monitored and have modified the KeyWatcher's KeyDown event so that it calls a new sub



```
Private Sub CheckForKeyBoardShortcut(vKey As Integer)
Dim pMenuItemPath As String
   If mKeyboardShortcuts.Exists(mCtrlDown + mShiftDown & "+" & vKey) Then
      pMenuItemPath = mKeyboardShortcuts(mCtrlDown + mShiftDown & "+" & vKey)
      If mDataSource.SubItemByPathKey(mDataSource.Key & ">" & pMenuItemPath).Enabled Then
         CurPopUp_Click pMenuItemPath
      End If
   End If
End Sub
```

I can continue with this if you like but will need some support from you, I suspect. For instance, re-setting the KeyWatcher with a new comma-separated list appears to stop it from functioning if it has already been started, regardless of what I try. Help!

Also, I will need to re-measure all the MenuItems in order to right-align the short-cut text tidily.

Also, also, not sure that implementing via the caption text is a good idea; opinion, please. Maybe it belongs in the underlying cMenuItems class. I just did it this way as proof-of-concept.

Ask, if something didn't make sense or you want to see more code...

----------


## Schmidt

> I've successfully added keyboard shortcuts to menu items (Ctrl+V) for paste, etc. Currently, I am adding a suffix to a menu's caption to implement this
> 
> e.g. MI.AddSubItem "Save", "&Save|Ctrl+Shift+V", "Document-Save"
> ...
> Also, I will need to re-measure all the MenuItems in order to right-align the short-cut text tidily.


That alone would be a nice addition to have (detecting the Pipe in a Caption-Description,
recalcalculating the resulting lenght of the Menu-Text - and then rendering that Info-Text
right-aligned in the cwMenuItem.

But I would stop there for the moment (the Menu-Item only showing the Info-Text) -
not really sure what would be the best way to proceed from there (where  the Events
should be handled or raised from).




> I can continue with this if you like but will need some support from you, I suspect. For instance, re-setting the KeyWatcher with a new comma-separated list appears to stop it from functioning if it has already been started, regardless of what I try. Help!


Normally it should work, when you overwrite an existing (WithEvents declared)
cKeyWatcher-Instance with a new one...

Private WithEvents MyKeyWatcher As cKeyWatcher

Set MyKeyWatcher = New_c.KeyWatcher(NewVKeyList)

Hmm, not yet sure if I'd rather like to handle this <Ctrl>+Key stuff over Form KeyPreview - 
and Keydown/Up-Events, implementing the <Ctrl>+Something Handling there (fullfilling 
the Promise of the Menu's "Info-only Captions" not in the Menu-System itself). 

If you find a way to solve it over the Key-Watcher directly, resulting in a Menu-Event, 
then Ok - but some Control-widgets as e.g. the cwTextBox already come with their
own KeyDown-Event Handling for <Ctrl>+C and <Ctrl>+V etc. - though that could 
be solved with an Extra-Property on the Textbox perhaps - or by checking in the central
<Ctrl>+C Handler, if the currently focused Widget if of Type cwTextBox ...
If TypeOf cWidgetForm.Root.ActiveWidget Is cwTextBox...




> ... also, not sure that implementing via the caption text is a good idea; opinion, please. Maybe it belongs in the underlying cMenuItems class. I just did it this way as proof-of-concept.


I see nothing wrong with the Pipe-Char delimiter in the Caption-String - since the Caption-Strings
are potential candidates to have defined in a language-resource - and what's written in english as:
"&Save|Ctrl+Shift+V"
would in a german language-table-column written as:
"&Speichern|Strg+Umsch+V"

So, it's indeed a good idea to have it in the Caption itself, to already cover proper translation in one place.

Olaf

----------


## ColinE66

Just a quick note as I'm off to work; it is not implemented in a form keypreview - it's in the cwMenuBar class. I think this is a good place for it as only menu items on a menu bar should have shortcuts. Here's a few code snips to give you a better idea (not tidied up as I'm still playing).

cwMenuBar


```
Public Property Set DataSource(NewValue As cMenuItem)
.
.
.
   Set KWatch = Nothing
   Set mKeyboardShortcuts = New cCollection
   mKeyWatcherCodes = "9,16,17,18"
.
.
.
  For i = 0 To mDataSource.SubItemCount - 1
    TxtWidth = CC.GetTextExtents(mDataSource.SubItemByIndex(i).Caption, FontHeight)
.
.
.
    RegisterKeyboardShortcuts mDataSource.SubItemByIndex(i), mDataSource.SubItemByIndex(i).Key
  Next i
  Set KWatch = New_c.KeyWatcher(mKeyWatcherCodes)
  W.Refresh
End Property


Private Sub RegisterKeyboardShortcuts(pItem As cMenuItem, pPath As String)
Dim i As Long, s() As String, ModifierValue As Long, KeyCode As Long
   Debug.Print pItem.Caption, pItem.Key, pPath
   If InStr(pItem.Caption, "|") Then
      s = Split(pItem.Caption, "|")
      ModifierValue = IIf(InStr(s(1), "Ctrl+"), 1, 0) + IIf(InStr(s(1), "Shift+"), 2, 0)
      s = Split(s(1), "+")
      KeyCode = Asc(s(1))
      If Not mKeyboardShortcuts.Exists(ModifierValue & "+" & KeyCode) Then
        mKeyboardShortcuts.Add pPath, ModifierValue & "+" & KeyCode
        mKeyWatcherCodes = mKeyWatcherCodes & "," & KeyCode
      End If
   End If
   For i = 0 To pItem.SubItemCount - 1
     RegisterKeyboardShortcuts pItem.SubItemByIndex(i), pPath & ">" & pItem.SubItemByIndex(i).Key
   Next i
End Sub

```




```
Private Sub KWatch_VKeyDown(ByVal vKey As Integer, ByVal MapIdx As Long)
  If vKey = 9 Or vKey = 16 Or vKey = 17 Then TabOrShiftOrCtrlKeyDown = True
  If vKey = 17 Then mCtrlDown = 1
  If vKey = 16 Then mShiftDown = 2

  CheckForKeyBoardShortcut vKey  
  If vKey = 18 And Not TabOrShiftOrCtrlKeyDown Then
    AltDown = CurPopUp Is Nothing
    If Not Screen.ActiveForm Is Nothing Then 'we send an Esc-KeyUp-Event in case of a VB-Form as TopLevel-Host
      Dim Evt(0 To 27) As Byte: Evt(0) = 1: Evt(4) = vbKeyEscape: Evt(8) = 2 '<- flagged as KeyUp
      SendInput 1, Evt(0), UBound(Evt) + 1
    End If
    If Not CurPopUp Is Nothing Then
      CurPopUp.DestroyPopup
      Set CurPopUp = Nothing
    End If
  End If
End Sub
```



```
Private Sub CheckForKeyBoardShortcut(vKey As Integer)
Dim pMenuItemPath As String
   If Not CurPopUp Is Nothing Then Exit Sub
   Debug.Print mCtrlDown + mShiftDown, vKey
   If mKeyboardShortcuts.Exists(mCtrlDown + mShiftDown & "+" & vKey) Then
      pMenuItemPath = mKeyboardShortcuts(mCtrlDown + mShiftDown & "+" & vKey)
      If mDataSource.SubItemByPathKey(mDataSource.Key & ">" & pMenuItemPath).Enabled Then
         CurPopUp_Click pMenuItemPath
      End If
   End If
End Sub
```

That's not everything but it should help to clarify my approach...

----------


## jpbro

> I see nothing wrong with the Pipe-Char delimiter in the Caption-String - since the Caption-Strings
> are potential candidates to have defined in a language-resource - and what's written in english as:
> "&Save|Ctrl+Shift+V"
> would in a german language-table-column written as:
> "&Speichern|Strg+Umsch+V"


Just a thought - but in some cases we might want to offer dynamic user-defined captions, and a user might want to use the pipe character (however unlikely). This would need some extra escaping code, so perhaps it would be better to use vbTab for the separator? I guess a knock against it is that it would involve a bit of extra typing though... "&Save" & vbTab & "Ctrl+Shift+V"

A more complete approach (though perhaps too much effort?) would be to have a Shortcut property and corresponding class that offered properties such as Enabled (Boolean), Ctrl (Boolean), Alt (Boolean), Shift (Boolean), KeyCode (Integer) and then the menu drawing class itself could handle displaying the shortcut in the appropriate language (although this would also required a mechanism to provide translations, but this could become a community effort).

----------


## Schmidt

> Just a thought - but in some cases we might want to offer dynamic user-defined captions, and a user might want to use the pipe character (however unlikely). This would need some extra escaping code, so perhaps it would be better to use vbTab for the separator? I guess a knock against it is that it would involve a bit of extra typing though... "&Save" & vbTab & "Ctrl+Shift+V"


I'd like a "typeable char", which is easy to enter into a DataGrid-Cell or a TextBox 
(without causing Focus-Switching or something like that - always having the 
"language-translator-guy" in mind, who's wading through the text-columns).

The Pipe-Char is (IMO) already unlikely enough - and we could handle it in the same way 
as the &-Char currently is handled (a user who wants it in a normal Caption would have
to "escape" it by typing it twice).




> A more complete approach (though perhaps too much effort?) would be to have a Shortcut property and corresponding class that offered properties such as Enabled (Boolean), Ctrl (Boolean), Alt (Boolean), Shift (Boolean), KeyCode (Integer) and then the menu drawing class itself could handle displaying the shortcut in the appropriate language (although this would also required a mechanism to provide translations, but this could become a community effort).


The Enabled-State, as well as the Alt-Key Handling (per &) is already covered in the current approach.
There's only the <Ctrl>+Something and the <Shift>+Something which needs to be added...


And there is already an Event which can be used for dynamic Menu-Text-Translations
(named 'ReplaceCaption' and raised by the cwMenu-Class shortly before "showing")

The following Zip contains a ProjectGroup  (_TestGroup.vbg),
which joins the two Projects: PopUpDemo.vbp and vbWidgets.vbp
and shows the usage of this Event in cfMain.cls of the PopupDemo-Project.

http://vbRichClient.com/Downloads/PopUpMenus.zip
(showing the interaction of a Cut/Copy/Paste/SelectAll popup with the cwTextBox-Widget,
including dynamic lang-switching)

Olaf

----------


## ColinE66

Right guys, I suspect there's more mileage left in the language discussion but I'm posting my code as of now just so y'all can see it in action and think a little more about the best way to implement this. As it stands, if the words 'Ctrl' and 'Shift' are not universal then this approach is a complete non-starter!

I have made no attempt to version control my changes in the attachment, I'm afraid, as I'm still playing around but the changes are not significant; Olaf will spot them immediately given that he is the original author, of course. 

It's just presented here for information and to provoke thought about implementation details...

MenuAndToolbarDemoMOD.zip

----------


## Schmidt

> Right guys, I suspect there's more mileage left in the language discussion but I'm posting my code as of now just so y'all can see it in action and think a little more about the best way to implement this. As it stands, if the words 'Ctrl' and 'Shift' are not universal then this approach is a complete non-starter!


I've introduced two Public String-Properties (LocalizedCtrlMarker and LocalizedShiftMarker) into cwMenuBar to handle that
(those are per default at the english localized Strings "Ctrl" and "Shift")




> I have made no attempt to version control my changes in the attachment, I'm afraid, as I'm still playing around but the changes are not significant; Olaf will spot them immediately given that he is the original author, of course.


Thanks for the Code - so far everything is looking nice - so I took over your changes (only slight adaptions in cwMenuBar) - 
and recommend a new download of the original Demo in Post #1, to work further from there, for the next potential "Diff" 
(Ping-Pong-principle so to say, when you post a Zip with changes, wait for the update of the "Post #1 download-sources" 
before adding new stuff).

But if possible in small(er) increments, since these are easier to follow... (sometimes that's difficult, I know)  :Wink: 

Olaf

----------


## ColinE66

Downloaded new zip from #1...

fyi, it wasn't my intention that you'd need to pick through the stuff in #32 to find the differences; I'd have done that for you had you been happy with the approach I took and the way it functioned. Anyway, hope it wasn't too onerous for you...

btw, how much more robust do you want me to make the modifier/keycode parsing? You will have seen that it was pretty much thrown-together up til now. Obviously, this aspect could grow by quite a few lines of code, if you'd like? However, it isn't really that difficult for the developer to conform to a (e.g.) '|Ctrl+Shift+V' style of notation. Your call....?

EDIT: 
I meant to point out that the current design has implications on what will be returned by the Caption property of a cwMenuItem and also the fact that the property Let for the Caption property will not, of course, re-trigger the re-population of the shortcut handling collection in the parent cwMenuBar. Just food for thought...

----------


## Schmidt

> Downloaded new zip from #1...
> 
> fyi, it wasn't my intention that you'd need to pick through the stuff in #32 to find the differences; I'd have done that for you had you been happy with the approach I took and the way it functioned. Anyway, hope it wasn't too onerous for you...


No - the larger change took (in Sum) perhaps the same time, as 3 smaller ones - 
it's just that those 3 smaller ones could be handled "somewhere in-between" more
easily - larger changes require a larger "contiguos timespan", which is not available everyday - 
but Monday was a Holiday in germany - so I had this larger timeframe to react immediately - 
no problem.




> btw, how much more robust do you want me to make the modifier/keycode parsing? You will have seen that it was pretty much thrown-together up til now. Obviously, this aspect could grow by quite a few lines of code, if you'd like? However, it isn't really that difficult for the developer to conform to a (e.g.) '|Ctrl+Shift+V' style of notation. Your call....?


I would leave it as it is for the time being - we already tried to think ahead with the two new Public Props on 
the interface, which allow to specify the "Ctrl" and "Shift" parsing-strings for other locales.
The Public interface is what makes the most worries later on, when it's lacking some things - internal stuff
can be fixed and hardened over time...

I plan to include at least the intrinsic-Widgets (CommandButton, Label, Frame, TextBox, Image - 
as well as the MenuSystem we have isolated here) directly into vbRichClient6 at the end of the year - 
for that the Public Interfaces need to be stable - the internal stuff is easier to understand, fix and enhance, 
when it's still kept as simple as possible in each Widget - so let's keep up with KISS for a while longer  :Wink: 




> I meant to point out that the current design has implications on what will be returned by the Caption property of a cwMenuItem and also the fact that the property Let for the Caption property will not, of course, re-trigger the re-population of the shortcut handling collection in the parent cwMenuBar. Just food for thought...


Yes - I'm aware of that - but thought that your current parsing-routine (the one which currently
takes the MenuItem as its first Parameter) could be made Friend - and then just called from the 
"deeper-layers" of the Menu-Hierarchy, since it is quite easy to reach the Parent-Widget (in this case
cwMenuBar) from any Child-Widget deeper down (then in turn calling the Friend-tagged Parsing-Function).

I perhaps forgot two or three stumbling-points, but still think that it's solvable internally 
(without introducing new things on the Public Widget-Interfaces...).

Thanks again for your work on this.

Olaf

----------


## ColinE66

OK - I'll leave the parsing as-is, then...

One last change. Remember how, occasionally, a menubaritem can appear to be 'active? You can replicate this by pressing alt and clicking on another app's window and back again; notice how the menu still appears to be active (outline effect)?

Well, anyway, it occurred to me that this could be eliminated by reacting to when the app loses focus. Now, I know that your WidgetForms support such an event so there may be a better way of solving this 'issue'  :Wink:  but, in terms of your vb-Form-hosted demo, I got around the problem as follows:

fTest


```
Private WithEvents SC As cSubClass

Private Sub SC_WindowProc(Result As Long, ByVal Msg As Long, ByVal wParam As Long, ByVal lParam As Long)
  Select Case Msg 
    Case &H1C 'app activate/deactivate
      MenuBar.CleanupActiveState , True
  End Select
  Result = SC.CallWindowProc(Msg, wParam, lParam)
End Sub
```

and

cwMenuBar


```
Public Sub CleanupActiveState(Optional ExcludeW As cWidgetBase, Optional AppFocusedStateChanged As Boolean)
Dim MenuBarItem As cwMenuBarItem
   mKeyWatcherCodesChanged = AppFocusedStateChanged
  For Each MenuBarItem In Widgets
    If Not ExcludeW Is MenuBarItem.Widget Then MenuBarItem.Widget.MouseLeave W
    MenuBarItem.Widget.Refresh
  Next
End Sub
```

It basically codes around your IsTopWindowActive test in Sub ShortcutActivation and has the side-effect of turning the KeyWatcher on and off. As said, that is just a side-effect but, because the impact of that is so minimal, it seems like a good one-line-of-code-fix to a problem, even if that solution is not necessarily elegant or intuitive with regard to future code maintenance.

EDIT:

Actually, I should explain a little more: Without resetting the mKeyWatcherCodesChanged flag, it will have required many more lines of code to achieve a fix because of the behaviour of the widget's refresh mechanism i.e. always calling the ShortcutActivation sub and therefore failing the IsTopWindowActive test when the app loses focus. I'm sure you know that anyway but it only took a few seconds more to read this!  :Wink:

----------


## Schmidt

I've now built-in a solution for the HoveredMenu-Deactivation-Routine (without SubClassing, using a Timer) - 
and also cleaned up a bit around the mKeyWatcherCodesChanged-stuff (which is gone 
and  not needed anymore).

Please check-out the new version from #1...

Olaf

----------


## ColinE66

All good, Olaf. I now declare your menus to be officially awesome  :Smilie:

----------


## jpbro

I've discovered a couple of issues with the current/latest menu implementation. I'm trying to familiarize myself with the code to see if I can fix the issues myself, but I figured I would report them here in case Olaf or Colin can see a quick and easy fix (being more familiar with the code).

1) If you move focus to another program, then return focus to the demo app, then press Alt+F (to try to activate the file menu), then only the parent menu bar gets focus (the menu doesn't appear). A subsequent Alt+F will flash the menu, but it will disappear. A third Alt+F will show the menu as expected.

2) In native Windows menus, pressing Alt (and releasing) and then pressing F will show the File menu. This doesn't happen with the vbWidgets menus.

----------


## ColinE66

Good catch.

With regard to (2), this will fix it if Olaf is happy with it:

cwMenubar


```
Private Sub W_KeyDown(KeyCode As Integer, Shift As Integer)
  If Not CurPopUp Is Nothing Then Exit Sub
  Dim MenuBarItem As cwMenuBarItem
  For Each MenuBarItem In Widgets
    If MenuBarItem.Widget.MouseOver And KeyCode = vbKeyDown Then 
      Exit For
    ElseIf InStr(1, MenuBarItem.Widget.AccessKeys, Chr$(KeyCode), vbTextCompare) Then
      CleanupActiveState
      KeyCode = vbKeyDown 'pretend the down arrow was pressed
      Exit For
    End If
  Next
  Select Case KeyCode
    Case vbKeyEscape
      If Not MenuBarItem Is Nothing Then MenuBarItem.Widget.MouseLeave Nothing
      W.Root.Widget.MouseUp 0, 0, -1, -1
      W.Root.Refresh
    Case vbKeyDown:
      If Not MenuBarItem Is Nothing Then
        MenuBarItem.Widget.MouseLeave Nothing
        MenuBarItem.ShowPopUp True
        MenuBarItem.Widget.Refresh
      End If
    Case vbKeyLeft:  CurPopUp_MenuBarEntryShift True
    Case vbKeyRight: CurPopUp_MenuBarEntryShift False
  End Select
End Sub
```

----------


## Schmidt

Taken over the changes in #40 now - and also think, that the point 1 in jpbro's posting #39 is now covered as well.

Please download again from #1

Olaf

----------


## ColinE66

Thanks Olaf.

Quick bug report: If one presses ALT and then right and then ALT again, the menu should be destroyed

Quick fix:

cwMenuBar:


```
Private Sub KWatch_VKeyDown(ByVal vKey As Integer, ByVal MapIdx As Long)
  If vKey = 9 Or vKey = 16 Or vKey = 17 Then TabOrShiftOrCtrlKeyDown = True
  If vKey = 17 Then mCtrlDown = 1
  If vKey = 16 Then mShiftDown = 2
 
  CheckForKeyBoardShortcut vKey
  
  If vKey = 18 Then
    W.Root.WidgetForm.SetFocus
    W.SetFocus
    If Not CurPopUp Is Nothing Then CurPopUp.DestroyPopup: Set CurPopUp = Nothing : MenuBarActive = True
    If Not Screen.ActiveForm Is Nothing Then 'we send an Esc-KeyUp-Event in case of a VB-Form as TopLevel-Host
      Dim Evt(0 To 27) As Byte: Evt(0) = 1: Evt(4) = vbKeyEscape: Evt(8) = 2 '<- flagged as KeyUp
      SendInput 1, Evt(0), UBound(Evt) + 1
    End If
  End If
End Sub
```

That way it gets immediately toggled to false in the VKeyUp event for which I also suggest the following readability change



```
    If Not MenuBarActive And Not TabOrShiftOrCtrlKeyDown And Widgets.Count > 0 Then
      MenuBarActive = True
    ElseIf MenuBarActive And Not TabOrShiftOrCtrlKeyDown And Widgets.Count > 0 Then
      MenuBarActive = False
    End If
```

Equivalent to



```
    If Not TabOrShiftOrCtrlKeyDown And Widgets.Count > 0 Then MenuBarActive = Not MenuBarActive
```

----------


## Schmidt

Applied these suggestions too - new download under #1

Olaf

----------


## ColinE66

Cool. One last _tiny_ suggestion. Currently, when pressing ALT, if one then presses the right (or left) arrow, we get the 'menu shift' effect. All good there, but the menu that is shifted to gets dropped down, as currently coded.

This fixes that;



```
Private Sub CurPopUp_MenuBarEntryShift(ByVal ShiftLeft As Boolean)
.
.
.
  mBlockDestroy = True
  If MenuBarActive Then
    MenuBarItem.Widget.MouseEnter Nothing
  Else
    MenuBarItem.ShowPopUp True
  End If
  MenuBarItem.Widget.Refresh
  W.Refresh
  mBlockDestroy = False
End Sub
```

to replace


```
  mBlockDestroy = True
  MenuBarActive = False: MenuBarItem.ShowPopUp True
  MenuBarItem.Widget.Refresh
  W.Refresh
  mBlockDestroy = False
```

And that is pretty much all I have to offer on this subject, I think!

----------


## Schmidt

Ok, now also taken over (for the sake of compatibility with the MS-Menu-behaviour).

New Download under #1.

Thanks again Colin.

Olaf

----------


## alpmann

Hello Olaf,

is it possible to use this dynamic menu as notification-icon with the menu as context menu?

Regards,
Matthias

----------


## CreativeDreamer

H Olaf.

Thanks for the great Menu-popup-toolbar code. I was playing around with the DLL of Office XP and .NET Style ActiveX V 1.61 that was on http://www.xpstyle-menu.com/  .The site had disappeared so I'm glad I came across your code. 

I still need to try out the menu system, as I like seeing how things work. Thanks once again, Dreamer.

----------


## smileyoufu

When the function is disabled, the text moves to the left.
        ToolBar.AddItem "TxtItem2", "Document-Save-As", "TxtItem2", , "Text-Item with Icon", False   'Enabled  = False  -> Bug,When the function is disabled, the text moves to the left
        ToolBar.AddItem "TxtItem5", "Document-Open", "TxtItem5", ddCrumbBar, "Text-Item with Icon and CrumbBar-Style-DropDown", False  'Enabled  = False  -> Bug,When the function is disabled, the text moves to the left

----------


## jpbro

> When the function is disabled, the text moves to the left.
>         ToolBar.AddItem "TxtItem2", "Document-Save-As", "TxtItem2", , "Text-Item with Icon", False   'Enabled  = False  -> Bug,When the function is disabled, the text moves to the left
>         ToolBar.AddItem "TxtItem5", "Document-Open", "TxtItem5", ddCrumbBar, "Text-Item with Icon and CrumbBar-Style-DropDown", False  'Enabled  = False  -> Bug,When the function is disabled, the text moves to the left


In the Draw method of the cwToolbarItem class, you can add this code to the _Else ' Disabled State_ block to fix this issue:



```
    If Len(W.ImageKey) Then
      xOffs = dy
    End If
```

----------


## jpbro

Olaf, a quick question about widget state drawing - Should there be a disabled state option for things like drawing the toolbar arrows? Right now they appear black when the parent widget is disabled, but I think it would be more appropriate to draw them in a disabled color. I see states like Hovered, Checked, Pressed, etc..., but not Disabled. Or is there a more appropriate way to accomplish this with the widget library?

----------


## Schmidt

> Olaf, a quick question about widget state drawing - Should there be a disabled state option for things like drawing the toolbar arrows? Right now they appear black when the parent widget is disabled, but I think it would be more appropriate to draw them in a disabled color. I see states like Hovered, Checked, Pressed, etc..., but not Disabled. Or is there a more appropriate way to accomplish this with the widget library?


Erm,the W.Enabled state reflects that Disabled State (when it returns False).
And each single Child-Widget (down the hierarchy) "inherits" the returned result, 
e.g. when the ParentControl in question was set to e.g.:

MyToolBar.Widget.Enabled = False
then all the cwToolBarItem-ChildWidgets will reflect this disabled parent-state in their own 
W.Enabled property (and can use that State-Info to properly redraw themselves).

Maybe in your own Tests you missed, calling a ...Widget.Refresh or something?
(the philosophy in the WidgetEngine is, to avoid doing too many "AutoRefresh-calls"
under the covers, when Widget-Properties are changed - to avoid performance-
issues, when certain Widget-Props are changed or initialized "in a loop" - 
so, manual Refreshs are needed after changing most Widget-Props (in case 
you want to see the immediate Effects).

Thanks for posting the code-snippet to fix the little Bug in cwToolBarItem - 
will integrate that into the vbWidgets-Repo.

Olaf

----------


## jpbro

Sorry for not being clearer. What I mean is that the arrow of a _disabled_ cwToolbarItem is drawn identically to an arrow of an _enabled_ cwToolbarItem.  For example, compare:



In other UIs that I'm familiar with, the arrow would be drawn in the "grayed" color (same as the disabled text color) to indicate that the arrow too is disabled.

What I was thinking was that it would be accomplished in the Draw method of the cwToolbarItem class with something like:

  If ArrowType Then
      Cairo.Theme.DrawTo CC, W, thmTypeArrow, *thmStateDisabled*, dx - 11.5, (dy - ArrowSize) \ 2, ArrowSize, ArrowSize, 0, thmDirectionDown
  End If

But there is no _thmStateDisabled_. I'm still getting familiar with the widget part of the library, so it's likely that my expectations are just out of sync with your implementations. Is there a way to draw the arrow in the disabled text color?

----------


## Schmidt

> Sorry for not being clearer. What I mean is that the arrow of a _disabled_ cwToolbarItem is drawn identically to an arrow of an _enabled_ cwToolbarItem.  For example, compare:


Ah, I see - I see, sorry...

Simple fix (directly within the cThemeWin7-Class) - then affecting all Arrow-Drawings automatically:

In case you don't want to wait on my next vbWidgets-GitHub-Push you can do that by finding the snippet: 


```
Case thmTypeArrow
        CC.TranslateDrawings (X + dx * 0.5) - 0.04, (y + dy * 0.5) + 0.22
        Angle = GetAngle(Direction)
        
        CC.RotateDrawingsDeg Angle
          CC.SetLineWidth 0.4
'            CC.MoveTo -Int(dx * 0.28) + 0.15, 0
'            CC.LineTo Int(dx * 0.28) + 0.45, -Int(dy * 0.5) - 0.45
'            CC.LineTo Int(dx * 0.28) + 0.45, Int(dy * 0.5) + 0.45
            CC.MoveTo -(dx * 0.29), 0
            CC.LineTo (dx * 0.29), -(dy * 0.58)
            CC.LineTo (dx * 0.29), (dy * 0.58)
          CC.ClosePath
        CC.RotateDrawingsDeg -Angle
        
        Color = W.ForeColor
...
```

Then just changing the last line above (which I've marked magenta) to:


```
     Color = IIf(W.Enabled, W.ForeColor, W.DisabledColor)
```

Just updated the cTheme-Class which is directly built into the RC5 as well with this small change
(coming in the next release - but when vbWidgets.dll is used, it currently overrides the
RC5-Theme-Instance anyways with its own one (cThemeWin7).

Olaf

----------


## smileyoufu

I do appreciate your timely help.The problem is resolved perfectly

----------


## smileyoufu

Will toolbar icons can achieve great style it?
Style icon：

----------


## smileyoufu

[VB6 Dynamic Menu-, Popup- and Toolbar-Demo] -----Error
Error step:.
1 to compile the project as a EXE file
2 executive menu command "File->Open->cancel",
3 exit EXE file
4 mistake


-----------------------------
Error step:
step1: IDE Moee create a cWidgetForm object
step2: IDE Moee create call "New_c.FSO.ShowOpenDialog" method
step3: Conversion project for exe files
step4: Execute "ExeExitErrorDemo.exe" file        (IDE Mode OK,Not error)
step5: Click the Browse button
step6: click Cancel button
Step7: fourth: exit the "ExeExitErrorDemo.exe" file
step8: there is an error

Demo:
ExeExitErrorDemo(vbRichClient5).zip


undefined Code:
Option Explicit'to reproduce the fault, the project must be compiled into a EXE file. In IDE mode, there will be no fault. 要重现该故障，必需将此工程编译为exe文件。在IDE模式下，不会出现故障的Private pnlTool As cWidgetForm Private Sub Form_Load()    Set pnlTool = Cairo.WidgetForms.CreateChild(Me.hWnd) '"After the cWidgetForm object is created, an error will occur.  创建cWidgetForm 对象后，才会发生错误End Sub Private Sub Form_Terminate()    If Forms.Count = 0 Then New_c.CleanupRichClientDllEnd Sub Private Sub btnBrowse_Click()    Dim strFilePath As String    strFilePath = OsdOpenDlg(Me.hWnd, "", "openregdlg")     'API OpenDlg Demo err    If Len(strFilePath) Then        MsgBox strFilePath    End IfEnd Sub Private Sub Command1_Click()    Dim Path As String    Path = New_c.FSO.ShowOpenDialog(, App.Path, , , , , Me.hWnd) 'New_c.FSO OpenDlg Demo err    If Len(Path) Then MsgBox PathEnd Sub

----------


## smileyoufu



----------


## jpbro

Hi smileyoufu - I can't reproduce the problem here with your demo, are you using the latest version of the vbRC5 library?

----------


## smileyoufu

Hi jpbro - Thank you very much for your help and reply.
My Rc5 Version：5.0.50  LastUpdate：2016-7-21 22:31
I'm sorry, maybe I didn't describe it clearly.

Update：
Error step:
step1: IDE Moee create a cWidgetForm object
step2: IDE Moee create call "New_c.FSO.ShowOpenDialog" method
step3: Conversion project for exe files
step4: Execute "ExeExitErrorDemo.exe" file        (IDE Mode OK,Not error)
step5: Click the Browse button
step6: click Cancel button
Step7: fourth: exit the "ExeExitErrorDemo.exe" file
step8: there is an error

ExeExitErrorDemo(vbRichClient5)-2.zip

----------


## jpbro

Strange, I can't reproduce the problem after following your steps. I tried on both Windows 10 and Windows XP and I'm able to close the window after showing and canceling the dialog without any error. 

NOTE: I did not run your EXE (you're not supposed to post binaries here BTW), but compiled my own binary from your source and I'm not getting the error.

----------


## ChenLin

你好smileyoufu ，我也没有发现你所说的问题，你的测试工程正常工作。
I also did not find out what you said, your test works normally.

----------


## smileyoufu

Re test results：
1.OS:Win10--All OK
2.OS:Win7 Chinese  （64）+ China”vbWidgets-MenuDemos”--OK
3.OS:Win7 Chinese  （64）+”MenuAndToolbarDemo” --Error]

----------


## smileyoufu

陈林：谢谢你的回复。如果你是中国的，请加我QQ86053924交流
Thank you very much.
Re test results：
1.OS:Win10--All OK
 2.OS:Win7 Chinese （64）+ China”vbWidgets-MenuDemos”--OK
 3.OS:Win7 Chinese （64）+”MenuAndToolbarDemo” --Error]

----------


## Schmidt

> 3.OS:Win7 Chinese （64）+”MenuAndToolbarDemo” --Error]


Also cannot reproduce the problem (from the Demo-Code you posted) - 
but you might add some explicit cleanup-code for the pnlTool-Panel 
(in Form_Unload)...



```
Private Sub Form_Unload(Cancel As Integer)
   pnlTool.Unload
   Set pnlTool = Nothing
End Sub
```

As for the MenuAndToolbarDemo itself - what if you use the (Dll-compiled) 
Widget-Classes directly from the vbWidgets.dll ...

Steps to switch to vbWidgets.dll:
- remove all the Private Class-Files from the Project (cfPopUp and all cw... Classes).
- put a reference to a recent vbWidgets.dll into the MenuAndToolbarDemo-Project (it contains the just removed Classes)

What is the behaviour then?

Olaf

----------


## smileyoufu

[QUOTE=Schmidt;5084477]Also cannot reproduce the problem (from the Demo-Code you posted) - 
but you might add some explicit cleanup-code for the pnlTool-Panel 
(in Form_Unload)...

Thank you Olaf：
Re：
1.Has been added pnl Tool.Unload code, but does not work, there is still an error
2.Here is a demo, with a minimum of code demonstrates the occurrence of an error process
3.This process does not use vb Widgets.dll .   errors and no relationship vb Widgets.dll

ExeExitErrorDemo(vbRichClient5)-The simplest error demonstration.zip



```
Option Explicit
'to reproduce the fault, the project must be compiled into a EXE file. In IDE mode, there will be no fault.
'要重现该故障，必需将此工程编译为exe文件。在IDE模式下，不会出现故障的
Private pnlTool As cWidgetForm
  '"After the cWidgetForm object is created, an error will occur.
  '创建cWidgetForm 对象后，才会发生错误
Private Sub Form_Load()
    Set pnlTool = Cairo.WidgetForms.CreateChild(Me.hWnd)
End Sub
Private Sub Form_Unload(Cancel As Integer)
pnlTool.Unload    'Add new    There is no change, there are still errors
    Set pnlTool = Nothing  'Add new    There is no change, there are still errors
End Sub

Private Sub Form_Terminate()
    If Forms.Count = 0 Then New_c.CleanupRichClientDll
End Sub
```




As for the MenuAndToolbarDemo itself - what if you use the (Dll-compiled) 
Widget-Classes directly from the vbWidgets.dll ...

Steps to switch to vbWidgets.dll:
- remove all the Private Class-Files from the Project (cfPopUp and all cw... Classes).
- put a reference to a recent vbWidgets.dll into the MenuAndToolbarDemo-Project (it contains the just removed Classes)

What is the behaviour then?
----------------------------------------
Re Olaf:
I'm using the latest vb Widgets.dll module test error persists
----------------------------------------

----------


## reexre

These menus are awesome!

I 'd like to use it in my PhotoModularFX to replace a DropDownList. (For  the selection of the Node/FX to add )

So I would have a main menu with -"Add Node"
-Then a number of Categories
-And For each category a number of entries.

The number of entries are about 200
I still don't know the number of categories.

In short
a big problem arises when a menu or submenu has a number of items such that they can not all be displayed at once on the screen.

It should be introduced a vertical scroll bar or a vList
but I have no idea how to do it ....




Another solution in "Fruity Loop" Studio by "image line"
is to add extra columns

----------


## ColinE66

Hi reexre,

As you may have seen if you've read this entire thread (OK, that's unlikely!), I did quite a lot of testing with this widget and posted, here, a lot of code changes and other feedback. Unfortunately, your particular case never crept onto my radar so I, personally, can offer no suggestions that will immediately remedy your problem. Hopefully, Olaf will chime-in if this is a situation he has already thought about.

If not, maybe I can find some time to look at this over the weekend. Happy to help out another proponent of RC5 and really good, also, to see an amazing product that you have delivered (using RC5) in PhotoModularFX.

----------


## jpbro

Unfortunately I have no immediate solution either, but if we're exploring possible solutions I'd like to chime in with some thoughts.

First, I don't like scrolling menus - if you have that many items, I think there must be a better UI choice. The Fruity Loops example is OK (definitely preferable to a scrolling menu IMO).

Another option would be to have alphabetic sub-menus like Notepad++:



But as a keyboard entry kind of guy, I would love to just be able to type into a search box for long menus like this. For example, the menu pops up (like the Fruity Loops example), but a search box also takes focus. I can then start typing: "p", "u", "r", 'e"... and the list gets filtered as I go eventually to show (with the first item highlighted):



```
Pure FM
Pure FM 1
Pure FM 2
Pure FM 3
Pure FM PWM
```

I can then press Enter to select the highlighted item (or click with the mouse if I prefer), or press up/down arrows to move the selection. ESC would first clear the search, then subsequently close the menu if pressed again (cancel/no selection).

----------


## jpbro

And before anyone starts complaining about my 76 "New" tabs...yes, yes, I know!

----------


## reexre

thanks collinE66
I'm close to what I need.
The only problem now is that a menu with a lot of items suffers of Very slow Mouseover-event (Highlight)


I changed in  _ConstructMenuFromDataSource_ of cwMenu


```
Private Sub ConstructMenuFromDataSource(DataSource As cMenuItem)
    Const IcoOffsX& = 30
    Dim DSItem As cMenuItem, MenuItem As cwMenuItem, MCC As cCairoContext, RM As cwMenu
    Dim I As Long, dx As Double, dy As Double, Caption As String
    Dim ItemX As Single
    Dim MaxH As Single

    Set RM = RootMenu

    Set mDataSource = DataSource
    Set MCC = W.MeasureContext
    mRowHeight = MCC.GetFontHeight + 7

    mMenuWidth = 0
    For I = 0 To mDataSource.SubItemCount - 1
        Set DSItem = mDataSource.SubItemByIndex(I)
        Caption = DSItem.Caption
        dx = MCC.GetTextExtents(Caption)
        If mMenuWidth < dx Then mMenuWidth = dx
    Next I
    mMenuWidth = IcoOffsX + mMenuWidth + 45

    mMenuHeight = 2
    For I = 0 To mDataSource.SubItemCount - 1
        Set DSItem = mDataSource.SubItemByIndex(I)

        Caption = DSItem.Caption
        If Caption = "-" Then
            dy = 8
        Else
            If Not RM Is Nothing Then RM.RaiseReplaceCaption DSItem, Caption
            dy = mRowHeight
        End If

        Set MenuItem = Widgets.Add(New cwMenuItem, DSItem.Key, ItemX, mMenuHeight, mMenuWidth - 2, dy)
        MenuItem.Widget.ImageKey = DSItem.IconKey
        MenuItem.Widget.Enabled = DSItem.Enabled
        MenuItem.Widget.FontName = W.FontName
        MenuItem.Widget.FontSize = W.FontSize
        MenuItem.Widget.BackColor = W.BackColor
        MenuItem.Caption = Caption
        MenuItem.IsCheckable = DSItem.IsCheckable
        MenuItem.IsOption = DSItem.IconKey = OptionMenuItemIconKey  'Here is the workaround to have
        'option-like cwMenuItems from cMenuItems
        'with "magic" iconkey indicator
        If DSItem.IsCheckable Then MenuItem.Checked = DSItem.Checked
        If Caption = "-" Then MenuItem.Widget.Enabled = False
        If DSItem.SubItemCount Then Set MenuItem.SubMenuDS = DSItem
        mMenuHeight = mMenuHeight + dy

        If mMenuHeight > Screen.Height / Screen.TwipsPerPixelY - dy * 2 Then
            MaxH = mMenuHeight
            ItemX = ItemX + mMenuWidth
            mMenuHeight = 0
        End If

    Next I

    If ItemX Then
        mMenuWidth = ItemX + mMenuWidth
        mMenuHeight = MaxH
    End If

    mMenuHeight = mMenuHeight + 4
End Sub
```

and just this line in _InitAndShow_


```
...
    If PopupPosY < 4 Then PopupPosY = 4

    Set PopUp = New cfPopUp
    PopUp.Load Me, PopupPosX, PopupPosY, mMenuWidth, mMenuHeight, mInitiatorWidget.Zoom

    PopUp.Show
...
```


I swap icons to the right...
I don't know if it is not a standard , but I prefer to have the icons at right...



EDIT 
 If mMenuHeight >* Screen.Height / Screen.TwipsPerPixelY* - dy * 2 Then
I think this should be replaced with something else, expecially if zoomed app or DPI-aware ... don't know exactly

----------


## ColinE66

Ok, if you could upload a small demo project incorporating the changes you have made so far, that would save me a bit of time.

I have to say, though, I agree with jpbro about this approach to your UI design. If I were a user of software that presented choices to me like that, I'd most likely look for an alternative program. It's just...too...overwhelming!

----------


## reexre

@CollinE66
Changes are only the "red" lines...  
I meant the menu to have all items in categories *plus* a "ALL/Everything" category with all items.
But , yes,  maybe is better to group all items in Categories, without the the ALL/Everything, even if I though it could be useful to find an Item when one doesn't know its category. (Alphabetical get)  
(In my PhotoModularFX now, all items are in a Dorp-Down List)
(I still have to find a good category for each Item)

EDIT (1):
Link to Small Demo 

EDIT (2)
Link removed (noone cares ... )

----------


## Schmidt

> I meant the menu to have all items in categories *plus* a "ALL/Everything" category with all items.


I agree with Colin that this "All/Everyting" entry in the Menu should be handled differently.

I would place it "at the top" of the menu - as a simple entry like: 
- "All..." (the 3 dots informing the User, that "something follows", usually a Popup-Dialog or some other kind of indirection into the GUI...)

Such a Popup-Dialogue (or non-modal ToolWindow) could then use (on the left-hand-side):
- a cwVList ... which in its OwnerDraw-Event allows you to not only render Text and SubText for the Item-Entry, but larger Icons as well
..(in your case I'd perhaps show "two-of-them", to kind of describe the "before-and-after" transition the effect in question will produce)

On the right-Hand-side beside the cwVList (if you'd make that ToolForm somewhat larger horizontally):
- you could show "on List-Click" even a "live-effect" of the transform, based on a "somewhat larger than IconSize, nice Input-Image" 
..(instead of the "principle-transition via two smaller Icons", which the cwVList-rendering shows on the left).

Since the cwVList would be scrollable (even searchable if you put a little Search-Field into that PopUp), 
you could give the User a nice "All Effects in alphabetical Order"-Overview in a still comparably small PopUp- or Tool-Window.

Olaf

----------


## ColinE66

Hi reexre,

Well, I took a look at the code and had a little play: One of the first things that struck me was how often the Paint event of cwMenu fires. When mousing over different items (on the same menu), it fires no less than six times! The smallest number I could that down to was two. It seems that, for each widget that receives the focus, we get two paint events in the parent. One for the newly-focused widget and one for the outgoing one, I'm guessing. If I am right about this (Olaf?), then presumably this is part of the internal widget engine behaviour.

Anyway, although I could reduce the number of these paints down to two (and some functionality was compromised by that, btw), it led to no significant improvement that could help in a scenario such as your large multi-column menu, I'm afraid. Maybe it's possible with some internal widget-engine changes, but that's speculation on my part...

----------


## LeoFar

Very very good! 
Just what I was looking for, thanks!




> The example is completely ownerdrawn and truly independent from any MS-Menu-APIs, so one
> can adapt *anything* as needed (e.g. the shape of the dropdown-form, colors, fonts, etc.) - 
> though the Demo  as it is tries for a moderate style, mimicking a Win7-look roughly (with some
> slight differences I personally like, but the whole thing is adaptable as said).
> Olaf


I'd like to change the style of the menu.
Where can I find some documentation?
I couldn't find anything about this on your site.

The RC5widgetsTutorial\5 Widgets and the Theme-Engine sample is empty.  :Frown: 

Thank a lot.

----------

