doughennig / ribbon Goto Github PK
View Code? Open in Web Editor NEWA Microsoft Office 365-like ribbon control for VFP forms
A Microsoft Office 365-like ribbon control for VFP forms
I expect on clicking on button and the sub-menu (bars) appear below it. I then can click on one of these bars.
The bars and disappear almost immediately. You have to watch very closely to see them flash.
Are there any useful screenshots? WinKey+Shift+S and then just paste them directly into the form
Doug,
Can i draw your attention to this https://www.tek-tips.com/viewthread.cfm?qid=1812073.
my solution is probably not for everyone.
n
I tried to run the sample.scx form with this command:
DO FORM "k:\_tools\_programming\ribbon-master\sample\sample.scx"
When I run it, it shows me the following message:
It occurs when executing this line of code in the "AddControl()" method of the "oRibbon" object.:
This.NewObject(tcName, tcClass, tcLibrary)
The values of the tcName, tcClass,and tcLibrary variables are "Home", "SFRibbonTab", and "SFRibbon.vcx", respectively.
What can I do in order to run the form correctly?
Here are some few modifications that will make us completely leave the Menu Timer, and avoid some errors previously documented:
The trick is in having a wider border, by changing the "Padding" value to 5. This way, when the mouse reaches this border, the menu will be hidden. The MenuForm "Deactivate()" method should do some extra caring as well. Very simple changes, with IMHO, a nice result.
1 - Set the property in SfRibbonMenu.Padding = 5 && Was 2, we need a wider "border" to detect the mouse
2 - In SfRibbonMenuForm.ShpBorder.MouseEnter() add the following code:
Thisform.Hide()
3 - In SfRibbonMenuForm.Deactivate() add the following code:
Thisform.Hide()
4 - In SfRibbonMenuForm.tmrClick.Timer() , clean the code for testing
If you'd like to see this feature implemented, add a π reaction to this post.
Doug,
had an issue with button menus appearing in the wrong place (even the wrong monitor) depending upon a form's .showwindow setting.
So i re-worked (in my copy) sfribbonmenuform by setting
.desktop = .f.
.showwindow = 1 (in top-level form... more later)
then by adding an invisible button (1x1) and having .show() setfocus to it... i was able to add
this.hide()
to the form's .lostfocus() event and get rid of the timer.
the biggest problem was that if the ribbon is being created in a form's .init() then the 'top-level form' this menu appeared in is not the yet-to-exist-form it is supposed to be on. So.... i had to change sfribbonmenu.addbar() to store the parameters in an array and not create the sfribbonmenuform till the first call on .showmenu(). That means .addbar() doesn't return an object so i don't have submenus as yet. i'm not using submenus so haven't looked into this. Could probably add a dummy (not container) object to the array and return that.. then use that object's properties to create the submenu when it instantiates the form.
Now the menu is IN the top level it is straightforward to calculate its position; that said objtoclient() wasn't always working for me so i currently loop through parent containers summing .left and .top as i go.
it's now working for me regardless of being in _screen, top level or whatever.
Nigel
If clicked on the RibbonDisplayButton, nothing seems to happen. "Nothing" means: not even the expected menus shows up.
This Bug happens on a ribbon-usage on _SCREEN. This might be happening due to this special usage, but as the menu-use on the sfRibbonToolbarButton is working just fine, there must be more to it.
The intended menu should show up and be selectable to the user.
If the menu-form-shows-in-taskbar-bug is not yet fixed, one can see that the menu shows up (in the taskbar) for a few milliseconds before dissapearing again.
There seems to be something going on that lets the menu dissapear right after ShowMenu()
Test
If i disable the Menu-Timer for good, the menu stays on and does not dissapear.
Of course a disabled timer is not a solution as it is responsible for the check-for-outofForm-Clicks. But the menu itself shows up and is workable just fine.
Yepp... the position is screwed up, but that is another problem i will put in another report as it might be easier to work on the individual tasks... Report for the wrong position is: #19 (comment)
So the test with deactivated menuform-timer proofs that the thing itself is working, but the menu to get to the point is simply dissapearing due to timer-action.
I have not (yet) find a solution to that and if this behavior is special to the oRibbon-on-_SCREEN-usage, but something lets the timer immediately "detect" an out-of-form-mouseclick. ...which then shuts down the menu-form right away.
I will edit/add further infos on this if i will find anything new, but until now... used on _SCREEN, this won't work.
i suspect it's the hard coding of cnFACTOR ?
for now i have replaced the call on .measurestring() and subsequent use of .nwidth in my copy with variations on
TXTWIDTH(THIS.CAPTION,THIS.lblbutton.FONTNAME,THIS.lblbutton.FONTSIZE) * FONTMETRIC(6,THIS.lblbutton.FONTNAME,THIS.lblbutton.FONTSIZE)
and that seems to be working in testing so far.
I can post my new versions of .calculatewidth() for sfribbontoolbarbutton, sfribbontoolbarbuttonhorizontal and sfribbontab but suspect you'd prefer a gdi solution?
n
Instead of using sometimes dangerous Execscript, I would add a new property to the Toolbar buttons - something like "ClickEvent". This would store the Current form related Method that would be fired when the button was clicked.
Internally, something similar to below would happen, at the button initialization:
IF NOT EMPTY(This.ClickEvent) and PEMSTATUS(Thisform, This.ClickEvent, 5) && We have the method in the form
BINDEVENT(This, "Click", Thisform, This.ClickEvent)
ENDIF
This would provide another safe way for us to put whatever code we like.
In my case, the Ribbon was not being released because the object "loThisform" was still active. This used to happen when an external object was calling the Form.Release().
If you agree to that, the current Click event should not show the "Not Implemented" Messagebox when empty
On my side, I am currently using the way below using BINDEVENTS directly with nice results:
m.loButton = .AddButton()
WITH m.loButton
* .IMAGE = m.lcImagePath + "Exit.png"
.CAPTION = "Close"
.COMMAND = "SET REPORTBEHAVIOR 90" && Dummy command to avoid the "Not implemented" msg
ENDWITH
BINDEVENT(loButton, "Click", Thisform, "ActionClose")
lnHighlightedColor = Thisform.oRibbon.GetThemeColor('buttonhighlightcolor')
Doug, This is an amazing work!
Loving to play with it, thanks for sharing!
I'd like you to add a small button so that when clicked it hides the ribbon, except the main options, and when clicked again, the ribbon shows.
This would be useful when there are forms that have much information to be shown, this button would allow to leave more space for those forms (when the ribbon is hidden).
Hi Doug,
I just played a little on this idea, and got it working. Hopefully you'd find it useful - When you find some time, you can try the following:
1 - In the menu Form, add a transparent container or a Shape control that should resize with the form every time a button is added
2 - The menu items don't need to be inside the container. Just make sure the container or shape is slightly bigger than the menu items, and Behind the menu items
3 - In the Container's 'MouseLeave()' event, add some code similar to the below
LPARAMETERS nButton, nShift, nXCoord, nYCoord
IF nXCoord <= This.Left OR nXCoord >= (This.Left + This.Width) OR ;
nYCoord <= This.Top OR nYCoord >= (This.Top + This.Height)
Thisform.Release()
ENDIF
This happens occasionally under production only, I still could not get the right steps.
I am using some short scripts for the ribbon buttons.
It seems that if I close the Form during the execution of a process EXECSCRIPT gets angry ;-)
When I navigate through the tabs, going from right to left, the last section separator on the right, suddenly disappears. When you click on any button or menu, the separator reappears.
The problem occurs when navigating from RIGHT TO LEFT through the tabs. Going from left to right the problem does not appear.
I would like to know if you have any update to solve this bug.
rather than use a public variable i have added (my copy) an .oParentform property to sfribbonbase and sfribbonmenuform , set it to 'thisform' in their .init() events and then changed sfribbontoolbarbutton.click() to
loThisform = This.oparentform
n
In class "sfRibbonToolbarSection", method "CalculateWidth"
Added some few code to realign a unique control if the section width is bigger than the inside control width
Are there any useful screenshots? WinKey+Shift+S and then just paste them directly into the form
A simple fix below:
LOCAL lnWidth, ;
lcType, ;
loControl, ;
llVertical, ;
lnLeft, ;
lnCaptionWidth
LOCAL lnTop
m.lnWidth = 0
for each m.loControl in This.Controls foxobject
m.lcType = iif(pemstatus(m.loControl, 'Type', 5), m.loControl.Type, '')
do case
* Do nothing if this is the separator or section label or the control isn't
* visible.
case inlist(m.loControl.Name, 'linSeparator', 'lblSection') or ;
(pemstatus(m.loControl, 'Visible', 5) and not m.loControl.Visible)
&& do nothing
* Horizontal button: if we already have enough buttons in this column, move to
* the next column. Set the Left property and if this isn't the first one, set
* the Top property. Then increment the total width value.
LOOP
case m.lcType = 'HButton'
if m.llVertical and m.lnTop + m.loControl.Height > This.Height
m.llVertical = .F.
endif m.llVertical ...
if m.llVertical
m.loControl.Top = m.lnTop
m.loControl.Left = m.lnLeft
ELSE
m.loControl.Left = max(m.lnWidth, This.Padding)
m.lnLeft = m.loControl.Left
endif m.llVertical
m.lnWidth = max(m.lnWidth, m.loControl.Left + m.loControl.Width)
m.lnTop = m.loControl.Top + m.loControl.Height
m.llVertical = .T.
* For all other controls, move the control to the next Left position and
* increment the total width value.
CASE 1 = 2
OTHERWISE
m.loControl.Left = max(m.lnWidth, This.Padding)
m.lnWidth = max(m.lnWidth, m.loControl.Left + m.loControl.Width)
m.llVertical = .F.
ENDCASE
* 2023-07-07 VfpImaging
m.lnControls = m.lnControls + 1
m.loUnique = m.loControl
*
NEXT m.loControl
m.lnCaptionWidth = This.oGDI.GetWidth(This.Caption)
This.linSeparator.Left = max(m.lnWidth, m.lnCaptionWidth) + This.Padding
store This.linSeparator.Left to This.Width, This.lblSection.Width
This.Parent.AdjustSections()
The menu activated on click to the oRibbon.RibbonDisplayButton is show on a wonky position. ...at least when the Ribbon is used in _SCREEN.
As the sfribbonmenu-form is a Top-Level-Form, the menuform.left is always absolut to windows-screen-positions not to THISFORM-relative-position.
As with the usage of menus on sfribbonToobarButton the menu should show up right "under" the button/ribbon and not "somewhere"-else.
The position of the menu is dependent of windows-screensize, mainform-size and -postiion. Which should not be the case.
Digging to the code, i see a difference in calling the .ShowMenu() Function between the call used in sfRibbonToolbarButton (works good) and the call used on the RibbonDisplayButton
There seems to be a double-headed but in here.
sfRibbonToobarButton has special Code in its ShowMenu - Method. The ShowMenu-Call from the RibbonDisplayButton has no special treatment for positions in it. It is simply called as-is.
If i copy the code to the RibbonDisplayButton.ShowMenu, everything works (nearly)
But this is counteracted by a code specially built-in for "off-form"-positions to achieve menu-positions not expand over the right border of the form.
As sfribbonmenuform is a top-Level-form, its .LEFT is always relative to windows-screen and not THISFORM.LEFT.
My solution works for _SCREEN & DESKTOP=.T. usage, but i have not tested sideeffects to "regular" form usage. It is possible that there needs to be a different aproach for each scenario.
Some tweaking is needed for the top-position on the Ribbon-Button.ShowMenu-Call, but that's something for another day.
if you use .addcontrol() to add a listbox it looks great and works most of the time; but every so often (one in 10?) the listbox fails to repaint itself when you switch between tabs. It's there because if i click the mouse on where it is supposed to be it will re-appear one row at a time as i move the mouse around.
I suspect this is a VFP thing rather than the ribbon class but haven't found a way round it yet; i tried putting .visible = .f., .visible = .t. in the listbox's refresh event in the hope it would re-paint itself and that helped a little. But i changed my mind about using a listbox anyway in this particular instance and haven't pursued it yet.
we can set a button's tooltiptext but it only applies to the container.
So edit the property to have an assign method and in sfribbontoolbarbutton.tooltiptext_assign
add this line at end
this.SetAll('tooltiptext',this.ToolTipText)
If a menu is instantiated, the form/window of the different menu-levels show up on the windows taskbar.
The windows used by the menusystem should not show up on the taskbar. The forms are only cleverly used by the menu-object-hierachy and are not "real" windows to be presented to the user on the taskbar.
...especially not because their title-caption is "sffibbonmenu" π
This bug might be only true if used in a top-level-form or the _screen itself. I have not tested to the contrary as my project resides directly IN _SCREEN which works great with just two little glitches. This being on of them.
Easy: Set sfribbonmenuform.ShowInTaskbar=.F. and everyone will be happy
When sfRibbon is used directly on _SCREEN, a menu attached to a sfRibbonToolbarButton has an offset to its top-property which should not be there.
The code shows why this happens. π
This only happens when oRibbon is attached directly to _SCREEN
...which works perfectly well.
The menu should be "attached" to the ribbon as it is when used on a "normal" form.
The Code of sfRibbonToolbarButton.ShowMenu is correctly using a special treatment if used on a "desktop=.T."-form. But... if used in _SCREEN the following code does not break, but delivers the wrong assumption as _SCREEN.Desktop is READONLY and cannot be altered to a usable .T.
Original Code
dodefault(objtoclient(This, 1) + This.Height + Thisform.Top + ; sysmetric(9) + sysmetric(4) + ; iif(Thisform.Desktop, 0, sysmetric(9) + sysmetric(20)), ; objtoclient(This, 2) + Thisform.Left + sysmetric(3))
If added an additional OR to test for _SCREEN, everything works fine:
dodefault(objtoclient(This, 1) + This.Height + Thisform.Top + ; sysmetric(9) + sysmetric(4) + ; iif(Thisform.Desktop OR thisform.HWnd==_screen.HWnd, 0, sysmetric(9) + sysmetric(20)), ; objtoclient(This, 2) + Thisform.Left + sysmetric(3))
Doug,
if you press mousedown on a tab and then move the mouse up to the window's title bar before releasing... the tab doesn't get a .mouseleave() event and remains highlighted. (i must have been doing this inadvertently every once in a while when clicking on a tab).
This is compounded by the line in .mouseenter()
This.nBackColor = This.BackColor
saving the (now) highlighted colour; themes_assign will have set .nBackColor already so i have commented this line out. Now at least next time i .mouseleave() it gets reset to the correct .backcolor.
I'm guessing the only way to prevent this happening in the first place is to put the tab's .top to 1 so it gets a chance to see .mouseleave() ?
n
Buttons are getting a fixed value of 100 pixels when the button caption is empty.
Fix:
In SfRibbonToolbarButton.CalculateWidth, added code to deal with the empty Caption:
LOCAL lnWidth
DO CASE
CASE NOT EMPTY(THIS.CAPTION)
IF THIS.WORDWRAP
THIS.oGDI.SETSIZE(THIS.LBLBUTTON.WIDTH, THIS.LBLBUTTON.HEIGHT)
ENDIF THIS.WORDWRAP
THIS.oGDI.MEASURESTRING(THIS.LBLBUTTON.CAPTION)
m.lnWidth = MAX(CEILING(THIS.oGDI.nWidth) + 2 * THIS.LBLBUTTON.LEFT, ;
THIS.IMGBUTTON.WIDTH + 2 * THIS.Padding)
&& use a width of the label or the image, whichever is wider
&& This.lblButton.Width is inaccurate if the form isn't visible yet
IF THIS.WIDTH <> m.lnWidth AND NOT THIS.WORDWRAP
THIS.WIDTH = m.lnWidth
THIS.LBLBUTTON.WIDTH = THIS.WIDTH - 2 * THIS.LBLBUTTON.LEFT
ENDIF THIS.WIDTH <> m.lnWidth ...
THIS.IMGBUTTON.LEFT = INT((THIS.WIDTH - THIS.IMGBUTTON.WIDTH) / 2)
&& center the image
* If there's one line of text, center the down button below the label.
IF THIS.oGDI.nLines = 1
THIS.IMGDOWN.LEFT = INT((THIS.WIDTH - THIS.IMGDOWN.WIDTH) / 2)
THIS.IMGDOWN.TOP = THIS.LBLBUTTON.TOP + 24
ELSE
THIS.IMGDOWN.LEFT = THIS.LBLBUTTON.LEFT + THIS.LBLBUTTON.WIDTH - 5
THIS.IMGDOWN.TOP = THIS.LBLBUTTON.TOP + THIS.LBLBUTTON.HEIGHT - 10
ENDIF THIS.oGDI.nLines = 1
THIS.nLines = THIS.oGDI.nLines
THIS.PARENT.CalculateWidth()
2023-07-06 VfpImaging
Align empty captions
OTHERWISE && Empty(This.Caption)
m.lnWidth = THIS.IMGBUTTON.WIDTH + (2 * THIS.Padding)
THIS.WIDTH = m.lnWidth
THIS.LBLBUTTON.WIDTH = THIS.WIDTH - 2 * THIS.LBLBUTTON.LEFT
THIS.IMGBUTTON.LEFT = INT((THIS.WIDTH - THIS.IMGBUTTON.WIDTH) / 2)
&& center the image
THIS.IMGDOWN.LEFT = INT((THIS.WIDTH - THIS.IMGDOWN.WIDTH) / 2)
THIS.IMGDOWN.TOP = THIS.LBLBUTTON.TOP + 24
THIS.nLines = 1
THIS.PARENT.CalculateWidth()
ENDCASE
A declarative, efficient, and flexible JavaScript library for building user interfaces.
π Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. πππ
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google β€οΈ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.