* - 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;
+
+ _SysDebug("Renderer_Menu_Create: ->MaxItems = %i", info->MaxItems);
return ret;
}
void Renderer_Menu_Redraw(tWindow *Window)
{
- // TODO: Implement Renderer_Menu_Redraw
-
+ tMenuWindowInfo *info = Window->RendererInfo;
+ int w, h, y, i;
+ _SysDebug("TODO: Implement Renderer_Menu_Redraw");
+
+// _SysDebug("info->nItems = %i", info->nItems);
+
+ w = info->CachedW;
+ 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;
+ }
+
+// _SysDebug("w = %i, h = %i", w, h);
+
// - Resize window to contain all items
+ WM_ResizeWindow(Window, 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 + 1,
+ 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_int_AddItem(tWindow *Window, int Length, void *Data)
+{
+ 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]) 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')
+ {
+ // Spacer
+ item->Label = NULL;
+
+ 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;
+ // TODO: Smarter height?
+ WM_ResizeWindow(Window, info->CachedW, info->nItems*ciMenu_ItemHeight);
+ }
+
+ return 0;
}
int Renderer_Menu_HandleMessage(tWindow *Window, int Msg, int Length, void *Data)
{
switch(Msg)
{
- 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;
-
- if(info->Items[req->ID]) break;
- item = malloc(sizeof(tMenuItem)+strlen(req->Label));
- info->Items[req->ID] = item;
-
- if(req->Label[0] == '\0')
+ case WNDMSG_MOUSEMOVE: {
+ tMenuWindowInfo *info = Window->RendererInfo;
+ struct sWndMsg_MouseMove *msg = Data;
+ int new_hilight;
+
+ if(Length < sizeof(*msg)) return -1;
+
+ if( msg->X < 0 || msg->X >= Window->W )
{
- // Spacer
- item->Label = NULL;
+ new_hilight = -1;
}
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 ++)
+ int i, y;
+ y = msg->Y;
+ new_hilight = -1;
+ for( i = 0; i < info->nItems; i ++ )
{
- if(*src == '&') {
- *dest = '\0';
- item->KeyOffset = ofs;
- src ++;
+ if( !info->Items[i] ) continue;
+
+ if( !info->Items[i]->Label )
+ {
+ // Spacer - doesn't hilight
+ if(y < ciMenu_SpacerHeight) {
+ new_hilight = -1;
+ break;
+ }
+ y -= ciMenu_SpacerHeight;
}
- else {
- *dest++ = *src++;
+ else
+ {
+ // Normal item, set the hilight
+ if(y < ciMenu_ItemHeight) {
+ new_hilight = i;
+ break;
+ }
+ y -= ciMenu_ItemHeight;
}
}
- *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];
- 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;
- }
- // - 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; }
+
+ if( new_hilight != info->HilightedItem )
+ {
+ info->HilightedItem = new_hilight;
+ 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: