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_HandleIPC_AddItem(tWindow *Window, size_t Length, const void *Data);
51 int Renderer_Menu_HandleIPC_SetFlags(tWindow *Window, size_t Length, const void *Data);
52 int Renderer_Menu_HandleMessage(tWindow *Window, int Msg, int Length, const void *Data);
55 const int ciMenu_Gap = 10; // Gap between label and shortcut
56 const int ciMenu_TopPadding = 2;
57 const int ciMenu_BottomPadding = 2;
58 const int ciMenu_LeftPadding = 2;
59 const int ciMenu_RightPadding = 2;
60 const int ciMenu_FontHeight = 16;
61 const int ciMenu_ItemHeight = 20;
62 const int ciMenu_SpacerHeight = 5;
63 const tColour cMenu_BackgroundColour = 0xCCCCCC;
64 const tColour cMenu_BorderColour = 0x000000;
65 const tColour cMenu_SpacerColour = 0x404040;
66 const tColour cMenu_LabelColour = 0x000000;
67 const tColour cMenu_ShortcutColour = 0x404040;
68 const tColour cMenu_HilightColour = 0xE0E0E0;
71 tWMRenderer gRenderer_Menu = {
73 .CreateWindow = Renderer_Menu_Create,
74 .Redraw = Renderer_Menu_Redraw,
75 .HandleMessage = Renderer_Menu_HandleMessage,
78 Renderer_Menu_HandleIPC_AddItem,
79 // Renderer_Menu_HandleIPC_SetFlags
82 tFont *gMenu_Font = NULL; // System monospace
85 void Renderer_Menu_Init(void)
87 WM_RegisterRenderer(&gRenderer_Menu);
90 tWindow *Renderer_Menu_Create(int Argument)
93 tMenuWindowInfo *info;
95 if(Argument < 5) Argument = 5;
96 if(Argument > 200) Argument = 200;
98 ret = WM_CreateWindowStruct(sizeof(*info) + Argument*sizeof(info->Items[0]));
99 info = ret->RendererInfo;
100 info->MaxItems = Argument;
101 info->HilightedItem = -1;
103 ret->Flags |= WINFLAG_NODECORATE;
104 ret->H = ciMenu_TopPadding + ciMenu_BottomPadding;
106 // _SysDebug("Renderer_Menu_Create: ->MaxItems = %i", info->MaxItems);
111 void Renderer_Menu_Redraw(tWindow *Window)
113 tMenuWindowInfo *info = Window->RendererInfo;
118 h = ciMenu_TopPadding + ciMenu_BottomPadding;
119 for( i = 0; i < info->nItems; i ++ )
121 if( !info->Items[i] ) continue;
123 if(info->Items[i]->Label)
124 h += ciMenu_ItemHeight;
126 h += ciMenu_SpacerHeight;
132 // _SysDebug("w = %i, h = %i", w, h);
134 // - Move the window such that it is on screen
135 // > Make sure to catch if the menu can't fit fully onscreen
138 WM_Render_FillRect(Window, 0, 0, w, h, cMenu_BackgroundColour);
139 WM_Render_DrawRect(Window, 0, 0, w, h, cMenu_BorderColour);
141 // - Render each item
142 y = ciMenu_TopPadding;
143 for( i = 0; i < info->nItems; i ++ )
145 tMenuItem *item = info->Items[i];
153 WM_Render_FillRect(Window,
154 1, y + ciMenu_SpacerHeight/2,
158 y += ciMenu_SpacerHeight;
163 if( info->HilightedItem == i )
165 WM_Render_FillRect(Window,
167 w-2, ciMenu_ItemHeight,
173 WM_Render_DrawText(Window,
174 ciMenu_LeftPadding, y+1,
175 w, ciMenu_ItemHeight,
183 WM_Render_FillRect(Window,
184 ciMenu_LeftPadding + item->UnderlineX, y + 1 + ciMenu_FontHeight,
193 WM_Render_DrawText(Window,
194 w - item->ShortcutWidth - ciMenu_RightPadding, y,
195 w, ciMenu_ItemHeight,
197 cMenu_ShortcutColour,
202 y += ciMenu_ItemHeight;
206 int Renderer_Menu_HandleIPC_AddItem(tWindow *Window, size_t Length, const void *Data)
208 const tMenuIPC_AddItem *Msg = Data;
209 tMenuWindowInfo *info = Window->RendererInfo;
214 if(Length < sizeof(*Msg) + 1 || Msg->Label[Length-sizeof(*Msg)-1] != '\0') {
215 _SysDebug("Renderer_Menu_int_AddItem: Size checks failed");
219 if(Msg->ID >= info->MaxItems) {
220 _SysDebug("Renderer_Menu_int_AddItem: ID (%i) >= MaxItems (%i)",
221 Msg->ID, info->MaxItems);
226 if(info->Items[Msg->ID]) {
227 _SysDebug("- Caught overwrite of %i", Msg->ID);
231 if(Msg->ID >= info->nItems) info->nItems = Msg->ID + 1;
233 item = malloc(sizeof(tMenuItem)+strlen(Msg->Label)+1);
234 info->Items[Msg->ID] = item;
236 if(Msg->Label[0] == '\0')
240 WM_ResizeWindow(Window, info->CachedW, Window->H+ciMenu_SpacerHeight);
246 char *dest = item->Data;
247 const char *src = Msg->Label;
251 item->KeyOffset = -1;
253 for(ofs = 0; *src && *src != '\t'; ofs ++)
257 item->KeyOffset = ofs;
265 // - Key combo / Shortcut
269 item->Shortcut = dest;
270 strcpy(item->Shortcut, src);
274 item->Shortcut = NULL;
278 // - Underline (hotkey)
279 if(item->KeyOffset == -1)
281 item->UnderlineX = 0;
282 item->UnderlineW = 0;
286 // Get width of preceding substring
287 WM_Render_GetTextDims(NULL, item->Label, item->KeyOffset, &item->UnderlineX, NULL);
288 // Get the width of the underlined character
289 // NOTE: 1 makes only one character be parsed, even if it is >1 byte long
290 WM_Render_GetTextDims(
291 NULL, item->Label+item->KeyOffset, 1,
292 &item->UnderlineW, NULL
296 WM_Render_GetTextDims(NULL, item->Label, -1, &item->LabelWidth, NULL);
298 WM_Render_GetTextDims(NULL, item->Shortcut, -1, &item->ShortcutWidth, NULL);
300 item->ShortcutWidth = 0;
302 // Get maximum lengths (to determine the size of the menu
303 if( item->LabelWidth > info->MaxLabelWidth )
304 info->MaxLabelWidth = item->LabelWidth;
305 if( item->ShortcutWidth > info->MaxShortcutWidth )
306 info->MaxShortcutWidth = item->ShortcutWidth;
309 // TODO: Check, do I want to resize down too?
310 // TODO: Take into account padding too
311 if( info->MaxLabelWidth + info->MaxShortcutWidth + ciMenu_Gap > info->CachedW )
313 info->CachedW = ciMenu_LeftPadding + info->MaxLabelWidth
314 + ciMenu_Gap + info->MaxShortcutWidth
315 + ciMenu_RightPadding;
317 WM_ResizeWindow(Window, info->CachedW, Window->H+ciMenu_ItemHeight);
323 * \brief Convert coordinates into an item index
325 int Renderer_Menu_int_GetItemByPos(tWindow *Window, tMenuWindowInfo *Info, int X, int Y)
329 if( X < 0 || X >= Window->W )
332 for( i = 0; i < Info->nItems; i ++ )
334 if( !Info->Items[i] ) continue;
336 if( !Info->Items[i]->Label )
338 // Spacer - not selectable
339 if(Y < ciMenu_SpacerHeight) {
342 Y -= ciMenu_SpacerHeight;
346 // Normal item, can be selected/hilighted
347 if(Y < ciMenu_ItemHeight) {
350 Y -= ciMenu_ItemHeight;
356 int Renderer_Menu_HandleMessage(tWindow *Window, int Msg, int Length, const void *Data)
358 tMenuWindowInfo *info = Window->RendererInfo;
362 const struct sWndMsg_Bool *msg = Data;
363 if(Length < sizeof(*msg)) return -1;
366 // _SysDebug(" - Shown, take focus");
367 // TODO: This shouldn't really be done, instead focus should be given
368 // when the menu is shown.
369 // WM_FocusWindow(Window);
374 _SysDebug("- Hidden, hide the children!");
378 const struct sWndMsg_Bool *msg = Data;
379 if(Length < sizeof(*msg)) return -1;
381 // TODO: Catch if focus was given away to a child
382 _SysDebug("- Lost focus");
383 WM_ShowWindow(Window, 0); // Hide!
386 _SysDebug("- Focus gained, TODO: Show accel keys");
390 case WNDMSG_MOUSEBTN: {
391 const struct sWndMsg_MouseButton *msg = Data;
394 if(Length < sizeof(*msg)) return -1;
396 if(msg->Button == 0 && msg->bPressed == 0)
398 item = Renderer_Menu_int_GetItemByPos(Window, info, msg->X, msg->Y);
401 tMenuMsg_Select _msg;
402 // TODO: Ignore sub-menus too
404 WM_SendMessage(Window, Window, MSG_MENU_SELECT, sizeof(_msg), &_msg);
405 WM_ShowWindow(Window, 0);
412 case WNDMSG_MOUSEMOVE: {
413 const struct sWndMsg_MouseMove *msg = Data;
416 if(Length < sizeof(*msg)) return -1;
418 new_hilight = Renderer_Menu_int_GetItemByPos(Window, info, msg->X, msg->Y);
420 if( new_hilight != info->HilightedItem )
422 info->HilightedItem = new_hilight;
423 // TODO: Change sub-menu
424 WM_Invalidate(Window);
429 // Only message to pass to client
430 case MSG_MENU_SELECT: