Skip to content
This repository was archived by the owner on Aug 31, 2021. It is now read-only.

Commit 4ac00eb

Browse files
committed
[[ Bug 14541 ]] Implement widget "popup menu ..." command.
1 parent 6e690d1 commit 4ac00eb

File tree

6 files changed

+195
-15
lines changed

6 files changed

+195
-15
lines changed

docs/lcb/notes/14541.md

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
# LiveCode Builder Host Library
2+
3+
## Ability to display a popup menu
4+
5+
* New syntax has been added to popup a menu constructed from a provided menu text.
6+
* `popup menu <MenuText> at <Point>`
7+
8+
# [14541] Widgets should be able to popup system menus

engine/src/button.cpp

Lines changed: 38 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -327,6 +327,8 @@ MCButton::MCButton()
327327

328328
// MM-2014-07-31: [[ ThreadedRendering ]] Used to ensure the default button animate message is only posted from a single thread.
329329
m_animate_posted = false;
330+
331+
m_menu_handler = nil;
330332
}
331333

332334
MCButton::MCButton(const MCButton &bref) : MCControl(bref)
@@ -378,6 +380,8 @@ MCButton::MCButton(const MCButton &bref) : MCControl(bref)
378380

379381
// MM-2014-07-31: [[ ThreadedRendering ]] Used to ensure the default button animate message is only posted from a single thread.
380382
m_animate_posted = false;
383+
384+
m_menu_handler = nil;
381385
}
382386

383387
MCButton::~MCButton()
@@ -732,7 +736,7 @@ Boolean MCButton::kdown(MCStringRef p_string, KeySym key)
732736
flags |= F_LABEL;
733737
if (entry != NULL)
734738
entry->settext(0, *t_pick, False);
735-
Exec_stat es = message_with_valueref_args(MCM_menu_pick, *t_pick);
739+
Exec_stat es = handlemenupick(*t_pick, nil);
736740
if (es == ES_NOT_HANDLED || es == ES_PASS)
737741
message_with_args(MCM_mouse_up, menubutton);
738742
// MW-2011-08-18: [[ Layers ]] Invalidate the whole object.
@@ -766,7 +770,7 @@ Boolean MCButton::kdown(MCStringRef p_string, KeySym key)
766770
t_label = mbptr->getlabeltext();
767771

768772
menu->menukdown(p_string, key, &t_pick, menuhistory);
769-
Exec_stat es = message_with_valueref_args(MCM_menu_pick, t_label);
773+
Exec_stat es = handlemenupick(t_label, nil);
770774
if (es == ES_NOT_HANDLED || es == ES_PASS)
771775
message_with_args(MCM_mouse_up, menubutton);
772776
}
@@ -1190,7 +1194,7 @@ Boolean MCButton::mdown(uint2 which)
11901194
setmenuhistoryprop(starttab + 1);
11911195
// MW-2011-08-18: [[ Layers ]] Invalidate the whole object.
11921196
layer_redrawall();
1193-
message_with_valueref_args(MCM_menu_pick, t_tab, t_oldhist);
1197+
handlemenupick(t_tab, t_oldhist);
11941198
}
11951199
}
11961200
}
@@ -1452,7 +1456,7 @@ Boolean MCButton::mup(uint2 which, bool p_release)
14521456
setmenuhistoryprop(starttab + 1);
14531457
// MW-2011-08-18: [[ Layers ]] Invalidate the whole object.
14541458
layer_redrawall();
1455-
message_with_valueref_args(MCM_menu_pick, t_tab, t_oldhist);
1459+
handlemenupick(t_tab, t_oldhist);
14561460
}
14571461
}
14581462
else
@@ -2882,7 +2886,7 @@ void MCButton::activate(Boolean notify, KeySym p_key)
28822886
else
28832887
{
28842888
if (!t_disabled)
2885-
message_with_valueref_args(MCM_menu_pick, *t_pick);
2889+
handlemenupick(*t_pick, nil);
28862890
}
28872891
}
28882892
else
@@ -3576,6 +3580,25 @@ Boolean MCButton::findmenu(bool p_just_for_accel)
35763580
return menu != NULL;
35773581
}
35783582

3583+
void MCButton::setmenuhandler(MCButtonMenuHandler *p_handler)
3584+
{
3585+
m_menu_handler = p_handler;
3586+
}
3587+
3588+
Exec_stat MCButton::handlemenupick(MCValueRef p_pick, MCValueRef p_old_pick)
3589+
{
3590+
if (m_menu_handler != nil)
3591+
{
3592+
if (m_menu_handler->OnMenuPick(this, p_pick, p_old_pick))
3593+
return ES_NORMAL;
3594+
}
3595+
3596+
if (p_old_pick == nil)
3597+
return message_with_valueref_args(MCM_menu_pick, p_pick);
3598+
else
3599+
return message_with_valueref_args(MCM_menu_pick, p_pick, p_old_pick);
3600+
}
3601+
35793602
bool MCSystemPick(MCStringRef p_options, bool p_use_checkmark, uint32_t p_initial_index, uint32_t& r_chosen_index, MCRectangle p_button_rect);
35803603

35813604
void MCButton::openmenu(Boolean grab)
@@ -3625,7 +3648,7 @@ void MCButton::openmenu(Boolean grab)
36253648
&t_label);
36263649
MCValueAssign(label, *t_label);
36273650
flags |= F_LABEL;
3628-
message_with_valueref_args(MCM_menu_pick, *t_label);
3651+
handlemenupick(*t_label, nil);
36293652
}
36303653
return;
36313654
}
@@ -3792,13 +3815,16 @@ void MCButton::docascade(MCStringRef p_pick)
37923815

37933816
if (pptr != this)
37943817
{
3795-
MCParameter *param = new MCParameter;
3796-
param->setvalueref_argument(*t_pick);
3797-
MCscreen->addmessage(pptr, MCM_menu_pick, MCS_time(), param);
3818+
if (m_menu_handler != nil && !m_menu_handler->OnMenuPick(pptr, *t_pick, nil))
3819+
{
3820+
MCParameter *param = new MCParameter;
3821+
param->setvalueref_argument(*t_pick);
3822+
MCscreen->addmessage(pptr, MCM_menu_pick, MCS_time(), param);
3823+
}
37983824
}
37993825
else
38003826
{
3801-
Exec_stat es = pptr->message_with_valueref_args(MCM_menu_pick, *t_pick);
3827+
Exec_stat es = pptr->handlemenupick(*t_pick, nil);
38023828
if (es == ES_NOT_HANDLED || es == ES_PASS)
38033829
pptr->message_with_args(MCM_mouse_up, menubutton);
38043830
}
@@ -4096,7 +4122,7 @@ void MCButton::setmenuhistory(int2 newline)
40964122

40974123
MCStringRef t_which;
40984124
t_builder.GetPickString(t_which);
4099-
message_with_valueref_args(MCM_menu_pick, t_which);
4125+
handlemenupick(t_which, nil);
41004126

41014127
resetlabel();
41024128
}
@@ -4122,7 +4148,7 @@ void MCButton::setmenuhistory(int2 newline)
41224148
MCValueRef t_oldline = nil;
41234149
/* UNCHECKED */ MCArrayFetchValueAtIndex(tabs, menuhistory, t_menuhistory);
41244150
/* UNCHECKED */ MCArrayFetchValueAtIndex(tabs, oldline, t_oldline);
4125-
message_with_valueref_args(MCM_menu_pick, t_menuhistory, t_oldline);
4151+
handlemenupick(t_menuhistory, t_oldline);
41264152
}
41274153

41284154
resetlabel();

engine/src/button.h

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -95,6 +95,12 @@ struct MCInterfaceButtonIcon;
9595
#define MENUCONTROL_ITEM 1
9696
#define MENUCONTROL_SEPARATOR 2
9797

98+
class MCButtonMenuHandler
99+
{
100+
public:
101+
virtual bool OnMenuPick(MCButton *p_button, MCValueRef p_pick, MCValueRef p_old_pick) = 0;
102+
};
103+
98104
class ButtonMenuCallback;
99105

100106
class MCButton : public MCControl
@@ -144,6 +150,8 @@ class MCButton : public MCControl
144150
static MCPropertyInfo kProperties[];
145151
static MCObjectPropertyTable kPropertyTable;
146152

153+
MCButtonMenuHandler *m_menu_handler;
154+
147155
// MM-2014-07-31: [[ ThreadedRendering ]] Used to ensure the default button animate message is only posted from a single thread.
148156
bool m_animate_posted : 1;
149157

@@ -287,6 +295,9 @@ class MCButton : public MCControl
287295
// a Mac menu as its assumed its not needed.
288296
Boolean findmenu(bool p_just_for_accel = false);
289297

298+
void setmenuhandler(MCButtonMenuHandler *p_handler);
299+
Exec_stat handlemenupick(MCValueRef p_pick, MCValueRef p_old_pick);
300+
290301
void openmenu(Boolean grab);
291302
void freemenu(Boolean force);
292303
MCRange getmenurange();

engine/src/desktop-menu.cpp

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -421,7 +421,7 @@ void MCButton::macopenmenu(void)
421421
t_menupick = s_popup_menupick;
422422
s_popup_menupick = nil;
423423

424-
Exec_stat es = message_with_valueref_args(MCM_menu_pick, t_menupick);
424+
Exec_stat es = handlemenupick(t_menupick, nil);
425425

426426
MCValueRelease(t_menupick);
427427

@@ -447,7 +447,7 @@ void MCButton::macopenmenu(void)
447447
t_menupick = s_popup_menupick;
448448
s_popup_menupick = nil;
449449

450-
Exec_stat es = message_with_valueref_args(MCM_menu_pick, t_menupick);
450+
Exec_stat es = handlemenupick(t_menupick, nil);
451451

452452
MCValueRelease(t_menupick);
453453

@@ -973,7 +973,7 @@ void MCPlatformHandleMenuSelect(MCPlatformMenuRef p_menu, uindex_t p_item_index)
973973
if (s_menubar_targets[t_current_menu_index] -> Exists())
974974
{
975975
((MCButton *)s_menubar_targets[t_current_menu_index] -> Get()) -> setmenuhistoryprop(t_last_menu_index + 1);
976-
s_menubar_targets[t_current_menu_index] -> Get() -> message_with_valueref_args(MCM_menu_pick, *t_result);
976+
((MCButton *)s_menubar_targets[t_current_menu_index] -> Get()) -> handlemenupick(*t_result, nil);
977977
}
978978
}
979979
else

engine/src/widget.cpp

Lines changed: 97 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@
2929
#include "card.h"
3030
#include "image.h"
3131
#include "widget.h"
32+
#include "button.h"
3233
#include "param.h"
3334
#include "osspec.h"
3435
#include "cmds.h"
@@ -50,6 +51,8 @@
5051

5152
#include "module-engine.h"
5253

54+
#include "graphics_util.h"
55+
5356
////////////////////////////////////////////////////////////////////////////////
5457

5558
void MCCanvasPush(MCGContextRef gcontext, uintptr_t& r_cookie);
@@ -1835,3 +1838,97 @@ extern "C" MC_DLLEXPORT void MCWidgetEvalIsPointNotWithinRect(MCCanvasPointRef p
18351838
MCWidgetEvalIsPointWithinRect(p_point, p_rect, t_within);
18361839
r_not_within = !t_within;
18371840
}
1841+
1842+
////////////////////////////////////////////////////////////////////////////////
1843+
1844+
static inline MCPoint _MCWidgetToStackLoc(MCWidget *p_widget, const MCPoint &p_point)
1845+
{
1846+
MCRectangle t_rect;
1847+
t_rect = p_widget->getrect();
1848+
return MCPointMake(p_point.x + t_rect.x, p_point.y + t_rect.y);
1849+
}
1850+
1851+
class MCPopupMenuHandler : public MCButtonMenuHandler
1852+
{
1853+
public:
1854+
MCPopupMenuHandler()
1855+
{
1856+
m_pick = nil;
1857+
}
1858+
1859+
~MCPopupMenuHandler()
1860+
{
1861+
MCValueRelease(m_pick);
1862+
}
1863+
1864+
virtual bool OnMenuPick(MCButton *p_button, MCValueRef p_pick, MCValueRef p_old_pick)
1865+
{
1866+
MCValueAssign(m_pick, p_pick);
1867+
return true;
1868+
}
1869+
1870+
MCValueRef GetPick()
1871+
{
1872+
return m_pick;
1873+
}
1874+
1875+
private:
1876+
MCValueRef m_pick;
1877+
};
1878+
1879+
extern "C" MC_DLLEXPORT MCStringRef MCWidgetExecPopupMenuAtLocation(MCStringRef p_menu, MCCanvasPointRef p_at)
1880+
{
1881+
if (MCwidgetobject == nil)
1882+
{
1883+
MCWidgetThrowNoCurrentWidgetError();
1884+
return nil;
1885+
}
1886+
1887+
MCButton *t_button;
1888+
t_button = nil;
1889+
1890+
t_button = (MCButton*)MCtemplatebutton->clone(True, OP_NONE, true);
1891+
if (t_button == nil)
1892+
{
1893+
MCErrorThrowOutOfMemory();
1894+
return nil;
1895+
}
1896+
1897+
MCPopupMenuHandler t_handler;
1898+
1899+
MCExecContext ctxt;
1900+
1901+
t_button->setmenuhandler(&t_handler);
1902+
1903+
t_button->SetStyle(ctxt, F_MENU);
1904+
t_button->SetMenuMode(ctxt, WM_POPUP);
1905+
t_button->SetText(ctxt, p_menu);
1906+
1907+
MCPoint t_at;
1908+
MCPoint *t_at_ptr;
1909+
t_at_ptr = nil;
1910+
1911+
if (p_at != nil)
1912+
{
1913+
MCGPoint t_point;
1914+
MCCanvasPointGetMCGPoint(p_at, t_point);
1915+
1916+
t_at = _MCWidgetToStackLoc(MCwidgetobject, MCGPointToMCPoint(t_point));
1917+
t_at_ptr = &t_at;
1918+
}
1919+
1920+
MCInterfaceExecPopupButton(ctxt, t_button, t_at_ptr);
1921+
1922+
t_button->SetVisible(ctxt, 0, false);
1923+
t_button->del();
1924+
t_button->scheduledelete();
1925+
1926+
MCAutoStringRef t_string;
1927+
1928+
if (t_handler.GetPick() != nil)
1929+
ctxt.ConvertToString(t_handler.GetPick(), &t_string);
1930+
1931+
return t_string.Take();
1932+
}
1933+
1934+
////////////////////////////////////////////////////////////////////////////////

engine/src/widget.mlc

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -776,4 +776,42 @@ begin
776776
MCWidgetEvalIsPointNotWithinRect(Point, Rect, output)
777777
end syntax
778778

779+
// ---------- popups ---------- //
780+
public foreign handler MCWidgetExecPopupMenuAtLocation(in pMenu as String, in pAt as Point) returns optional String binds to "<builtin>"
781+
782+
783+
/*
784+
Summary: Displays a popup menu.
785+
Menu: An expression that evaluates to a string, which describes the menu items.
786+
Location: An expression that evaluates to a <Point> relative to the current widget. The topleft corner of the popup window will be placed here.
787+
788+
The result: The selected menu item. If the menu is dismissed (by clicking outside the bounds of the menu) this value will be undefined.
789+
790+
Example:
791+
private variable mFont as Font
792+
793+
public handler OnMouseDown()
794+
popup menu "Helvetica\nTimes New Roman\nArial" at the mouse position
795+
if the result is defined then
796+
put font (the result) at size 20 into mFont
797+
redraw all
798+
end if
799+
end handler
800+
801+
public handler OnPaint()
802+
...
803+
set the font of this canvas to mFont
804+
...
805+
end handler
806+
807+
Description:
808+
Use to popup a menu at the specified location.
809+
*/
810+
syntax PopupMenu is statement
811+
"popup" "menu" <mMenu: Expression> "at" <mAt: Expression>
812+
begin
813+
MCWidgetExecPopupMenuAtLocation(mMenu, mAt)
814+
end syntax
815+
816+
779817
end module

0 commit comments

Comments
 (0)