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, 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 _SysDebug("Renderer_Menu_Create: ->MaxItems = %i", info->MaxItems);
101 void Renderer_Menu_Redraw(tWindow *Window)
103 tMenuWindowInfo *info = Window->RendererInfo;
107 h = ciMenu_TopPadding + ciMenu_BottomPadding;
108 for( i = 0; i < info->nItems; i ++ )
110 if( !info->Items[i] ) continue;
112 if(info->Items[i]->Label)
113 h += ciMenu_ItemHeight;
115 h += ciMenu_SpacerHeight;
118 // _SysDebug("w = %i, h = %i", w, h);
120 // - Resize window to contain all items
121 WM_ResizeWindow(Window, w, h);
123 // - Move the window such that it is on screen
124 // > Make sure to catch if the menu can't fit fully onscreen
127 WM_Render_FillRect(Window, 0, 0, w, h, cMenu_BackgroundColour);
128 WM_Render_DrawRect(Window, 0, 0, w, h, cMenu_BorderColour);
130 // - Render each item
131 y = ciMenu_TopPadding;
132 for( i = 0; i < info->nItems; i ++ )
134 tMenuItem *item = info->Items[i];
142 WM_Render_FillRect(Window,
143 1, y + ciMenu_SpacerHeight/2,
147 y += ciMenu_SpacerHeight;
152 if( info->HilightedItem == i )
154 WM_Render_FillRect(Window,
156 w-2, ciMenu_ItemHeight,
162 WM_Render_DrawText(Window,
163 ciMenu_LeftPadding, y,
164 w, ciMenu_ItemHeight,
172 WM_Render_FillRect(Window,
173 ciMenu_LeftPadding + item->UnderlineX, y + ciMenu_FontHeight,
182 WM_Render_DrawText(Window,
183 w - item->ShortcutWidth - ciMenu_RightPadding, y,
184 w, ciMenu_ItemHeight,
186 cMenu_ShortcutColour,
191 y += ciMenu_ItemHeight;
195 int Renderer_Menu_int_AddItem(tWindow *Window, int Length, void *Data)
197 tMenuWindowInfo *info = Window->RendererInfo;
198 tMenuMsg_AddItem *req = Data;
203 if(Length < sizeof(*req) + 1 || req->Label[Length-sizeof(*req)-1] != '\0') {
204 _SysDebug("Renderer_Menu_int_AddItem: Size checks failed");
208 if(req->ID >= info->MaxItems) {
209 _SysDebug("Renderer_Menu_int_AddItem: ID (%i) >= MaxItems (%i)",
210 req->ID, info->MaxItems);
215 if(info->Items[req->ID]) return 0;
217 if(req->ID >= info->nItems) info->nItems = req->ID + 1;
219 item = malloc(sizeof(tMenuItem)+strlen(req->Label));
220 info->Items[req->ID] = item;
222 if(req->Label[0] == '\0')
231 char *dest = item->Data;
232 char *src = req->Label;
236 item->KeyOffset = -1;
238 for(ofs = 0; *src && *src != '\t'; ofs ++)
242 item->KeyOffset = ofs;
250 // - Key combo / Shortcut
254 item->Shortcut = dest;
255 strcpy(item->Shortcut, src);
259 item->Shortcut = NULL;
263 // - Underline (hotkey)
264 if(item->KeyOffset == -1)
266 item->UnderlineX = 0;
267 item->UnderlineW = 0;
271 char tmp = item->Label[item->KeyOffset];
272 // Get width of preceding substring
273 item->Label[item->KeyOffset] = '\0';
274 WM_Render_GetTextDims(NULL, item->Label, &item->UnderlineX, NULL);
275 // Get the width of the underlined character
276 // TODO: Fix for high UTF-8 characters
277 item->Label[item->KeyOffset] = tmp;
278 tmp = item->Label[item->KeyOffset+1];
279 item->Label[item->KeyOffset+1] = '\0';
280 WM_Render_GetTextDims(
281 NULL, item->Label+item->KeyOffset,
282 &item->UnderlineW, NULL
284 item->Label[item->KeyOffset+1] = tmp;
287 WM_Render_GetTextDims(NULL, item->Label, &item->LabelWidth, NULL);
289 WM_Render_GetTextDims(NULL, item->Shortcut, &item->ShortcutWidth, NULL);
291 item->ShortcutWidth = 0;
293 if( item->LabelWidth > info->MaxLabelWidth )
294 info->MaxLabelWidth = item->LabelWidth;
295 if( item->ShortcutWidth > info->MaxShortcutWidth )
296 info->MaxShortcutWidth = item->ShortcutWidth;
298 if( info->MaxLabelWidth + info->MaxShortcutWidth + ciMenu_Gap > info->CachedW )
300 info->CachedW = ciMenu_LeftPadding + info->MaxLabelWidth
301 + ciMenu_Gap + info->MaxShortcutWidth
302 + ciMenu_RightPadding;
303 // TODO: Smarter height?
304 // Doesn't matter a lot here really
305 WM_ResizeWindow(Window, info->CachedW, info->nItems*ciMenu_ItemHeight);
311 int Renderer_Menu_int_GetItemByPos(tWindow *Window, tMenuWindowInfo *Info, int X, int Y)
315 if( X < 0 || X >= Window->W )
318 for( i = 0; i < Info->nItems; i ++ )
320 if( !Info->Items[i] ) continue;
322 if( !Info->Items[i]->Label )
324 // Spacer - not selectable
325 if(Y < ciMenu_SpacerHeight) {
328 Y -= ciMenu_SpacerHeight;
332 // Normal item, can be selected/hilighted
333 if(Y < ciMenu_ItemHeight) {
336 Y -= ciMenu_ItemHeight;
342 int Renderer_Menu_HandleMessage(tWindow *Window, int Msg, int Length, void *Data)
344 tMenuWindowInfo *info = Window->RendererInfo;
348 struct sWndMsg_Bool *msg = Data;
349 if(Length < sizeof(*msg)) return -1;
352 // _SysDebug(" - Shown, take focus");
353 // TODO: This shouldn't really be done, instead focus should be given
354 // when the menu is shown.
355 // WM_FocusWindow(Window);
360 _SysDebug("- Hidden, hide the children!");
364 struct sWndMsg_Bool *msg = Data;
365 if(Length < sizeof(*msg)) return -1;
367 // TODO: Catch if focus was given away to a child
368 _SysDebug("- Lost focus");
369 WM_ShowWindow(Window, 0); // Hide!
372 _SysDebug("- Focus gained, TODO: Show accel keys");
376 case WNDMSG_MOUSEBTN: {
377 struct sWndMsg_MouseButton *msg = Data;
380 if(Length < sizeof(*msg)) return -1;
382 if(msg->Button == 0 && msg->bPressed == 0)
384 item = Renderer_Menu_int_GetItemByPos(Window, info, msg->X, msg->Y);
387 tMenuMsg_Select _msg;
388 // TODO: Ignore sub-menus too
390 WM_SendMessage(Window, Window, MSG_MENU_SELECT, sizeof(_msg), &_msg);
391 WM_ShowWindow(Window, 0);
398 case WNDMSG_MOUSEMOVE: {
399 struct sWndMsg_MouseMove *msg = Data;
402 if(Length < sizeof(*msg)) return -1;
404 new_hilight = Renderer_Menu_int_GetItemByPos(Window, info, msg->X, msg->Y);
406 if( new_hilight != info->HilightedItem )
408 info->HilightedItem = new_hilight;
409 // TODO: Change sub-menu
410 WM_Invalidate(Window);
415 // Manipulation messages
416 case MSG_MENU_ADDITEM:
417 _SysDebug("MSG_MENU_ADDITEM");
418 return Renderer_Menu_int_AddItem(Window, Length, Data);
420 // Only message to pass to client
421 case MSG_MENU_SELECT: