2 * Acess2 Window Manager v3
3 * - By John Hodge (thePowersGang)
6 * - Pop-up menu window class/renderer
9 #include <wm_renderer.h>
10 #include <menu_messages.h>
11 #include <wm_messages.h>
16 typedef struct sMenuItem
33 typedef struct sMenuWindowInfo
47 void Renderer_Menu_Init(void);
48 tWindow *Renderer_Menu_Create(int Argument);
49 void Renderer_Menu_Redraw(tWindow *Window);
50 int Renderer_Menu_HandleMessage(tWindow *Window, int Msg, int Length, const void *Data);
53 const int ciMenu_Gap = 10; // Gap between label and shortcut
54 const int ciMenu_TopPadding = 2;
55 const int ciMenu_BottomPadding = 2;
56 const int ciMenu_LeftPadding = 2;
57 const int ciMenu_RightPadding = 2;
58 const int ciMenu_FontHeight = 16;
59 const int ciMenu_ItemHeight = 20;
60 const int ciMenu_SpacerHeight = 5;
61 const tColour cMenu_BackgroundColour = 0xCCCCCC;
62 const tColour cMenu_BorderColour = 0x000000;
63 const tColour cMenu_SpacerColour = 0x404040;
64 const tColour cMenu_LabelColour = 0x000000;
65 const tColour cMenu_ShortcutColour = 0x404040;
66 const tColour cMenu_HilightColour = 0xE0E0E0;
69 tWMRenderer gRenderer_Menu = {
71 .CreateWindow = Renderer_Menu_Create,
72 .Redraw = Renderer_Menu_Redraw,
73 .HandleMessage = Renderer_Menu_HandleMessage
75 tFont *gMenu_Font = NULL; // System monospace
78 void Renderer_Menu_Init(void)
80 WM_RegisterRenderer(&gRenderer_Menu);
83 tWindow *Renderer_Menu_Create(int Argument)
86 tMenuWindowInfo *info;
88 if(Argument < 5) Argument = 5;
89 if(Argument > 200) Argument = 200;
91 ret = WM_CreateWindowStruct(sizeof(*info) + Argument*sizeof(info->Items[0]));
92 info = ret->RendererInfo;
93 info->MaxItems = Argument;
94 info->HilightedItem = -1;
96 ret->Flags |= WINFLAG_NODECORATE;
97 ret->H = ciMenu_TopPadding + ciMenu_BottomPadding;
99 // _SysDebug("Renderer_Menu_Create: ->MaxItems = %i", info->MaxItems);
104 void Renderer_Menu_Redraw(tWindow *Window)
106 tMenuWindowInfo *info = Window->RendererInfo;
111 h = ciMenu_TopPadding + ciMenu_BottomPadding;
112 for( i = 0; i < info->nItems; i ++ )
114 if( !info->Items[i] ) continue;
116 if(info->Items[i]->Label)
117 h += ciMenu_ItemHeight;
119 h += ciMenu_SpacerHeight;
125 // _SysDebug("w = %i, h = %i", w, h);
127 // - Move the window such that it is on screen
128 // > Make sure to catch if the menu can't fit fully onscreen
131 WM_Render_FillRect(Window, 0, 0, w, h, cMenu_BackgroundColour);
132 WM_Render_DrawRect(Window, 0, 0, w, h, cMenu_BorderColour);
134 // - Render each item
135 y = ciMenu_TopPadding;
136 for( i = 0; i < info->nItems; i ++ )
138 tMenuItem *item = info->Items[i];
146 WM_Render_FillRect(Window,
147 1, y + ciMenu_SpacerHeight/2,
151 y += ciMenu_SpacerHeight;
156 if( info->HilightedItem == i )
158 WM_Render_FillRect(Window,
160 w-2, ciMenu_ItemHeight,
166 WM_Render_DrawText(Window,
167 ciMenu_LeftPadding, y+1,
168 w, ciMenu_ItemHeight,
176 WM_Render_FillRect(Window,
177 ciMenu_LeftPadding + item->UnderlineX, y + 1 + ciMenu_FontHeight,
186 WM_Render_DrawText(Window,
187 w - item->ShortcutWidth - ciMenu_RightPadding, y,
188 w, ciMenu_ItemHeight,
190 cMenu_ShortcutColour,
195 y += ciMenu_ItemHeight;
199 int Renderer_Menu_int_AddItem(tWindow *Window, int Length, const tMenuMsg_AddItem *Msg)
201 tMenuWindowInfo *info = Window->RendererInfo;
206 if(Length < sizeof(*Msg) + 1 || Msg->Label[Length-sizeof(*Msg)-1] != '\0') {
207 _SysDebug("Renderer_Menu_int_AddItem: Size checks failed");
211 if(Msg->ID >= info->MaxItems) {
212 _SysDebug("Renderer_Menu_int_AddItem: ID (%i) >= MaxItems (%i)",
213 Msg->ID, info->MaxItems);
218 if(info->Items[Msg->ID]) {
219 _SysDebug("- Caught overwrite of %i", Msg->ID);
223 if(Msg->ID >= info->nItems) info->nItems = Msg->ID + 1;
225 item = malloc(sizeof(tMenuItem)+strlen(Msg->Label)+1);
226 info->Items[Msg->ID] = item;
228 if(Msg->Label[0] == '\0')
232 WM_ResizeWindow(Window, info->CachedW, Window->H+ciMenu_SpacerHeight);
238 char *dest = item->Data;
239 const char *src = Msg->Label;
243 item->KeyOffset = -1;
245 for(ofs = 0; *src && *src != '\t'; ofs ++)
249 item->KeyOffset = ofs;
257 // - Key combo / Shortcut
261 item->Shortcut = dest;
262 strcpy(item->Shortcut, src);
266 item->Shortcut = NULL;
270 // - Underline (hotkey)
271 if(item->KeyOffset == -1)
273 item->UnderlineX = 0;
274 item->UnderlineW = 0;
278 // Get width of preceding substring
279 WM_Render_GetTextDims(NULL, item->Label, item->KeyOffset, &item->UnderlineX, NULL);
280 // Get the width of the underlined character
281 // NOTE: 1 makes only one character be parsed, even if it is >1 byte long
282 WM_Render_GetTextDims(
283 NULL, item->Label+item->KeyOffset, 1,
284 &item->UnderlineW, NULL
288 WM_Render_GetTextDims(NULL, item->Label, -1, &item->LabelWidth, NULL);
290 WM_Render_GetTextDims(NULL, item->Shortcut, -1, &item->ShortcutWidth, NULL);
292 item->ShortcutWidth = 0;
294 // Get maximum lengths (to determine the size of the menu
295 if( item->LabelWidth > info->MaxLabelWidth )
296 info->MaxLabelWidth = item->LabelWidth;
297 if( item->ShortcutWidth > info->MaxShortcutWidth )
298 info->MaxShortcutWidth = item->ShortcutWidth;
301 // TODO: Check, do I want to resize down too?
302 // TODO: Take into account padding too
303 if( info->MaxLabelWidth + info->MaxShortcutWidth + ciMenu_Gap > info->CachedW )
305 info->CachedW = ciMenu_LeftPadding + info->MaxLabelWidth
306 + ciMenu_Gap + info->MaxShortcutWidth
307 + ciMenu_RightPadding;
309 WM_ResizeWindow(Window, info->CachedW, Window->H+ciMenu_ItemHeight);
315 * \brief Convert coordinates into an item index
317 int Renderer_Menu_int_GetItemByPos(tWindow *Window, tMenuWindowInfo *Info, int X, int Y)
321 if( X < 0 || X >= Window->W )
324 for( i = 0; i < Info->nItems; i ++ )
326 if( !Info->Items[i] ) continue;
328 if( !Info->Items[i]->Label )
330 // Spacer - not selectable
331 if(Y < ciMenu_SpacerHeight) {
334 Y -= ciMenu_SpacerHeight;
338 // Normal item, can be selected/hilighted
339 if(Y < ciMenu_ItemHeight) {
342 Y -= ciMenu_ItemHeight;
348 int Renderer_Menu_HandleMessage(tWindow *Window, int Msg, int Length, const void *Data)
350 tMenuWindowInfo *info = Window->RendererInfo;
354 const struct sWndMsg_Bool *msg = Data;
355 if(Length < sizeof(*msg)) return -1;
358 // _SysDebug(" - Shown, take focus");
359 // TODO: This shouldn't really be done, instead focus should be given
360 // when the menu is shown.
361 // WM_FocusWindow(Window);
366 _SysDebug("- Hidden, hide the children!");
370 const struct sWndMsg_Bool *msg = Data;
371 if(Length < sizeof(*msg)) return -1;
373 // TODO: Catch if focus was given away to a child
374 _SysDebug("- Lost focus");
375 WM_ShowWindow(Window, 0); // Hide!
378 _SysDebug("- Focus gained, TODO: Show accel keys");
382 case WNDMSG_MOUSEBTN: {
383 const struct sWndMsg_MouseButton *msg = Data;
386 if(Length < sizeof(*msg)) return -1;
388 if(msg->Button == 0 && msg->bPressed == 0)
390 item = Renderer_Menu_int_GetItemByPos(Window, info, msg->X, msg->Y);
393 tMenuMsg_Select _msg;
394 // TODO: Ignore sub-menus too
396 WM_SendMessage(Window, Window, MSG_MENU_SELECT, sizeof(_msg), &_msg);
397 WM_ShowWindow(Window, 0);
404 case WNDMSG_MOUSEMOVE: {
405 const struct sWndMsg_MouseMove *msg = Data;
408 if(Length < sizeof(*msg)) return -1;
410 new_hilight = Renderer_Menu_int_GetItemByPos(Window, info, msg->X, msg->Y);
412 if( new_hilight != info->HilightedItem )
414 info->HilightedItem = new_hilight;
415 // TODO: Change sub-menu
416 WM_Invalidate(Window);
421 // Manipulation messages
422 case MSG_MENU_ADDITEM:
423 // _SysDebug("MSG_MENU_ADDITEM");
424 return Renderer_Menu_int_AddItem(Window, Length, Data);
426 // Only message to pass to client
427 case MSG_MENU_SELECT: