* - Pop-up menu window class/renderer
*/
#include <common.h>
+#include <wm_renderer.h>
#include <menu_messages.h>
+#include <wm_messages.h>
+#include <stdlib.h>
+#include <string.h>
// === STRUCTURES ===
typedef struct sMenuItem
typedef struct sMenuWindowInfo
{
int MaxLabelWidth;
- int MinLabelWidth;
-
+ int MaxShortcutWidth;
+ int CachedW;
+
+ int HilightedItem;
+
int MaxItems;
int nItems;
tMenuItem *Items[];
} tMenuWindowInfo;
+// === PROTOTYPES ===
+void Renderer_Menu_Init(void);
+tWindow *Renderer_Menu_Create(int Argument);
+void Renderer_Menu_Redraw(tWindow *Window);
+ int Renderer_Menu_HandleMessage(tWindow *Window, int Msg, int Length, void *Data);
+
+// === CONSTANTS ===
+const int ciMenu_Gap = 10; // Gap between label and shortcut
+const int ciMenu_TopPadding = 2;
+const int ciMenu_BottomPadding = 2;
+const int ciMenu_LeftPadding = 2;
+const int ciMenu_RightPadding = 2;
+const int ciMenu_FontHeight = 16;
+const int ciMenu_ItemHeight = 20;
+const int ciMenu_SpacerHeight = 5;
+const tColour cMenu_BackgroundColour = 0xCCCCCC;
+const tColour cMenu_BorderColour = 0x000000;
+const tColour cMenu_SpacerColour = 0x404040;
+const tColour cMenu_LabelColour = 0x000000;
+const tColour cMenu_ShortcutColour = 0x404040;
+const tColour cMenu_HilightColour = 0xE0E0E0;
+
// === GLOBALS ===
tWMRenderer gRenderer_Menu = {
.Name = "Menu",
.Redraw = Renderer_Menu_Redraw,
.HandleMessage = Renderer_Menu_HandleMessage
};
+tFont *gMenu_Font = NULL; // System monospace
// === CODE ===
void Renderer_Menu_Init(void)
ret = WM_CreateWindowStruct(sizeof(*info) + Argument*sizeof(info->Items[0]));
info = ret->RendererInfo;
info->MaxItems = Argument;
+ info->HilightedItem = -1;
+
+ ret->Flags |= WINFLAG_NODECORATE;
+ ret->H = ciMenu_TopPadding + ciMenu_BottomPadding;
+
+// _SysDebug("Renderer_Menu_Create: ->MaxItems = %i", info->MaxItems);
return ret;
}
void Renderer_Menu_Redraw(tWindow *Window)
{
- // TODO: Implement Renderer_Menu_Redraw
-
- // - Resize window to contain all items
+ tMenuWindowInfo *info = Window->RendererInfo;
+ int w, h, y, i;
+
+ w = info->CachedW;
+ #if 0
+ h = ciMenu_TopPadding + ciMenu_BottomPadding;
+ for( i = 0; i < info->nItems; i ++ )
+ {
+ if( !info->Items[i] ) continue;
+
+ if(info->Items[i]->Label)
+ h += ciMenu_ItemHeight;
+ else
+ h += ciMenu_SpacerHeight;
+ }
+ #else
+ h = Window->H;
+ #endif
+
+// _SysDebug("w = %i, h = %i", w, h);
// - Move the window such that it is on screen
// > Make sure to catch if the menu can't fit fully onscreen
+ // - Clear
+ WM_Render_FillRect(Window, 0, 0, w, h, cMenu_BackgroundColour);
+ WM_Render_DrawRect(Window, 0, 0, w, h, cMenu_BorderColour);
+
// - Render each item
+ y = ciMenu_TopPadding;
+ for( i = 0; i < info->nItems; i ++ )
+ {
+ tMenuItem *item = info->Items[i];
+
+ // Unused slot
+ if(!item) continue;
+
+ // Spacer
+ if(!item->Label)
+ {
+ WM_Render_FillRect(Window,
+ 1, y + ciMenu_SpacerHeight/2,
+ w-2, 1,
+ cMenu_SpacerColour
+ );
+ y += ciMenu_SpacerHeight;
+ continue ;
+ }
+
+ // Hilight
+ if( info->HilightedItem == i )
+ {
+ WM_Render_FillRect(Window,
+ 1, y,
+ w-2, ciMenu_ItemHeight,
+ cMenu_HilightColour
+ );
+ }
+
+ // Text
+ WM_Render_DrawText(Window,
+ ciMenu_LeftPadding, y,
+ w, ciMenu_ItemHeight,
+ gMenu_Font,
+ cMenu_LabelColour,
+ item->Label
+ );
+ // Underline
+ if(item->UnderlineW)
+ {
+ WM_Render_FillRect(Window,
+ ciMenu_LeftPadding + item->UnderlineX, y + ciMenu_FontHeight,
+ item->UnderlineW, 1,
+ cMenu_LabelColour
+ );
+ }
+
+ // Shortcut key
+ if(item->Shortcut)
+ {
+ WM_Render_DrawText(Window,
+ w - item->ShortcutWidth - ciMenu_RightPadding, y,
+ w, ciMenu_ItemHeight,
+ gMenu_Font,
+ cMenu_ShortcutColour,
+ item->Shortcut
+ );
+ }
+
+ y += ciMenu_ItemHeight;
+ }
}
-int Renderer_Menu_HandleMessage(tWindow *Window, int Msg, int Length, void *Data)
+int Renderer_Menu_int_AddItem(tWindow *Window, int Length, void *Data)
{
- switch(Msg)
+ tMenuWindowInfo *info = Window->RendererInfo;
+ tMenuMsg_AddItem *req = Data;
+ tMenuItem *item;
+
+ // Sanity checking
+ // - Message length
+ if(Length < sizeof(*req) + 1 || req->Label[Length-sizeof(*req)-1] != '\0') {
+ _SysDebug("Renderer_Menu_int_AddItem: Size checks failed");
+ return -1;
+ }
+ // - ID Number
+ if(req->ID >= info->MaxItems) {
+ _SysDebug("Renderer_Menu_int_AddItem: ID (%i) >= MaxItems (%i)",
+ req->ID, info->MaxItems);
+ return -1;
+ }
+
+ // Don't overwrite
+ if(info->Items[req->ID]) {
+ _SysDebug("- Caught overwrite of %i", req->ID);
+ return 0;
+ }
+ // Bookkeeping
+ if(req->ID >= info->nItems) info->nItems = req->ID + 1;
+ // Allocate
+ item = malloc(sizeof(tMenuItem)+strlen(req->Label));
+ info->Items[req->ID] = item;
+
+ if(req->Label[0] == '\0')
{
- case MSG_MENU_ADDITEM: {
- tMenuMsg_AddItem *req = Data;
- tMenuItem *item;
- if(Length < sizeof(*req) + 1) return -1;
- if(req->Label[Length-sizeof(*req)] != '\0') return -1;
+ // Spacer
+ item->Label = NULL;
+ WM_ResizeWindow(Window, info->CachedW, Window->H+ciMenu_SpacerHeight);
- if(info->Items[req->ID]) break;
- item = malloc(sizeof(tMenuItem)+strlen(req->Label));
- info->Items[req->ID] = item;
-
- if(req->Label[0] == '\0')
+ return 0;
+ }
+
+ // Actual item
+ char *dest = item->Data;
+ char *src = req->Label;
+ int ofs = 0;
+
+ // - Main label
+ item->KeyOffset = -1;
+ item->Label = dest;
+ for(ofs = 0; *src && *src != '\t'; ofs ++)
+ {
+ if(*src == '&') {
+ *dest = '\0';
+ item->KeyOffset = ofs;
+ src ++;
+ }
+ else {
+ *dest++ = *src++;
+ }
+ }
+ *dest++ = '\0';
+ // - Key combo / Shortcut
+ if(*src)
+ {
+ src ++;
+ item->Shortcut = dest;
+ strcpy(item->Shortcut, src);
+ }
+ else
+ {
+ item->Shortcut = NULL;
+ }
+
+ // Get dimensions
+ // - Underline (hotkey)
+ if(item->KeyOffset == -1)
+ {
+ item->UnderlineX = 0;
+ item->UnderlineW = 0;
+ }
+ else
+ {
+ char tmp = item->Label[item->KeyOffset];
+ // Get width of preceding substring
+ item->Label[item->KeyOffset] = '\0';
+ WM_Render_GetTextDims(NULL, item->Label, &item->UnderlineX, NULL);
+ // Get the width of the underlined character
+ // TODO: Fix for high UTF-8 characters
+ item->Label[item->KeyOffset] = tmp;
+ tmp = item->Label[item->KeyOffset+1];
+ item->Label[item->KeyOffset+1] = '\0';
+ WM_Render_GetTextDims(
+ NULL, item->Label+item->KeyOffset,
+ &item->UnderlineW, NULL
+ );
+ item->Label[item->KeyOffset+1] = tmp;
+ }
+ // - Labels
+ WM_Render_GetTextDims(NULL, item->Label, &item->LabelWidth, NULL);
+ if(item->Shortcut)
+ WM_Render_GetTextDims(NULL, item->Shortcut, &item->ShortcutWidth, NULL);
+ else
+ item->ShortcutWidth = 0;
+
+ if( item->LabelWidth > info->MaxLabelWidth )
+ info->MaxLabelWidth = item->LabelWidth;
+ if( item->ShortcutWidth > info->MaxShortcutWidth )
+ info->MaxShortcutWidth = item->ShortcutWidth;
+
+ if( info->MaxLabelWidth + info->MaxShortcutWidth + ciMenu_Gap > info->CachedW )
+ {
+ info->CachedW = ciMenu_LeftPadding + info->MaxLabelWidth
+ + ciMenu_Gap + info->MaxShortcutWidth
+ + ciMenu_RightPadding;
+ }
+ WM_ResizeWindow(Window, info->CachedW, Window->H+ciMenu_ItemHeight);
+
+ return 0;
+}
+
+int Renderer_Menu_int_GetItemByPos(tWindow *Window, tMenuWindowInfo *Info, int X, int Y)
+{
+ int i;
+
+ if( X < 0 || X >= Window->W )
+ return -1;
+
+ for( i = 0; i < Info->nItems; i ++ )
+ {
+ if( !Info->Items[i] ) continue;
+
+ if( !Info->Items[i]->Label )
{
- // Spacer
- item->Label = NULL;
+ // Spacer - not selectable
+ if(Y < ciMenu_SpacerHeight) {
+ return -1;
+ }
+ Y -= ciMenu_SpacerHeight;
}
else
{
- // Actual item
- char *dest = item->Data;
- char *src = req->Label;
- int ofs = 0;
- // - Main label
- item->KeyOffset = -1;
- item->Label = dest;
- for(ofs = 0; *src && *src != '\t'; ofs ++)
- {
- if(*src == '&') {
- *dest = '\0';
- item->KeyOffset = ofs;
- src ++;
- }
- else {
- *dest++ = *src++;
- }
+ // Normal item, can be selected/hilighted
+ if(Y < ciMenu_ItemHeight) {
+ return i;
}
- *dest = '\0';
- // - Key combo / Shortcut
- if(*src)
- {
- src ++;
- item->Shortcut = dest;
- strcpy(item->Shortcut, src);
- }
- else
- {
- item->Shortcut = NULL;
- }
-
- // Get dimensions
- // - Underline (hotkey)
- if(item->KeyOffset == -1)
- {
- item->UnderlineX = 0;
- item->UnderlineW = 0;
- }
- else
+ Y -= ciMenu_ItemHeight;
+ }
+ }
+ return -1;
+}
+
+int Renderer_Menu_HandleMessage(tWindow *Window, int Msg, int Length, void *Data)
+{
+ tMenuWindowInfo *info = Window->RendererInfo;
+ switch(Msg)
+ {
+ case WNDMSG_SHOW: {
+ struct sWndMsg_Bool *msg = Data;
+ if(Length < sizeof(*msg)) return -1;
+ if(msg->Val)
+ {
+// _SysDebug(" - Shown, take focus");
+ // TODO: This shouldn't really be done, instead focus should be given
+ // when the menu is shown.
+// WM_FocusWindow(Window);
+ }
+ else
+ {
+ // Hide Children
+ _SysDebug("- Hidden, hide the children!");
+ }
+ return 0; }
+ case WNDMSG_FOCUS: {
+ struct sWndMsg_Bool *msg = Data;
+ if(Length < sizeof(*msg)) return -1;
+ if(!msg->Val) {
+ // TODO: Catch if focus was given away to a child
+ _SysDebug("- Lost focus");
+ WM_ShowWindow(Window, 0); // Hide!
+ }
+ else {
+ _SysDebug("- Focus gained, TODO: Show accel keys");
+ }
+ return 0; }
+
+ case WNDMSG_MOUSEBTN: {
+ struct sWndMsg_MouseButton *msg = Data;
+ int item;
+
+ if(Length < sizeof(*msg)) return -1;
+
+ if(msg->Button == 0 && msg->bPressed == 0)
+ {
+ item = Renderer_Menu_int_GetItemByPos(Window, info, msg->X, msg->Y);
+ if(item != -1)
{
- char tmp = item->Label[item->KeyOffset];
- item->Label[item->KeyOffset] = '\0';
- WM_Render_GetTextDims(NULL, item->Label, &item->UnderlineX, NULL);
- item->Label[item->KeyOffset] = tmp;
- tmp = item->Label[item->KeyOffset+1];
- item->Label[item->KeyOffset+1] = '\0';
- WM_Render_GetTextDims(NULL, item->Label+item->KeyOffset, &item->UnderlineW, NULL);
- item->Label[item->KeyOffset+1] = tmp;
+ tMenuMsg_Select _msg;
+ // TODO: Ignore sub-menus too
+ _msg.ID = item;
+ WM_SendMessage(Window, Window, MSG_MENU_SELECT, sizeof(_msg), &_msg);
+ WM_ShowWindow(Window, 0);
}
- // - Labels
- WM_Render_GetTextDims(NULL, item->Label, &item->LabelWidth, NULL);
- if(item->Shortcut)
- WM_Render_GetTextDims(NULL, item->Shortcut, &item->ShortcutWidth, NULL);
- else
- item->ShortcutWidth = 0;
}
-
- break; }
+
+
+ return 0; }
+
+ case WNDMSG_MOUSEMOVE: {
+ struct sWndMsg_MouseMove *msg = Data;
+ int new_hilight;
+
+ if(Length < sizeof(*msg)) return -1;
+
+ new_hilight = Renderer_Menu_int_GetItemByPos(Window, info, msg->X, msg->Y);
+
+ if( new_hilight != info->HilightedItem )
+ {
+ info->HilightedItem = new_hilight;
+ // TODO: Change sub-menu
+ WM_Invalidate(Window);
+ }
+
+ return 0; }
+
+ // Manipulation messages
+ case MSG_MENU_ADDITEM:
+// _SysDebug("MSG_MENU_ADDITEM");
+ return Renderer_Menu_int_AddItem(Window, Length, Data);
// Only message to pass to client
case MSG_MENU_SELECT: