4 // Graphical User Interface support
7 // JLH = James Hammons <jlhamm@acm.org>
10 // --- ---------- ------------------------------------------------------------
11 // JLH 02/03/2006 Created this file
12 // JLH 03/13/2006 Added functions to allow shutting down GUI externally
13 // JLH 03/22/2006 Finalized basic multiple window support
17 // - Memory leak on quitting with a window active [DONE]
18 // - Multiple window handling [DONE]
23 #include "menu.h" // Element class methods are pulled in here...
27 #include "diskselector.h"
28 #include "diskwindow.h"
31 #include "applevideo.h"
34 //#define DEBUG_MAIN_LOOP
36 // New main screen buffering
37 // This works, but the colors are rendered incorrectly. Also, it seems that there's
38 // fullscreen blitting still going on--dragging the disk is fast at first but then
39 // gets painfully slow. Not sure what's going on there.
40 //#define USE_NEW_MAINBUFFERING
42 //#ifdef DEBUG_MAIN_LOOP
47 Work flow: Draw floppy drive.
48 If disk in drive, MO shows eject graphic, otherwise show load graphic.
49 If hit 'new blank image':
50 If disk in drive, ask if want to save if modified
52 If hit 'swap disks', swap disks.
56 GUI::GUI(SDL_Surface * surface): menuItem(new MenuItems())
58 Element::SetScreen(surface);
59 // windowList.push_back(new Menu());
61 // Create drive windows, and config windows here...
62 windowList.push_back(new Window(30, 30, 200, 100));
63 windowList.push_back(new Window(30, 140, 200, 100));
64 windowList.push_back(new Button(30, 250, "Click!"));
65 windowList.push_back(new Text(30, 20, floppyDrive.ImageName(0)));
66 windowList.push_back(new Text(30, 130, floppyDrive.ImageName(1)));
67 windowList.push_back(new DiskWindow(&floppyDrive, 240, 20));
73 // Clean up menuItem, if any
80 for(std::list<Element *>::iterator i=windowList.begin(); i!=windowList.end(); i++)
86 void GUI::AddMenuTitle(const char * title)
88 menuItem->title = title;
89 menuItem->item.clear();
93 void GUI::AddMenuItem(const char * item, Element * (* a)(void)/*= NULL*/, SDL_Scancode k/*= SDLK_UNKNOWN*/)
95 menuItem->item.push_back(NameAction(item, a, k));
99 void GUI::CommitItemsToMenu(void)
101 //We could just do a simple check here to see if more than one item is in the list,
102 //and if so fail. Make it so you build the menu first before allowing any other action. [DONE]
104 //Right now, we just silently fail...
105 if (windowList.size() > 1)
107 WriteLog("GUI: Can't find menu--more than one item in windowList!\n");
111 ((Menu *)(*windowList.begin()))->Add(*menuItem);
120 std::list<Element *>::iterator i;
122 // Not sure what replaces this in SDL2...
123 // SDL_EnableKeyRepeat(150, 75);
125 // Also: Need to pick up backbuffer (for those windows that have them)
128 // Initial update... [Now handled correctly in the constructor]
129 // Uh, still needed here, though... Only makes sense that it should
130 for(i=windowList.begin(); i!=windowList.end(); i++)
133 #ifndef USE_NEW_MAINBUFFERING
134 RenderScreenBuffer();
142 // if (SDL_PollEvent(&event))
143 if (SDL_WaitEvent(&event))
145 #ifdef DEBUG_MAIN_LOOP
146 WriteLog("An event was found!");
148 if (event.type == SDL_USEREVENT)
150 #ifdef DEBUG_MAIN_LOOP
151 WriteLog(" -- SDL_USEREVENT\n");
153 //Mebbe add another user event for screen refresh? Why not!
154 if (event.user.code == WINDOW_CLOSE)
156 for(i=windowList.begin(); i!=windowList.end(); i++)
158 if (*i == (Element *)event.user.data1)
166 else if (event.user.code == MENU_ITEM_CHOSEN)
168 // Confused? Let me enlighten... What we're doing here is casting
169 // data1 as a pointer to a function which returns a Element pointer and
170 // which takes no parameters (the "(Element *(*)(void))" part), then
171 // derefencing it (the "*" in front of that) in order to call the
172 // function that it points to. Clear as mud? Yeah, I hate function
173 // pointers too, but what else are you gonna do?
174 Element * window = (*(Element *(*)(void))event.user.data1)();
177 windowList.push_back(window);
179 while (SDL_PollEvent(&event)); // Flush the event queue...
181 event.type = SDL_MOUSEMOTION;
183 SDL_GetMouseState(&mx, &my);
184 event.motion.x = mx, event.motion.y = my;
185 SDL_PushEvent(&event); // & update mouse position...!
187 oldMouse.x = mouse.x, oldMouse.y = mouse.y;
188 mouse.x = mx, mouse.y = my; // This prevents "mouse flash"...
190 //There's a *small* problem with the following approach--if a window and a bunch of
191 //child widgets send this message, we'll get a bunch of unnecessary refresh events...
192 //This could be controlled by having the main window refresh itself intelligently...
194 //What we could do instead is set a variable in Element and check it after the fact
195 //to see whether or not a refresh is needed.
196 //[This is what we do now.]
198 //Dirty rectangle is also possible...
199 else if (event.user.code == SCREEN_REFRESH_NEEDED)
200 #ifndef USE_NEW_MAINBUFFERING
201 RenderScreenBuffer();
206 //Not sure what to do here for SDL2...
208 else if (event.type == SDL_ACTIVEEVENT)
210 //Need to do a screen refresh here...
211 if (event.active.state == SDL_APPMOUSEFOCUS)
212 showMouse = (event.active.gain ? true : false);
214 #ifndef USE_NEW_MAINBUFFERING
215 RenderScreenBuffer();
221 else if (event.type == SDL_KEYDOWN)
223 #ifdef DEBUG_MAIN_LOOP
224 WriteLog(" -- SDL_KEYDOWN\n");
226 if (event.key.keysym.sym == SDLK_F1)
229 //Not sure that this is the right way to handle this...
230 //Probably should only give this to the top level window...
231 // for(i=windowList.begin(); i!=windowList.end(); i++)
232 // (*i)->HandleKey(event.key.keysym.sym);
233 windowList.back()->HandleKey(event.key.keysym.scancode);
235 else if (event.type == SDL_MOUSEMOTION)
237 #ifdef DEBUG_MAIN_LOOP
238 WriteLog(" -- SDL_MOUSEMOTION\n");
240 //This is for tracking a custom mouse cursor, which we're not doing--YET.
241 oldMouse.x = mouse.x, oldMouse.y = mouse.y;
242 mouse.x = event.motion.x, mouse.y = event.motion.y;
244 //Not sure that this is the right way to handle this...
245 //Right now, we should probably only do mouseover for the last item in the list...
247 //Though, it seems to screw other things up. Maybe it IS better to pass it to all windows?
248 //Or maybe to just the ones that aren't completely obscured?
249 //Probably. Right now, a disk's close button that should be obscured by one sitting on
250 //top of it gets redrawn. Not good. !!! FIX !!!
251 for(i=windowList.begin(); i!=windowList.end(); i++)
252 (*i)->HandleMouseMove(mouse.x, mouse.y);
253 // windowList.back()->HandleMouseMove(mouse.x, mouse.y);
255 else if (event.type == SDL_MOUSEBUTTONDOWN)
257 #ifdef DEBUG_MAIN_LOOP
258 WriteLog(" -- SDL_MOUSEBUTTONDOWN\n");
260 //Not sure that this is the right way to handle this...
261 // What we should do here is ensure that whatever has been clicked on gets moved to the
262 // highest priority--in our current data schema that would be the end of the list... !!! FIX !!!
267 We could do the following:
269 - Go through list and find which window has been clicked on (if any). If more
270 than one is clicked on, take the one highest in the Z order (closer to the end
273 - If item is highest in Z order, pass click through to window and exit.
275 - Otherwise, restore backing store on each window in reverse order.
277 - Remove item clicked on from the list. Put removed item at the end of the list.
279 - Go through list and pass click through to each window in the list. Also do a
280 blit to backing store and a Draw() for each window.
282 Could also do a check (if not clicked on highest Z window) to see which windows
283 it overlaps and just do restore/redraw for those that overlap. To wit:
285 - Create new list containing only those windows that overlap the clicking on window.
287 - Go through list and do a blit to backing store and a Draw() for each window.
289 - Go through list and pass click through to each window in the list.
295 for(i=windowList.begin(); i!=windowList.end(); i++)
296 (*i)->HandleMouseButton(event.button.x, event.button.y, true);
298 // We use the 1st algorithm here, since it's simpler. If we need to, we can optimize
301 // Walk backward through the list and see if a window was hit.
302 // This will automagically return us the window with the highest Z.
304 std::list<Element *>::reverse_iterator ri;
305 std::list<Element *>::iterator hit;// = windowList.end();
307 for(ri=windowList.rbegin(); ri!=windowList.rend(); ri++)
309 if ((*ri)->Inside(event.button.x, event.button.y))
311 // Here's a bit of STL weirdness: Converting from a reverse
312 // iterator to a regular iterator requires backing the iterator
313 // up a position after grabbing it's base() OR going forward
314 // one position with the reverse iterator before grabbing base().
315 // Ugly, but it gets the job done...
317 // Put it back where we found it, so the tests following this
324 // If we hit the highest in the list, then pass the event through
325 // to the window for handling. if we hit no windows, then pass the
326 // event to all windows. Otherwise, we need to shuffle windows.
328 //NOTE: We need to pass the click to all windows regardless of whether they're topmost or not...
329 if (ri == windowList.rbegin())
331 for(i=windowList.begin(); i!=windowList.end(); i++)
332 (*i)->HandleMouseButton(event.button.x, event.button.y, true);
334 else if (ri == windowList.rend())
336 for(i=windowList.begin(); i!=windowList.end(); i++)
337 (*i)->HandleMouseButton(event.button.x, event.button.y, true);
341 // - Otherwise, restore backing store on each window in reverse order.
342 for(ri=windowList.rbegin(); ri!=windowList.rend(); ri++)
343 (*ri)->RestoreScreenFromBackstore();
344 // At this point, the screen has been restored...
346 // - Remove item clicked on from the list. Put removed item at the end of the list.
347 windowList.push_back(*hit);
348 windowList.erase(hit);
349 // - Go through list and pass click through to each window in the list. Also do a
350 // blit to backing store and a Draw() for each window.
351 for(i=windowList.begin(); i!= windowList.end(); i++)
353 // Grab bg into backstore
354 (*i)->SaveScreenToBackstore();
356 (*i)->HandleMouseButton(event.button.x, event.button.y, true);
364 A slightly different way to handle this would be to loop through all windows, compare
365 all those above it to see if they obscure it; if so then subdivide it's update rectangle
366 to eliminate drawing the parts that aren't shown. The beauty of this approach is that
367 you don't have to care what order the windows are drawn in and you don't need to worry
368 about the order of restoring the backing store.
370 You *do* still need to determine the Z-order of the windows, in order to get the subdivisions
371 correct, but that's not too terrible.
373 Also, when doing a window drag, the coverage lists for all windows have to be regenerated.
375 std::list<Element *>::reverse_iterator ri;
376 bool movedWindow = false;
378 for(ri=windowList.rbegin(); ri!=windowList.rend(); ri++)
380 if ((*ri)->Inside(event.button.x, event.button.y))
382 // Remove item clicked on from the list & put removed item at the
383 // end of the list, thus putting the window at the top of the Z
384 // order. But IFF window is not already topmost!
385 if (ri != windowList.rbegin())
387 windowList.push_back(*ri);
388 // Here's a bit of STL weirdness: Converting from a reverse
389 // iterator to a regular iterator requires backing the iterator
390 // up a position after grabbing it's base() OR going forward
391 // one position with the reverse iterator before grabbing base().
392 // Ugly, but it get the job done...
393 windowList.erase((++ri).base());
401 //Small problem here: we should only pass the *hit* to the topmost window and pass
402 //*misses* to everyone else... Otherwise, you can have overlapping draggable windows
403 //and be able to drag both by clicking on a point that intersects both...
404 //(though that may be an interesting way to handle things!)
405 //The thing is that you want to do it on purpose (like with a special grouping widget)
406 //instead of by accident. So, !!! FIX !!!
407 // Pass the click on to all windows
408 // for(i=windowList.begin(); i!=windowList.end(); i++)
409 // (*i)->HandleMouseButton(event.button.x, event.button.y, true);
410 windowList.back()->HandleMouseButton(event.button.x, event.button.y, true);
412 // // & bail if nothing changed...
416 // Check for overlap/build coverage lists [O((n^2)/2) algorithm!]
417 //One way to optimize this would be to only reset coverage lists from the point in
418 //the Z order where the previous window was.
419 for(i=windowList.begin(); i!=windowList.end(); i++)
421 //One other little quirk: Probably need to clear the backing store as well!
423 (*i)->ResetCoverageList();
425 // This looks odd, but it's just a consequence of iterator weirdness.
426 // Otherwise we could just stick a j+1 in the for loop below. :-P
427 std::list<Element *>::iterator j = i;
430 for(; j!=windowList.end(); j++)
431 (*i)->AdjustCoverageList((*j)->GetExtents());
433 // (*i)->HandleMouseButton(event.button.x, event.button.y, true);
438 else if (event.type == SDL_MOUSEBUTTONUP)
440 #ifdef DEBUG_MAIN_LOOP
441 WriteLog(" -- SDL_MOUSEBUTTONUP\n");
443 //Not sure that this is the right way to handle this...
444 for(i=windowList.begin(); i!=windowList.end(); i++)
445 (*i)->HandleMouseButton(event.button.x, event.button.y, false);
446 //I think we should only do topmost here...
448 // windowList.back()->HandleMouseButton(event.button.x, event.button.y, false);
450 #ifdef DEBUG_MAIN_LOOP
452 WriteLog(" -- Unknown event\n");
455 if (Element::ScreenNeedsRefreshing())
457 #ifndef USE_NEW_MAINBUFFERING
458 #ifdef DEBUG_MAIN_LOOP
459 WriteLog("Screen refresh called!\n");
461 RenderScreenBuffer();
462 Element::ScreenWasRefreshed();
465 Element::ScreenWasRefreshed();
469 //hm. Works, but slows things way down.
470 //Now we use WaitEvents() instead. Yay!
474 // Not sure what to do for this in SDL 2...
475 // SDL_EnableKeyRepeat(0, 0);
488 // NEW GUI STARTS HERE
492 // Okay, this is ugly but works and I can't think of any better way to handle
493 // this. So what we do when we pass the GIMP bitmaps into a function is pass
494 // them as a (void *) and then cast them as type (Bitmap *) in order to use
495 // them. Yes, it's ugly. Come up with something better!
500 unsigned int bytesPerPixel; // 3:RGB, 4:RGBA
501 unsigned char pixelData[];
507 #include "applevideo.h"
508 #include "diskselector.h"
512 // Icons, in GIMP "C" format
513 #include "gfx/icon-selection.c"
514 #include "gfx/disk-icon.c"
515 #include "gfx/power-off-icon.c"
516 #include "gfx/power-on-icon.c"
517 #include "gfx/disk-swap-icon.c"
518 #include "gfx/disk-door-open.c"
519 #include "gfx/disk-door-closed.c"
520 #include "gfx/save-state-icon.c"
521 #include "gfx/load-state-icon.c"
522 #include "gfx/config-icon.c"
525 const char numeralOne[(7 * 7) + 1] =
534 const char numeralTwo[(7 * 7) + 1] =
543 const char ejectIcon[(8 * 7) + 1] =
552 const char driveLight[(5 * 5) + 1] =
560 enum { SBS_SHOWING, SBS_HIDING, SBS_SHOWN, SBS_HIDDEN };
563 SDL_Texture * GUI2::overlay = NULL;
564 SDL_Rect GUI2::olDst;
565 int GUI2::sidebarState = SBS_HIDDEN;
566 int32_t GUI2::dx = 0;
567 int32_t GUI2::iconSelected = -1;
568 bool GUI2::hasKeyboardFocus = false;
569 int32_t lastIconSelected = -1;
570 SDL_Texture * iconSelection = NULL;
571 SDL_Texture * diskIcon = NULL;
572 SDL_Texture * disk1Icon = NULL;
573 SDL_Texture * disk2Icon = NULL;
574 SDL_Texture * powerOnIcon = NULL;
575 SDL_Texture * powerOffIcon = NULL;
576 SDL_Texture * diskSwapIcon = NULL;
577 SDL_Texture * stateSaveIcon = NULL;
578 SDL_Texture * stateLoadIcon = NULL;
579 SDL_Texture * configIcon = NULL;
580 SDL_Texture * doorOpen = NULL;
581 SDL_Texture * doorClosed = NULL;
582 uint32_t texturePointer[128 * 380];
583 const char iconHelp[7][80] = { "Turn emulated Apple off/on",
584 "Insert floppy image into drive #1", "Insert floppy image into drive #2",
585 "Swap disks", "Save emulator state", "Load emulator state",
586 "Configure Apple2" };
588 #define SIDEBAR_X_POS (VIRTUAL_SCREEN_WIDTH - 80)
601 void GUI2::Init(SDL_Renderer * renderer)
603 overlay = SDL_CreateTexture(renderer, SDL_PIXELFORMAT_ABGR8888,
604 SDL_TEXTUREACCESS_TARGET, 128, 380);
608 WriteLog("GUI: Could not create overlay!\n");
612 if (SDL_SetTextureBlendMode(overlay, SDL_BLENDMODE_BLEND) == -1)
613 WriteLog("GUI: Could not set blend mode for overlay.\n");
615 for(uint32_t i=0; i<128*380; i++)
616 texturePointer[i] = 0xB0A000A0;
618 SDL_UpdateTexture(overlay, NULL, texturePointer, 128 * sizeof(Uint32));
620 olDst.x = VIRTUAL_SCREEN_WIDTH;
625 iconSelection = CreateTexture(renderer, &icon_selection);
626 diskIcon = CreateTexture(renderer, &disk_icon);
627 doorOpen = CreateTexture(renderer, &door_open);
628 doorClosed = CreateTexture(renderer, &door_closed);
629 disk1Icon = CreateTexture(renderer, &disk_icon);
630 disk2Icon = CreateTexture(renderer, &disk_icon);
631 powerOffIcon = CreateTexture(renderer, &power_off);
632 powerOnIcon = CreateTexture(renderer, &power_on);
633 diskSwapIcon = CreateTexture(renderer, &disk_swap);
634 stateSaveIcon = CreateTexture(renderer, &save_state);
635 stateLoadIcon = CreateTexture(renderer, &load_state);
636 configIcon = CreateTexture(renderer, &config);
638 // Set up drive icons in their current states
639 // AssembleDriveIcon(renderer, 0);
640 // AssembleDriveIcon(renderer, 1);
642 if (SDL_SetRenderTarget(renderer, overlay) < 0)
644 WriteLog("GUI: Could not set Render Target to overlay... (%s)\n", SDL_GetError());
648 DrawSidebarIcons(renderer);
649 // Set render target back to default
650 SDL_SetRenderTarget(renderer, NULL);
653 DiskSelector::Init(renderer);
654 DiskSelector::showWindow = true;
656 WriteLog("GUI: Successfully initialized.\n");
660 SDL_Texture * GUI2::CreateTexture(SDL_Renderer * renderer, const void * source)
662 Bitmap * bitmap = (Bitmap *)source;
663 SDL_Texture * texture = SDL_CreateTexture(renderer, SDL_PIXELFORMAT_ABGR8888,
664 // SDL_TEXTUREACCESS_STATIC, bitmap->width, bitmap->height);
665 SDL_TEXTUREACCESS_TARGET, bitmap->width, bitmap->height);
666 SDL_SetTextureBlendMode(texture, SDL_BLENDMODE_BLEND);
667 SDL_UpdateTexture(texture, NULL, (Uint32 *)bitmap->pixelData,
668 bitmap->width * sizeof(Uint32));
674 void GUI2::MouseDown(int32_t x, int32_t y, uint32_t buttons)
679 void GUI2::MouseUp(int32_t x, int32_t y, uint32_t buttons)
684 void GUI2::MouseMove(int32_t x, int32_t y, uint32_t buttons)
686 if (sidebarState != SBS_SHOWN)
690 if (x > SIDEBAR_X_POS)
692 //printf("GUI: sidebar showing (x = %i)...\n", x);
693 sidebarState = SBS_SHOWING;
698 //printf("GUI: sidebar hiding[1] (x = %i)...\n", x);
699 sidebarState = SBS_HIDING;
705 if (x < SIDEBAR_X_POS)
707 iconSelected = lastIconSelected = -1;
708 HandleIconSelection(sdlRenderer);
709 //printf("GUI: sidebar hiding[2] (x = %i)...\n", x);
710 sidebarState = SBS_HIDING;
713 // We're in the right zone, and the sidebar is shown, so let's select
717 if (y < 4 || y > 383)
722 iconSelected = (y - 4) / 54;
724 if (iconSelected != lastIconSelected)
726 HandleIconSelection(sdlRenderer);
727 lastIconSelected = iconSelected;
728 SpawnMessage("%s", iconHelp[iconSelected]);
730 // Show what's in the selected drive
731 if (iconSelected >= 1 && iconSelected <= 2)
733 if (!floppyDrive.IsEmpty(iconSelected - 1))
734 SpawnMessage("\"%s\"", floppyDrive.ImageName(iconSelected - 1));
742 void GUI2::HandleIconSelection(SDL_Renderer * renderer)
744 // Set up drive icons in their current states
745 AssembleDriveIcon(renderer, 0);
746 AssembleDriveIcon(renderer, 1);
748 // Reload the background...
749 SDL_UpdateTexture(overlay, NULL, texturePointer, 128 * sizeof(Uint32));
751 if (SDL_SetRenderTarget(renderer, overlay) < 0)
753 WriteLog("GUI: Could not set Render Target to overlay... (%s)\n", SDL_GetError());
757 // Draw the icon selector, if an icon is selected
758 if (iconSelected >= 0)
760 SDL_Rect dst;// = { 54, 54, 24 - 7, 2 };
761 dst.w = dst.h = 54, dst.x = 24 - 7, dst.y = 2 + (iconSelected * 54);
762 SDL_RenderCopy(renderer, iconSelection, NULL, &dst);
765 DrawSidebarIcons(renderer);
767 // Set render target back to default
768 SDL_SetRenderTarget(renderer, NULL);
772 void GUI2::AssembleDriveIcon(SDL_Renderer * renderer, int driveNumber)
774 SDL_Texture * drive[2] = { disk1Icon, disk2Icon };
775 const char * number[2] = { numeralOne, numeralTwo };
777 if (SDL_SetRenderTarget(renderer, drive[driveNumber]) < 0)
779 WriteLog("GUI: Could not set Render Target to overlay... (%s)\n", SDL_GetError());
783 SDL_RenderClear(renderer);
784 SDL_RenderCopy(renderer, diskIcon, NULL, NULL);
786 // Drive door @ (16, 7)
788 dst.w = 8, dst.h = 10, dst.x = 16, dst.y = 7;
789 SDL_RenderCopy(renderer, (floppyDrive.IsEmpty(driveNumber) ?
790 doorOpen : doorClosed), NULL, &dst);
792 // Numeral @ (30, 20)
793 DrawCharArray(renderer, number[driveNumber], 30, 20, 7, 7, 0xD0, 0xE0, 0xF0);
794 DrawDriveLight(renderer, driveNumber);
795 DrawEjectButton(renderer, driveNumber);
797 // Set render target back to default
798 SDL_SetRenderTarget(renderer, NULL);
802 void GUI2::DrawEjectButton(SDL_Renderer * renderer, int driveNumber)
804 if (floppyDrive.IsEmpty(driveNumber))
807 DrawCharArray(renderer, ejectIcon, 29, 31, 8, 7, 0x00, 0xAA, 0x00);
811 void GUI2::DrawDriveLight(SDL_Renderer * renderer, int driveNumber)
813 int lightState = floppyDrive.DriveLightStatus(driveNumber);
814 int r = 0x77, g = 0x00, b = 0x00;
816 if (lightState == DLS_READ)
817 r = 0x20, g = 0xFF, b = 0x20;
818 else if (lightState == DLS_WRITE)
819 r = 0xFF, g = 0x30, b = 0x30;
821 // Drive light @ (8, 21)
822 DrawCharArray(renderer, driveLight, 8, 21, 5, 5, r, g, b);
826 void GUI2::DrawCharArray(SDL_Renderer * renderer, const char * array, int x,
827 int y, int w, int h, int r, int g, int b)
829 SDL_SetRenderDrawColor(renderer, r, g, b, 0xFF);
831 for(int j=0; j<h; j++)
833 for(int i=0; i<w; i++)
835 if (array[(j * w) + i] != ' ')
836 SDL_RenderDrawPoint(renderer, x + i, y + j);
840 SDL_SetRenderDrawColor(renderer, 0x00, 0x00, 0x00, 0x00);
844 void GUI2::HandleGUIState(void)
848 if (olDst.x < SIDEBAR_X_POS && sidebarState == SBS_SHOWING)
850 olDst.x = SIDEBAR_X_POS;
851 sidebarState = SBS_SHOWN;
854 else if (olDst.x > VIRTUAL_SCREEN_WIDTH && sidebarState == SBS_HIDING)
856 olDst.x = VIRTUAL_SCREEN_WIDTH;
857 sidebarState = SBS_HIDDEN;
863 void GUI2::DrawSidebarIcons(SDL_Renderer * renderer)
865 SDL_Texture * icons[7] = { powerOnIcon, disk1Icon, disk2Icon, diskSwapIcon,
866 stateSaveIcon, stateLoadIcon, configIcon };
869 dst.w = dst.h = 40, dst.x = 24, dst.y = 2 + 7;
871 for(int i=0; i<7; i++)
873 SDL_RenderCopy(renderer, icons[i], NULL, &dst);
879 void GUI2::Render(SDL_Renderer * renderer)
886 if (sidebarState != SBS_HIDDEN)
887 HandleIconSelection(renderer);
889 SDL_RenderCopy(renderer, overlay, NULL, &olDst);
892 DiskSelector::Render(renderer);
901 cut into 7 pieces give ~54 pix per piece
902 So, let's try 40x40 icons, and see if that's good enough...
905 drive proportions: 1.62 : 1
918 | ^| <-- eject button
939 maybe state save/load