mirror of
https://github.com/Rayerd/dfl.git
synced 2025-04-25 20:49:58 +03:00
1096 lines
19 KiB
D
1096 lines
19 KiB
D
// Written by Christopher E. Miller
|
|
// See the included license.txt for copyright and license details.
|
|
|
|
|
|
///
|
|
module dfl.menu;
|
|
|
|
private import dfl.internal.dlib;
|
|
|
|
private import dfl.internal.winapi, dfl.control, dfl.base, dfl.event;
|
|
private import dfl.internal.utf, dfl.drawing, dfl.application, dfl.collections;
|
|
debug(APP_PRINT)
|
|
{
|
|
private import dfl.internal.clib;
|
|
}
|
|
|
|
version(DFL_NO_MENUS)
|
|
{
|
|
}
|
|
else
|
|
{
|
|
///
|
|
class ContextMenu: Menu // docmain
|
|
{
|
|
///
|
|
final void show(Control control, Point pos)
|
|
{
|
|
SetForegroundWindow(control.handle);
|
|
TrackPopupMenu(hmenu, TPM_LEFTALIGN | TPM_LEFTBUTTON | TPM_RIGHTBUTTON,
|
|
pos.x, pos.y, 0, control.handle, null);
|
|
}
|
|
|
|
|
|
//EventHandler popup;
|
|
Event!(ContextMenu, EventArgs) popup; ///
|
|
|
|
|
|
// Used internally.
|
|
this(HMENU hmenu, bool owned = true)
|
|
{
|
|
super(hmenu, owned);
|
|
|
|
_init();
|
|
}
|
|
|
|
|
|
this()
|
|
{
|
|
super(CreatePopupMenu());
|
|
|
|
_init();
|
|
}
|
|
|
|
|
|
~this()
|
|
{
|
|
Application.removeMenu(this);
|
|
|
|
debug(APP_PRINT)
|
|
cprintf("~ContextMenu\n");
|
|
}
|
|
|
|
|
|
protected override void onReflectedMessage(ref Message m)
|
|
{
|
|
super.onReflectedMessage(m);
|
|
|
|
switch(m.msg)
|
|
{
|
|
case WM_INITMENU:
|
|
assert(cast(HMENU)m.wParam == handle);
|
|
|
|
//onPopup(EventArgs.empty);
|
|
popup(this, EventArgs.empty);
|
|
break;
|
|
|
|
default:
|
|
}
|
|
}
|
|
|
|
|
|
private:
|
|
void _init()
|
|
{
|
|
Application.addContextMenu(this);
|
|
}
|
|
}
|
|
|
|
|
|
///
|
|
class MenuItem: Menu // docmain
|
|
{
|
|
///
|
|
final @property void text(Dstring txt) // setter
|
|
{
|
|
if(!menuItems.length && txt == SEPARATOR_TEXT)
|
|
{
|
|
_type(_type() | MFT_SEPARATOR);
|
|
}
|
|
else
|
|
{
|
|
if(mparent)
|
|
{
|
|
MENUITEMINFOA mii;
|
|
|
|
if(fType & MFT_SEPARATOR)
|
|
fType = ~MFT_SEPARATOR;
|
|
mii.cbSize = mii.sizeof;
|
|
mii.fMask = MIIM_TYPE | MIIM_STATE; // Not setting the state can cause implicit disabled/gray if the text was empty.
|
|
mii.fType = fType;
|
|
mii.fState = fState;
|
|
//mii.dwTypeData = stringToStringz(txt);
|
|
|
|
mparent._setInfo(mid, false, &mii, txt);
|
|
}
|
|
}
|
|
|
|
mtext = txt;
|
|
}
|
|
|
|
/// ditto
|
|
final @property Dstring text() // getter
|
|
{
|
|
// if(mparent) fetch text ?
|
|
return mtext;
|
|
}
|
|
|
|
|
|
///
|
|
final @property void parent(Menu m) // setter
|
|
{
|
|
m.menuItems.add(this);
|
|
}
|
|
|
|
/// ditto
|
|
final @property Menu parent() // getter
|
|
{
|
|
return mparent;
|
|
}
|
|
|
|
|
|
package final void _setParent(Menu newParent)
|
|
{
|
|
assert(!mparent);
|
|
mparent = newParent;
|
|
|
|
if(cast(size_t)mindex > mparent.menuItems.length)
|
|
mindex = mparent.menuItems.length.toI32;
|
|
|
|
_setParent();
|
|
}
|
|
|
|
|
|
private void _setParent()
|
|
{
|
|
MENUITEMINFOA mii;
|
|
MenuItem miparent;
|
|
|
|
mii.cbSize = mii.sizeof;
|
|
mii.fMask = MIIM_TYPE | MIIM_STATE | MIIM_ID | MIIM_SUBMENU;
|
|
mii.fType = fType;
|
|
mii.fState = fState;
|
|
mii.wID = mid;
|
|
mii.hSubMenu = handle;
|
|
//if(!(fType & MFT_SEPARATOR))
|
|
// mii.dwTypeData = stringToStringz(mtext);
|
|
miparent = cast(MenuItem)mparent;
|
|
if(miparent && !miparent.hmenu)
|
|
{
|
|
miparent.hmenu = CreatePopupMenu();
|
|
|
|
if(miparent.parent() && miparent.parent.hmenu)
|
|
{
|
|
MENUITEMINFOA miiPopup;
|
|
|
|
miiPopup.cbSize = miiPopup.sizeof;
|
|
miiPopup.fMask = MIIM_SUBMENU;
|
|
miiPopup.hSubMenu = miparent.hmenu;
|
|
miparent.parent._setInfo(miparent._menuID, false, &miiPopup);
|
|
}
|
|
}
|
|
mparent._insert(mindex, true, &mii, (fType & MFT_SEPARATOR) ? null : mtext);
|
|
}
|
|
|
|
|
|
package final void _unsetParent()
|
|
{
|
|
assert(mparent);
|
|
assert(mparent.menuItems.length > 0);
|
|
assert(mparent.hmenu);
|
|
|
|
// Last child menu item, make the parent non-popup now.
|
|
if(mparent.menuItems.length == 1)
|
|
{
|
|
MenuItem miparent;
|
|
|
|
miparent = cast(MenuItem)mparent;
|
|
if(miparent && miparent.hmenu)
|
|
{
|
|
MENUITEMINFOA miiPopup;
|
|
|
|
miiPopup.cbSize = miiPopup.sizeof;
|
|
miiPopup.fMask = MIIM_SUBMENU;
|
|
miiPopup.hSubMenu = null;
|
|
miparent.parent._setInfo(miparent._menuID, false, &miiPopup);
|
|
|
|
miparent.hmenu = null;
|
|
}
|
|
}
|
|
|
|
mparent = null;
|
|
|
|
if(!Menu._compat092)
|
|
{
|
|
mindex = -1;
|
|
}
|
|
}
|
|
|
|
|
|
///
|
|
final @property void barBreak(bool byes) // setter
|
|
{
|
|
if(byes)
|
|
_type(_type() | MFT_MENUBARBREAK);
|
|
else
|
|
_type(_type() & ~MFT_MENUBARBREAK);
|
|
}
|
|
|
|
/// ditto
|
|
final @property bool barBreak() // getter
|
|
{
|
|
return (_type() & MFT_MENUBARBREAK) != 0;
|
|
}
|
|
|
|
|
|
// Can't be break().
|
|
|
|
///
|
|
final @property void breakItem(bool byes) // setter
|
|
{
|
|
if(byes)
|
|
_type(_type() | MFT_MENUBREAK);
|
|
else
|
|
_type(_type() & ~MFT_MENUBREAK);
|
|
}
|
|
|
|
/// ditto
|
|
final @property bool breakItem() // getter
|
|
{
|
|
return (_type() & MFT_MENUBREAK) != 0;
|
|
}
|
|
|
|
|
|
///
|
|
final @property void checked(bool byes) // setter
|
|
{
|
|
if(byes)
|
|
_state(_state() | MFS_CHECKED);
|
|
else
|
|
_state(_state() & ~MFS_CHECKED);
|
|
}
|
|
|
|
/// ditto
|
|
final @property bool checked() // getter
|
|
{
|
|
return (_state() & MFS_CHECKED) != 0;
|
|
}
|
|
|
|
|
|
///
|
|
final @property void defaultItem(bool byes) // setter
|
|
{
|
|
if(byes)
|
|
_state(_state() | MFS_DEFAULT);
|
|
else
|
|
_state(_state() & ~MFS_DEFAULT);
|
|
}
|
|
|
|
/// ditto
|
|
final @property bool defaultItem() // getter
|
|
{
|
|
return (_state() & MFS_DEFAULT) != 0;
|
|
}
|
|
|
|
|
|
///
|
|
final @property void enabled(bool byes) // setter
|
|
{
|
|
if(byes)
|
|
_state(_state() & ~MFS_GRAYED);
|
|
else
|
|
_state(_state() | MFS_GRAYED);
|
|
}
|
|
|
|
/// ditto
|
|
final @property bool enabled() // getter
|
|
{
|
|
return (_state() & MFS_GRAYED) == 0;
|
|
}
|
|
|
|
|
|
///
|
|
final @property void index(int idx) // setter
|
|
{// Note: probably fails when the parent exists because mparent is still set and menuItems.insert asserts it's null.
|
|
if(mparent)
|
|
{
|
|
if(cast(uint)idx > mparent.menuItems.length)
|
|
throw new DflException("Invalid menu index");
|
|
|
|
//RemoveMenu(mparent.handle, mid, MF_BYCOMMAND);
|
|
mparent._remove(mid, MF_BYCOMMAND);
|
|
mparent.menuItems._delitem(mindex);
|
|
|
|
/+
|
|
mindex = idx;
|
|
_setParent();
|
|
mparent.menuItems._additem(this);
|
|
+/
|
|
mparent.menuItems.insert(idx, this);
|
|
}
|
|
|
|
if(Menu._compat092)
|
|
{
|
|
mindex = idx;
|
|
}
|
|
}
|
|
|
|
/// ditto
|
|
final @property int index() // getter
|
|
{
|
|
return mindex;
|
|
}
|
|
|
|
|
|
override @property bool isParent() // getter
|
|
{
|
|
return handle != null; // ?
|
|
}
|
|
|
|
|
|
deprecated final @property void mergeOrder(int ord) // setter
|
|
{
|
|
//mergeord = ord;
|
|
}
|
|
|
|
deprecated final @property int mergeOrder() // getter
|
|
{
|
|
//return mergeord;
|
|
return 0;
|
|
}
|
|
|
|
|
|
// TODO: mergeType().
|
|
|
|
|
|
///
|
|
// Returns a NUL char if none.
|
|
final @property char mnemonic() // getter
|
|
{
|
|
bool singleAmp = false;
|
|
|
|
foreach(char ch; mtext)
|
|
{
|
|
if(singleAmp)
|
|
{
|
|
if(ch == '&')
|
|
singleAmp = false;
|
|
else
|
|
return ch;
|
|
}
|
|
else
|
|
{
|
|
if(ch == '&')
|
|
singleAmp = true;
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
/+
|
|
// TODO: implement owner drawn menus.
|
|
|
|
final @property void ownerDraw(bool byes) // setter
|
|
{
|
|
|
|
}
|
|
|
|
final @property bool ownerDraw() // getter
|
|
{
|
|
|
|
}
|
|
+/
|
|
|
|
|
|
///
|
|
final @property void radioCheck(bool byes) // setter
|
|
{
|
|
auto par = parent;
|
|
auto pidx = index;
|
|
if(par)
|
|
par.menuItems._removing(pidx, this);
|
|
|
|
if(byes)
|
|
//_type(_type() | MFT_RADIOCHECK);
|
|
fType |= MFT_RADIOCHECK;
|
|
else
|
|
//_type(_type() & ~MFT_RADIOCHECK);
|
|
fType &= ~MFT_RADIOCHECK;
|
|
|
|
if(par)
|
|
par.menuItems._added(pidx, this);
|
|
}
|
|
|
|
/// ditto
|
|
final @property bool radioCheck() // getter
|
|
{
|
|
return (_type() & MFT_RADIOCHECK) != 0;
|
|
}
|
|
|
|
|
|
// TODO: shortcut(), showShortcut().
|
|
|
|
|
|
/+
|
|
// TODO: need to fake this ?
|
|
|
|
final @property void visible(bool byes) // setter
|
|
{
|
|
// ?
|
|
mvisible = byes;
|
|
}
|
|
|
|
final @property bool visible() // getter
|
|
{
|
|
return mvisible;
|
|
}
|
|
+/
|
|
|
|
|
|
///
|
|
final void performClick()
|
|
{
|
|
onClick(EventArgs.empty);
|
|
}
|
|
|
|
|
|
///
|
|
final void performSelect()
|
|
{
|
|
onSelect(EventArgs.empty);
|
|
}
|
|
|
|
|
|
// Used internally.
|
|
this(HMENU hmenu, bool owned = true) // package
|
|
{
|
|
super(hmenu, owned);
|
|
_init();
|
|
}
|
|
|
|
|
|
///
|
|
this(MenuItem[] items)
|
|
{
|
|
if(items.length)
|
|
{
|
|
HMENU hm = CreatePopupMenu();
|
|
super(hm);
|
|
}
|
|
else
|
|
{
|
|
super();
|
|
}
|
|
_init();
|
|
|
|
menuItems.addRange(items);
|
|
}
|
|
|
|
/// ditto
|
|
this(Dstring text)
|
|
{
|
|
_init();
|
|
|
|
this.text = text;
|
|
}
|
|
|
|
/// ditto
|
|
this(Dstring text, MenuItem[] items)
|
|
{
|
|
if(items.length)
|
|
{
|
|
HMENU hm = CreatePopupMenu();
|
|
super(hm);
|
|
}
|
|
else
|
|
{
|
|
super();
|
|
}
|
|
_init();
|
|
|
|
this.text = text;
|
|
|
|
menuItems.addRange(items);
|
|
}
|
|
|
|
/// ditto
|
|
this()
|
|
{
|
|
_init();
|
|
}
|
|
|
|
|
|
~this()
|
|
{
|
|
Application.removeMenu(this);
|
|
|
|
debug(APP_PRINT)
|
|
cprintf("~MenuItem\n");
|
|
}
|
|
|
|
|
|
override Dstring toString()
|
|
{
|
|
return text;
|
|
}
|
|
|
|
|
|
override Dequ opEquals(Object o)
|
|
{
|
|
return text == getObjectString(o);
|
|
}
|
|
|
|
|
|
Dequ opEquals(Dstring val)
|
|
{
|
|
return text == val;
|
|
}
|
|
|
|
|
|
override int opCmp(Object o)
|
|
{
|
|
return stringICmp(text, getObjectString(o));
|
|
}
|
|
|
|
|
|
int opCmp(Dstring val)
|
|
{
|
|
return stringICmp(text, val);
|
|
}
|
|
|
|
|
|
protected override void onReflectedMessage(ref Message m)
|
|
{
|
|
super.onReflectedMessage(m);
|
|
|
|
switch(m.msg)
|
|
{
|
|
case WM_COMMAND:
|
|
assert(LOWORD(m.wParam) == mid);
|
|
|
|
onClick(EventArgs.empty);
|
|
break;
|
|
|
|
case WM_MENUSELECT:
|
|
onSelect(EventArgs.empty);
|
|
break;
|
|
|
|
case WM_INITMENUPOPUP:
|
|
assert(!HIWORD(m.lParam));
|
|
//assert(cast(HMENU)msg.wParam == mparent.handle);
|
|
assert(cast(HMENU)m.wParam == handle);
|
|
//assert(GetMenuItemID(mparent.handle, LOWORD(msg.lParam)) == mid);
|
|
|
|
onPopup(EventArgs.empty);
|
|
break;
|
|
|
|
default:
|
|
}
|
|
}
|
|
|
|
|
|
//EventHandler click;
|
|
Event!(MenuItem, EventArgs) click; ///
|
|
//EventHandler popup;
|
|
Event!(MenuItem, EventArgs) popup; ///
|
|
//EventHandler select;
|
|
Event!(MenuItem, EventArgs) select; ///
|
|
|
|
|
|
protected:
|
|
|
|
///
|
|
final @property int menuID() // getter
|
|
{
|
|
return mid;
|
|
}
|
|
|
|
|
|
package final @property int _menuID()
|
|
{
|
|
return mid;
|
|
}
|
|
|
|
|
|
///
|
|
void onClick(EventArgs ea)
|
|
{
|
|
click(this, ea);
|
|
}
|
|
|
|
|
|
///
|
|
void onPopup(EventArgs ea)
|
|
{
|
|
popup(this, ea);
|
|
}
|
|
|
|
|
|
///
|
|
void onSelect(EventArgs ea)
|
|
{
|
|
select(this, ea);
|
|
}
|
|
|
|
|
|
private:
|
|
|
|
int mid; // Menu ID.
|
|
Dstring mtext;
|
|
Menu mparent;
|
|
UINT fType = 0; // MFT_*
|
|
UINT fState = 0;
|
|
int mindex = -1; //0;
|
|
//int mergeord = 0;
|
|
|
|
enum SEPARATOR_TEXT = "-";
|
|
|
|
static assert(!MFS_UNCHECKED);
|
|
static assert(!MFT_STRING);
|
|
|
|
|
|
void _init()
|
|
{
|
|
if(Menu._compat092)
|
|
{
|
|
mindex = 0;
|
|
}
|
|
|
|
mid = Application.addMenuItem(this);
|
|
}
|
|
|
|
|
|
@property void _type(UINT newType) // setter
|
|
{
|
|
if(mparent)
|
|
{
|
|
MENUITEMINFOA mii;
|
|
|
|
mii.cbSize = mii.sizeof;
|
|
mii.fMask = MIIM_TYPE;
|
|
mii.fType = newType;
|
|
|
|
mparent._setInfo(mid, false, &mii);
|
|
}
|
|
|
|
fType = newType;
|
|
}
|
|
|
|
|
|
@property UINT _type() // getter
|
|
{
|
|
// if(mparent) fetch value ?
|
|
return fType;
|
|
}
|
|
|
|
|
|
@property void _state(UINT newState) // setter
|
|
{
|
|
if(mparent)
|
|
{
|
|
MENUITEMINFOA mii;
|
|
|
|
mii.cbSize = mii.sizeof;
|
|
mii.fMask = MIIM_STATE;
|
|
mii.fState = newState;
|
|
|
|
mparent._setInfo(mid, false, &mii);
|
|
}
|
|
|
|
fState = newState;
|
|
}
|
|
|
|
|
|
@property UINT _state() // getter
|
|
{
|
|
// if(mparent) fetch value ? No: Windows seems to add disabled/gray when the text is empty.
|
|
return fState;
|
|
}
|
|
}
|
|
|
|
|
|
///
|
|
abstract class Menu: DObject // docmain
|
|
{
|
|
// Retain DFL 0.9.2 compatibility.
|
|
deprecated static void setDFL092()
|
|
{
|
|
version(SET_DFL_092)
|
|
{
|
|
pragma(msg, "DFL: DFL 0.9.2 compatibility set at compile time");
|
|
}
|
|
else
|
|
{
|
|
//_compat092 = true;
|
|
Application.setCompat(DflCompat.MENU_092);
|
|
}
|
|
}
|
|
|
|
version(SET_DFL_092)
|
|
private enum _compat092 = true;
|
|
else version(DFL_NO_COMPAT)
|
|
private enum _compat092 = false;
|
|
else
|
|
private static @property bool _compat092() // getter
|
|
{ return 0 != (Application._compat & DflCompat.MENU_092); }
|
|
|
|
|
|
///
|
|
static class MenuItemCollection
|
|
{
|
|
protected this(Menu owner)
|
|
{
|
|
_owner = owner;
|
|
}
|
|
|
|
|
|
package final void _additem(MenuItem mi)
|
|
{
|
|
// Fix indices after this point.
|
|
int idx;
|
|
idx = mi.index + 1; // Note, not orig idx.
|
|
if(idx < items.length)
|
|
{
|
|
foreach(MenuItem onmi; items[idx .. items.length])
|
|
{
|
|
onmi.mindex++;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
// Note: clear() doesn't call this. Update: does now.
|
|
package final void _delitem(int idx)
|
|
{
|
|
// Fix indices after this point.
|
|
if(idx < items.length)
|
|
{
|
|
foreach(MenuItem onmi; items[idx .. items.length])
|
|
{
|
|
onmi.mindex--;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
/+
|
|
void insert(int index, MenuItem mi)
|
|
{
|
|
mi.mindex = index;
|
|
mi._setParent(_owner);
|
|
_additem(mi);
|
|
}
|
|
+/
|
|
|
|
|
|
void add(MenuItem mi)
|
|
{
|
|
if(!Menu._compat092)
|
|
{
|
|
mi.mindex = length.toI32;
|
|
}
|
|
|
|
/+
|
|
mi._setParent(_owner);
|
|
_additem(mi);
|
|
+/
|
|
insert(mi.mindex, mi);
|
|
}
|
|
|
|
void add(Dstring value)
|
|
{
|
|
return add(new MenuItem(value));
|
|
}
|
|
|
|
|
|
void addRange(MenuItem[] items)
|
|
{
|
|
if(!Menu._compat092)
|
|
return _wraparray.addRange(items);
|
|
|
|
foreach(MenuItem it; items)
|
|
{
|
|
insert(length.toI32, it);
|
|
}
|
|
}
|
|
|
|
void addRange(Dstring[] items)
|
|
{
|
|
if(!Menu._compat092)
|
|
return _wraparray.addRange(items);
|
|
|
|
foreach(Dstring it; items)
|
|
{
|
|
insert(length.toI32, it);
|
|
}
|
|
}
|
|
|
|
|
|
// TODO: finish.
|
|
|
|
|
|
package:
|
|
|
|
Menu _owner;
|
|
MenuItem[] items; // Kept populated so the menu can be moved around.
|
|
|
|
|
|
void _added(size_t idx, MenuItem val)
|
|
{
|
|
val.mindex = idx.toI32;
|
|
val._setParent(_owner);
|
|
_additem(val);
|
|
}
|
|
|
|
|
|
void _removing(size_t idx, MenuItem val)
|
|
{
|
|
if(size_t.max == idx) // Clear all.
|
|
{
|
|
}
|
|
else
|
|
{
|
|
val._unsetParent();
|
|
//RemoveMenu(_owner.handle, val._menuID, MF_BYCOMMAND);
|
|
//_owner._remove(val._menuID, MF_BYCOMMAND);
|
|
_owner._remove(idx.toI32, MF_BYPOSITION);
|
|
_delitem(idx.toI32);
|
|
}
|
|
}
|
|
|
|
|
|
public:
|
|
|
|
mixin ListWrapArray!(MenuItem, items,
|
|
_blankListCallback!(MenuItem), _added,
|
|
_removing, _blankListCallback!(MenuItem),
|
|
true, false, false,
|
|
true) _wraparray; // CLEAR_EACH
|
|
}
|
|
|
|
|
|
// Extra.
|
|
deprecated final void opOpAssign(string op)(MenuItem mi) if (op == "~")
|
|
{
|
|
menuItems.insert(menuItems.length.toI32, mi);
|
|
}
|
|
|
|
|
|
private void _init()
|
|
{
|
|
items = new MenuItemCollection(this);
|
|
}
|
|
|
|
|
|
// Menu item that isn't popup (yet).
|
|
protected this()
|
|
{
|
|
_init();
|
|
}
|
|
|
|
|
|
// Used internally.
|
|
this(HMENU hmenu, bool owned = true) // package
|
|
{
|
|
this.hmenu = hmenu;
|
|
this.owned = owned;
|
|
|
|
_init();
|
|
}
|
|
|
|
|
|
// Used internally.
|
|
this(HMENU hmenu, MenuItem[] items) // package
|
|
{
|
|
this.owned = true;
|
|
this.hmenu = hmenu;
|
|
|
|
_init();
|
|
|
|
menuItems.addRange(items);
|
|
}
|
|
|
|
|
|
// Don't call directly.
|
|
@disable this(MenuItem[] items);
|
|
/+{
|
|
/+
|
|
this.owned = true;
|
|
|
|
_init();
|
|
|
|
menuItems.addRange(items);
|
|
+/
|
|
|
|
assert(0);
|
|
}+/
|
|
|
|
|
|
~this()
|
|
{
|
|
if(owned)
|
|
DestroyMenu(hmenu);
|
|
}
|
|
|
|
|
|
///
|
|
final @property void tag(Object o) // setter
|
|
{
|
|
ttag = o;
|
|
}
|
|
|
|
/// ditto
|
|
final @property Object tag() // getter
|
|
{
|
|
return ttag;
|
|
}
|
|
|
|
|
|
///
|
|
final @property HMENU handle() // getter
|
|
{
|
|
return hmenu;
|
|
}
|
|
|
|
|
|
///
|
|
final @property MenuItemCollection menuItems() // getter
|
|
{
|
|
return items;
|
|
}
|
|
|
|
|
|
///
|
|
@property bool isParent() // getter
|
|
{
|
|
return false;
|
|
}
|
|
|
|
|
|
///
|
|
protected void onReflectedMessage(ref Message m)
|
|
{
|
|
}
|
|
|
|
|
|
package final void _reflectMenu(ref Message m)
|
|
{
|
|
onReflectedMessage(m);
|
|
}
|
|
|
|
|
|
/+ package +/ protected void _setInfo(UINT uItem, BOOL fByPosition, LPMENUITEMINFOA lpmii, Dstring typeData = null) // package
|
|
{
|
|
if(typeData.length)
|
|
{
|
|
if(dfl.internal.utf.useUnicode)
|
|
{
|
|
static assert(MENUITEMINFOW.sizeof == MENUITEMINFOA.sizeof);
|
|
lpmii.dwTypeData = cast(typeof(lpmii.dwTypeData))dfl.internal.utf.toUnicodez(typeData);
|
|
_setMenuItemInfoW(hmenu, uItem, fByPosition, cast(MENUITEMINFOW*)lpmii);
|
|
}
|
|
else
|
|
{
|
|
lpmii.dwTypeData = cast(typeof(lpmii.dwTypeData))dfl.internal.utf.unsafeAnsiz(typeData);
|
|
SetMenuItemInfoA(hmenu, uItem, fByPosition, lpmii);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
SetMenuItemInfoA(hmenu, uItem, fByPosition, lpmii);
|
|
}
|
|
}
|
|
|
|
|
|
/+ package +/ protected void _insert(UINT uItem, BOOL fByPosition, LPMENUITEMINFOA lpmii, Dstring typeData = null) // package
|
|
{
|
|
if(typeData.length)
|
|
{
|
|
if(dfl.internal.utf.useUnicode)
|
|
{
|
|
static assert(MENUITEMINFOW.sizeof == MENUITEMINFOA.sizeof);
|
|
lpmii.dwTypeData = cast(typeof(lpmii.dwTypeData))dfl.internal.utf.toUnicodez(typeData);
|
|
_insertMenuItemW(hmenu, uItem, fByPosition, cast(MENUITEMINFOW*)lpmii);
|
|
}
|
|
else
|
|
{
|
|
lpmii.dwTypeData = cast(typeof(lpmii.dwTypeData))dfl.internal.utf.unsafeAnsiz(typeData);
|
|
InsertMenuItemA(hmenu, uItem, fByPosition, lpmii);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
InsertMenuItemA(hmenu, uItem, fByPosition, lpmii);
|
|
}
|
|
}
|
|
|
|
|
|
/+ package +/ protected void _remove(UINT uPosition, UINT uFlags) // package
|
|
{
|
|
RemoveMenu(hmenu, uPosition, uFlags);
|
|
}
|
|
|
|
|
|
package HMENU hmenu;
|
|
|
|
private:
|
|
bool owned = true;
|
|
MenuItemCollection items;
|
|
Object ttag;
|
|
}
|
|
|
|
|
|
///
|
|
class MainMenu: Menu // docmain
|
|
{
|
|
// Used internally.
|
|
this(HMENU hmenu, bool owned = true)
|
|
{
|
|
super(hmenu, owned);
|
|
}
|
|
|
|
|
|
///
|
|
this()
|
|
{
|
|
super(CreateMenu());
|
|
}
|
|
|
|
/// ditto
|
|
this(MenuItem[] items)
|
|
{
|
|
super(CreateMenu(), items);
|
|
}
|
|
|
|
|
|
/+ package +/ protected override void _setInfo(UINT uItem, BOOL fByPosition, LPMENUITEMINFOA lpmii, Dstring typeData = null) // package
|
|
{
|
|
Menu._setInfo(uItem, fByPosition, lpmii, typeData);
|
|
|
|
if(hwnd)
|
|
DrawMenuBar(hwnd);
|
|
}
|
|
|
|
|
|
/+ package +/ protected override void _insert(UINT uItem, BOOL fByPosition, LPMENUITEMINFOA lpmii, Dstring typeData = null) // package
|
|
{
|
|
Menu._insert(uItem, fByPosition, lpmii, typeData);
|
|
|
|
if(hwnd)
|
|
DrawMenuBar(hwnd);
|
|
}
|
|
|
|
|
|
/+ package +/ protected override void _remove(UINT uPosition, UINT uFlags) // package
|
|
{
|
|
Menu._remove(uPosition, uFlags);
|
|
|
|
if(hwnd)
|
|
DrawMenuBar(hwnd);
|
|
}
|
|
|
|
|
|
private:
|
|
|
|
HWND hwnd = HWND.init;
|
|
|
|
|
|
package final void _setHwnd(HWND hwnd)
|
|
{
|
|
this.hwnd = hwnd;
|
|
}
|
|
}
|
|
}
|
|
|