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]
22 #include "menu.h" // Element class methods are pulled in here...
26 #include "diskselector.h"
27 #include "diskwindow.h"
30 #include "applevideo.h"
33 //#define DEBUG_MAIN_LOOP
35 // New main screen buffering
36 // This works, but the colors are rendered incorrectly. Also, it seems that there's
37 // fullscreen blitting still going on--dragging the disk is fast at first but then
38 // gets painfully slow. Not sure what's going on there.
39 //#define USE_NEW_MAINBUFFERING
41 //#ifdef DEBUG_MAIN_LOOP
46 Work flow: Draw floppy drive.
47 If disk in drive, MO shows eject graphic, otherwise show load graphic.
48 If hit 'new blank image':
49 If disk in drive, ask if want to save if modified
51 If hit 'swap disks', swap disks.
55 GUI::GUI(SDL_Surface * surface): menuItem(new MenuItems())
57 Element::SetScreen(surface);
58 // windowList.push_back(new Menu());
60 // Create drive windows, and config windows here...
61 windowList.push_back(new Window(30, 30, 200, 100));
62 windowList.push_back(new Window(30, 140, 200, 100));
63 windowList.push_back(new Button(30, 250, "Click!"));
64 windowList.push_back(new Text(30, 20, floppyDrive.ImageName(0)));
65 windowList.push_back(new Text(30, 130, floppyDrive.ImageName(1)));
66 windowList.push_back(new DiskWindow(&floppyDrive, 240, 20));
72 // Clean up menuItem, if any
79 for(std::list<Element *>::iterator i=windowList.begin(); i!=windowList.end(); i++)
85 void GUI::AddMenuTitle(const char * title)
87 menuItem->title = title;
88 menuItem->item.clear();
92 void GUI::AddMenuItem(const char * item, Element * (* a)(void)/*= NULL*/, SDL_Scancode k/*= SDLK_UNKNOWN*/)
94 menuItem->item.push_back(NameAction(item, a, k));
98 void GUI::CommitItemsToMenu(void)
100 //We could just do a simple check here to see if more than one item is in the list,
101 //and if so fail. Make it so you build the menu first before allowing any other action. [DONE]
103 //Right now, we just silently fail...
104 if (windowList.size() > 1)
106 WriteLog("GUI: Can't find menu--more than one item in windowList!\n");
110 ((Menu *)(*windowList.begin()))->Add(*menuItem);
119 std::list<Element *>::iterator i;
121 // Not sure what replaces this in SDL2...
122 // SDL_EnableKeyRepeat(150, 75);
124 // Also: Need to pick up backbuffer (for those windows that have them)
127 // Initial update... [Now handled correctly in the constructor]
128 // Uh, still needed here, though... Only makes sense that it should
129 for(i=windowList.begin(); i!=windowList.end(); i++)
132 #ifndef USE_NEW_MAINBUFFERING
133 RenderScreenBuffer();
141 // if (SDL_PollEvent(&event))
142 if (SDL_WaitEvent(&event))
144 #ifdef DEBUG_MAIN_LOOP
145 WriteLog("An event was found!");
147 if (event.type == SDL_USEREVENT)
149 #ifdef DEBUG_MAIN_LOOP
150 WriteLog(" -- SDL_USEREVENT\n");
152 //Mebbe add another user event for screen refresh? Why not!
153 if (event.user.code == WINDOW_CLOSE)
155 for(i=windowList.begin(); i!=windowList.end(); i++)
157 if (*i == (Element *)event.user.data1)
165 else if (event.user.code == MENU_ITEM_CHOSEN)
167 // Confused? Let me enlighten... What we're doing here is casting
168 // data1 as a pointer to a function which returns a Element pointer and
169 // which takes no parameters (the "(Element *(*)(void))" part), then
170 // derefencing it (the "*" in front of that) in order to call the
171 // function that it points to. Clear as mud? Yeah, I hate function
172 // pointers too, but what else are you gonna do?
173 Element * window = (*(Element *(*)(void))event.user.data1)();
176 windowList.push_back(window);
178 while (SDL_PollEvent(&event)); // Flush the event queue...
180 event.type = SDL_MOUSEMOTION;
182 SDL_GetMouseState(&mx, &my);
183 event.motion.x = mx, event.motion.y = my;
184 SDL_PushEvent(&event); // & update mouse position...!
186 oldMouse.x = mouse.x, oldMouse.y = mouse.y;
187 mouse.x = mx, mouse.y = my; // This prevents "mouse flash"...
189 //There's a *small* problem with the following approach--if a window and a bunch of
190 //child widgets send this message, we'll get a bunch of unnecessary refresh events...
191 //This could be controlled by having the main window refresh itself intelligently...
193 //What we could do instead is set a variable in Element and check it after the fact
194 //to see whether or not a refresh is needed.
195 //[This is what we do now.]
197 //Dirty rectangle is also possible...
198 else if (event.user.code == SCREEN_REFRESH_NEEDED)
199 #ifndef USE_NEW_MAINBUFFERING
200 RenderScreenBuffer();
205 //Not sure what to do here for SDL2...
207 else if (event.type == SDL_ACTIVEEVENT)
209 //Need to do a screen refresh here...
210 if (event.active.state == SDL_APPMOUSEFOCUS)
211 showMouse = (event.active.gain ? true : false);
213 #ifndef USE_NEW_MAINBUFFERING
214 RenderScreenBuffer();
220 else if (event.type == SDL_KEYDOWN)
222 #ifdef DEBUG_MAIN_LOOP
223 WriteLog(" -- SDL_KEYDOWN\n");
225 if (event.key.keysym.sym == SDLK_F1)
228 //Not sure that this is the right way to handle this...
229 //Probably should only give this to the top level window...
230 // for(i=windowList.begin(); i!=windowList.end(); i++)
231 // (*i)->HandleKey(event.key.keysym.sym);
232 windowList.back()->HandleKey(event.key.keysym.scancode);
234 else if (event.type == SDL_MOUSEMOTION)
236 #ifdef DEBUG_MAIN_LOOP
237 WriteLog(" -- SDL_MOUSEMOTION\n");
239 //This is for tracking a custom mouse cursor, which we're not doing--YET.
240 oldMouse.x = mouse.x, oldMouse.y = mouse.y;
241 mouse.x = event.motion.x, mouse.y = event.motion.y;
243 //Not sure that this is the right way to handle this...
244 //Right now, we should probably only do mouseover for the last item in the list...
246 //Though, it seems to screw other things up. Maybe it IS better to pass it to all windows?
247 //Or maybe to just the ones that aren't completely obscured?
248 //Probably. Right now, a disk's close button that should be obscured by one sitting on
249 //top of it gets redrawn. Not good. !!! FIX !!!
250 for(i=windowList.begin(); i!=windowList.end(); i++)
251 (*i)->HandleMouseMove(mouse.x, mouse.y);
252 // windowList.back()->HandleMouseMove(mouse.x, mouse.y);
254 else if (event.type == SDL_MOUSEBUTTONDOWN)
256 #ifdef DEBUG_MAIN_LOOP
257 WriteLog(" -- SDL_MOUSEBUTTONDOWN\n");
259 //Not sure that this is the right way to handle this...
260 // What we should do here is ensure that whatever has been clicked on gets moved to the
261 // highest priority--in our current data schema that would be the end of the list... !!! FIX !!!
266 We could do the following:
268 - Go through list and find which window has been clicked on (if any). If more
269 than one is clicked on, take the one highest in the Z order (closer to the end
272 - If item is highest in Z order, pass click through to window and exit.
274 - Otherwise, restore backing store on each window in reverse order.
276 - Remove item clicked on from the list. Put removed item at the end of the list.
278 - Go through list and pass click through to each window in the list. Also do a
279 blit to backing store and a Draw() for each window.
281 Could also do a check (if not clicked on highest Z window) to see which windows
282 it overlaps and just do restore/redraw for those that overlap. To wit:
284 - Create new list containing only those windows that overlap the clicking on window.
286 - Go through list and do a blit to backing store and a Draw() for each window.
288 - Go through list and pass click through to each window in the list.
294 for(i=windowList.begin(); i!=windowList.end(); i++)
295 (*i)->HandleMouseButton(event.button.x, event.button.y, true);
297 // We use the 1st algorithm here, since it's simpler. If we need to, we can optimize
300 // Walk backward through the list and see if a window was hit.
301 // This will automagically return us the window with the highest Z.
303 std::list<Element *>::reverse_iterator ri;
304 std::list<Element *>::iterator hit;// = windowList.end();
306 for(ri=windowList.rbegin(); ri!=windowList.rend(); ri++)
308 if ((*ri)->Inside(event.button.x, event.button.y))
310 // Here's a bit of STL weirdness: Converting from a reverse
311 // iterator to a regular iterator requires backing the iterator
312 // up a position after grabbing it's base() OR going forward
313 // one position with the reverse iterator before grabbing base().
314 // Ugly, but it gets the job done...
316 // Put it back where we found it, so the tests following this
323 // If we hit the highest in the list, then pass the event through
324 // to the window for handling. if we hit no windows, then pass the
325 // event to all windows. Otherwise, we need to shuffle windows.
327 //NOTE: We need to pass the click to all windows regardless of whether they're topmost or not...
328 if (ri == windowList.rbegin())
330 for(i=windowList.begin(); i!=windowList.end(); i++)
331 (*i)->HandleMouseButton(event.button.x, event.button.y, true);
333 else if (ri == windowList.rend())
335 for(i=windowList.begin(); i!=windowList.end(); i++)
336 (*i)->HandleMouseButton(event.button.x, event.button.y, true);
340 // - Otherwise, restore backing store on each window in reverse order.
341 for(ri=windowList.rbegin(); ri!=windowList.rend(); ri++)
342 (*ri)->RestoreScreenFromBackstore();
343 // At this point, the screen has been restored...
345 // - Remove item clicked on from the list. Put removed item at the end of the list.
346 windowList.push_back(*hit);
347 windowList.erase(hit);
348 // - Go through list and pass click through to each window in the list. Also do a
349 // blit to backing store and a Draw() for each window.
350 for(i=windowList.begin(); i!= windowList.end(); i++)
352 // Grab bg into backstore
353 (*i)->SaveScreenToBackstore();
355 (*i)->HandleMouseButton(event.button.x, event.button.y, true);
363 A slightly different way to handle this would be to loop through all windows, compare
364 all those above it to see if they obscure it; if so then subdivide it's update rectangle
365 to eliminate drawing the parts that aren't shown. The beauty of this approach is that
366 you don't have to care what order the windows are drawn in and you don't need to worry
367 about the order of restoring the backing store.
369 You *do* still need to determine the Z-order of the windows, in order to get the subdivisions
370 correct, but that's not too terrible.
372 Also, when doing a window drag, the coverage lists for all windows have to be regenerated.
374 std::list<Element *>::reverse_iterator ri;
375 bool movedWindow = false;
377 for(ri=windowList.rbegin(); ri!=windowList.rend(); ri++)
379 if ((*ri)->Inside(event.button.x, event.button.y))
381 // Remove item clicked on from the list & put removed item at the
382 // end of the list, thus putting the window at the top of the Z
383 // order. But IFF window is not already topmost!
384 if (ri != windowList.rbegin())
386 windowList.push_back(*ri);
387 // Here's a bit of STL weirdness: Converting from a reverse
388 // iterator to a regular iterator requires backing the iterator
389 // up a position after grabbing it's base() OR going forward
390 // one position with the reverse iterator before grabbing base().
391 // Ugly, but it get the job done...
392 windowList.erase((++ri).base());
400 //Small problem here: we should only pass the *hit* to the topmost window and pass
401 //*misses* to everyone else... Otherwise, you can have overlapping draggable windows
402 //and be able to drag both by clicking on a point that intersects both...
403 //(though that may be an interesting way to handle things!)
404 //The thing is that you want to do it on purpose (like with a special grouping widget)
405 //instead of by accident. So, !!! FIX !!!
406 // Pass the click on to all windows
407 // for(i=windowList.begin(); i!=windowList.end(); i++)
408 // (*i)->HandleMouseButton(event.button.x, event.button.y, true);
409 windowList.back()->HandleMouseButton(event.button.x, event.button.y, true);
411 // // & bail if nothing changed...
415 // Check for overlap/build coverage lists [O((n^2)/2) algorithm!]
416 //One way to optimize this would be to only reset coverage lists from the point in
417 //the Z order where the previous window was.
418 for(i=windowList.begin(); i!=windowList.end(); i++)
420 //One other little quirk: Probably need to clear the backing store as well!
422 (*i)->ResetCoverageList();
424 // This looks odd, but it's just a consequence of iterator weirdness.
425 // Otherwise we could just stick a j+1 in the for loop below. :-P
426 std::list<Element *>::iterator j = i;
429 for(; j!=windowList.end(); j++)
430 (*i)->AdjustCoverageList((*j)->GetExtents());
432 // (*i)->HandleMouseButton(event.button.x, event.button.y, true);
437 else if (event.type == SDL_MOUSEBUTTONUP)
439 #ifdef DEBUG_MAIN_LOOP
440 WriteLog(" -- SDL_MOUSEBUTTONUP\n");
442 //Not sure that this is the right way to handle this...
443 for(i=windowList.begin(); i!=windowList.end(); i++)
444 (*i)->HandleMouseButton(event.button.x, event.button.y, false);
445 //I think we should only do topmost here...
447 // windowList.back()->HandleMouseButton(event.button.x, event.button.y, false);
449 #ifdef DEBUG_MAIN_LOOP
451 WriteLog(" -- Unknown event\n");
454 if (Element::ScreenNeedsRefreshing())
456 #ifndef USE_NEW_MAINBUFFERING
457 #ifdef DEBUG_MAIN_LOOP
458 WriteLog("Screen refresh called!\n");
460 RenderScreenBuffer();
461 Element::ScreenWasRefreshed();
464 Element::ScreenWasRefreshed();
468 //hm. Works, but slows things way down.
469 //Now we use WaitEvents() instead. Yay!
473 // Not sure what to do for this in SDL 2...
474 // SDL_EnableKeyRepeat(0, 0);
487 // NEW GUI STARTS HERE
491 // Okay, this is ugly but works and I can't think of any better way to handle
492 // this. So what we do when we pass the GIMP bitmaps into a function is pass
493 // them as a (void *) and then cast them as type (Bitmap *) in order to use
494 // them. Yes, it's ugly. Come up with something better!
499 unsigned int bytesPerPixel; // 3:RGB, 4:RGBA
500 unsigned char pixelData[];
504 // Icons, in GIMP "C" format
505 #include "gfx/icon-selection.c"
506 #include "gfx/disk-icon.c"
507 #include "gfx/power-off-icon.c"
508 #include "gfx/power-on-icon.c"
509 #include "gfx/disk-swap-icon.c"
510 #include "gfx/disk-door-open.c"
511 #include "gfx/disk-door-closed.c"
512 #include "gfx/save-state-icon.c"
513 #include "gfx/load-state-icon.c"
514 #include "gfx/config-icon.c"
517 const char numeralOne[(7 * 7) + 1] =
526 const char numeralTwo[(7 * 7) + 1] =
535 const char ejectIcon[(8 * 7) + 1] =
544 const char driveLight[(5 * 5) + 1] =
552 enum { SBS_SHOWING, SBS_HIDING, SBS_SHOWN, SBS_HIDDEN };
555 SDL_Texture * GUI2::overlay = NULL;
556 SDL_Rect GUI2::olDst;
557 int GUI2::sidebarState = SBS_HIDDEN;
558 int32_t GUI2::dx = 0;
559 int32_t GUI2::iconSelected = -1;
560 bool GUI2::hasKeyboardFocus = false;
561 int32_t lastIconSelected = -1;
562 SDL_Texture * iconSelection = NULL;
563 SDL_Texture * diskIcon = NULL;
564 SDL_Texture * disk1Icon = NULL;
565 SDL_Texture * disk2Icon = NULL;
566 SDL_Texture * powerOnIcon = NULL;
567 SDL_Texture * powerOffIcon = NULL;
568 SDL_Texture * diskSwapIcon = NULL;
569 SDL_Texture * stateSaveIcon = NULL;
570 SDL_Texture * stateLoadIcon = NULL;
571 SDL_Texture * configIcon = NULL;
572 SDL_Texture * doorOpen = NULL;
573 SDL_Texture * doorClosed = NULL;
574 uint32_t texturePointer[128 * 380];
575 const char iconHelp[7][80] = { "Turn emulated Apple off/on",
576 "Insert floppy image into drive #1", "Insert floppy image into drive #2",
577 "Swap disks", "Save emulator state", "Load emulator state",
578 "Configure Apple2" };
580 #define SIDEBAR_X_POS (VIRTUAL_SCREEN_WIDTH - 80)
593 void GUI2::Init(SDL_Renderer * renderer)
595 overlay = SDL_CreateTexture(renderer, SDL_PIXELFORMAT_ABGR8888,
596 SDL_TEXTUREACCESS_TARGET, 128, 380);
600 WriteLog("GUI: Could not create overlay!\n");
604 if (SDL_SetTextureBlendMode(overlay, SDL_BLENDMODE_BLEND) == -1)
605 WriteLog("GUI: Could not set blend mode for overlay.\n");
607 for(uint32_t i=0; i<128*380; i++)
608 texturePointer[i] = 0xB0A000A0;
610 SDL_UpdateTexture(overlay, NULL, texturePointer, 128 * sizeof(Uint32));
612 olDst.x = VIRTUAL_SCREEN_WIDTH;
617 iconSelection = CreateTexture(renderer, &icon_selection);
618 diskIcon = CreateTexture(renderer, &disk_icon);
619 doorOpen = CreateTexture(renderer, &door_open);
620 doorClosed = CreateTexture(renderer, &door_closed);
621 disk1Icon = CreateTexture(renderer, &disk_icon);
622 disk2Icon = CreateTexture(renderer, &disk_icon);
623 powerOffIcon = CreateTexture(renderer, &power_off);
624 powerOnIcon = CreateTexture(renderer, &power_on);
625 diskSwapIcon = CreateTexture(renderer, &disk_swap);
626 stateSaveIcon = CreateTexture(renderer, &save_state);
627 stateLoadIcon = CreateTexture(renderer, &load_state);
628 configIcon = CreateTexture(renderer, &config);
630 // Set up drive icons in their current states
631 // AssembleDriveIcon(renderer, 0);
632 // AssembleDriveIcon(renderer, 1);
634 if (SDL_SetRenderTarget(renderer, overlay) < 0)
636 WriteLog("GUI: Could not set Render Target to overlay... (%s)\n", SDL_GetError());
640 DrawSidebarIcons(renderer);
641 // Set render target back to default
642 SDL_SetRenderTarget(renderer, NULL);
645 DiskSelector::Init(renderer);
646 DiskSelector::showWindow = true;
648 WriteLog("GUI: Successfully initialized.\n");
652 SDL_Texture * GUI2::CreateTexture(SDL_Renderer * renderer, const void * source)
654 Bitmap * bitmap = (Bitmap *)source;
655 SDL_Texture * texture = SDL_CreateTexture(renderer, SDL_PIXELFORMAT_ABGR8888,
656 // SDL_TEXTUREACCESS_STATIC, bitmap->width, bitmap->height);
657 SDL_TEXTUREACCESS_TARGET, bitmap->width, bitmap->height);
658 SDL_SetTextureBlendMode(texture, SDL_BLENDMODE_BLEND);
659 SDL_UpdateTexture(texture, NULL, (Uint32 *)bitmap->pixelData,
660 bitmap->width * sizeof(Uint32));
666 void GUI2::MouseDown(int32_t x, int32_t y, uint32_t buttons)
671 void GUI2::MouseUp(int32_t x, int32_t y, uint32_t buttons)
676 void GUI2::MouseMove(int32_t x, int32_t y, uint32_t buttons)
678 if (sidebarState != SBS_SHOWN)
682 if (x > SIDEBAR_X_POS)
684 //printf("GUI: sidebar showing (x = %i)...\n", x);
685 sidebarState = SBS_SHOWING;
690 //printf("GUI: sidebar hiding[1] (x = %i)...\n", x);
691 sidebarState = SBS_HIDING;
697 if (x < SIDEBAR_X_POS)
699 iconSelected = lastIconSelected = -1;
700 HandleIconSelection(sdlRenderer);
701 //printf("GUI: sidebar hiding[2] (x = %i)...\n", x);
702 sidebarState = SBS_HIDING;
705 // We're in the right zone, and the sidebar is shown, so let's select
709 if (y < 4 || y > 383)
714 iconSelected = (y - 4) / 54;
716 if (iconSelected != lastIconSelected)
718 HandleIconSelection(sdlRenderer);
719 lastIconSelected = iconSelected;
720 SpawnMessage("%s", iconHelp[iconSelected]);
722 // Show what's in the selected drive
723 if (iconSelected >= 1 && iconSelected <= 2)
725 if (!floppyDrive.IsEmpty(iconSelected - 1))
726 SpawnMessage("\"%s\"", floppyDrive.ImageName(iconSelected - 1));
734 void GUI2::HandleIconSelection(SDL_Renderer * renderer)
736 // Set up drive icons in their current states
737 AssembleDriveIcon(renderer, 0);
738 AssembleDriveIcon(renderer, 1);
740 // Reload the background...
741 SDL_UpdateTexture(overlay, NULL, texturePointer, 128 * sizeof(Uint32));
743 if (SDL_SetRenderTarget(renderer, overlay) < 0)
745 WriteLog("GUI: Could not set Render Target to overlay... (%s)\n", SDL_GetError());
749 // Draw the icon selector, if an icon is selected
750 if (iconSelected >= 0)
752 SDL_Rect dst;// = { 54, 54, 24 - 7, 2 };
753 dst.w = dst.h = 54, dst.x = 24 - 7, dst.y = 2 + (iconSelected * 54);
754 SDL_RenderCopy(renderer, iconSelection, NULL, &dst);
757 DrawSidebarIcons(renderer);
759 // Set render target back to default
760 SDL_SetRenderTarget(renderer, NULL);
764 void GUI2::AssembleDriveIcon(SDL_Renderer * renderer, int driveNumber)
766 SDL_Texture * drive[2] = { disk1Icon, disk2Icon };
767 const char * number[2] = { numeralOne, numeralTwo };
769 if (SDL_SetRenderTarget(renderer, drive[driveNumber]) < 0)
771 WriteLog("GUI: Could not set Render Target to overlay... (%s)\n", SDL_GetError());
775 SDL_RenderClear(renderer);
776 SDL_RenderCopy(renderer, diskIcon, NULL, NULL);
778 // Drive door @ (16, 7)
780 dst.w = 8, dst.h = 10, dst.x = 16, dst.y = 7;
781 SDL_RenderCopy(renderer, (floppyDrive.IsEmpty(driveNumber) ?
782 doorOpen : doorClosed), NULL, &dst);
784 // Numeral @ (30, 20)
785 DrawCharArray(renderer, number[driveNumber], 30, 20, 7, 7, 0xD0, 0xE0, 0xF0);
786 DrawDriveLight(renderer, driveNumber);
787 DrawEjectButton(renderer, driveNumber);
789 // Set render target back to default
790 SDL_SetRenderTarget(renderer, NULL);
794 void GUI2::DrawEjectButton(SDL_Renderer * renderer, int driveNumber)
796 if (floppyDrive.IsEmpty(driveNumber))
799 DrawCharArray(renderer, ejectIcon, 29, 31, 8, 7, 0x00, 0xAA, 0x00);
803 void GUI2::DrawDriveLight(SDL_Renderer * renderer, int driveNumber)
805 int lightState = floppyDrive.DriveLightStatus(driveNumber);
806 int r = 0x77, g = 0x00, b = 0x00;
808 if (lightState == DLS_READ)
809 r = 0x20, g = 0xFF, b = 0x20;
810 else if (lightState == DLS_WRITE)
811 r = 0xFF, g = 0x30, b = 0x30;
813 // Drive light @ (8, 21)
814 DrawCharArray(renderer, driveLight, 8, 21, 5, 5, r, g, b);
818 void GUI2::DrawCharArray(SDL_Renderer * renderer, const char * array, int x,
819 int y, int w, int h, int r, int g, int b)
821 SDL_SetRenderDrawColor(renderer, r, g, b, 0xFF);
823 for(int j=0; j<h; j++)
825 for(int i=0; i<w; i++)
827 if (array[(j * w) + i] != ' ')
828 SDL_RenderDrawPoint(renderer, x + i, y + j);
832 SDL_SetRenderDrawColor(renderer, 0x00, 0x00, 0x00, 0x00);
836 void GUI2::HandleGUIState(void)
840 if (olDst.x < SIDEBAR_X_POS && sidebarState == SBS_SHOWING)
842 olDst.x = SIDEBAR_X_POS;
843 sidebarState = SBS_SHOWN;
846 else if (olDst.x > VIRTUAL_SCREEN_WIDTH && sidebarState == SBS_HIDING)
848 olDst.x = VIRTUAL_SCREEN_WIDTH;
849 sidebarState = SBS_HIDDEN;
855 void GUI2::DrawSidebarIcons(SDL_Renderer * renderer)
857 SDL_Texture * icons[7] = { powerOnIcon, disk1Icon, disk2Icon, diskSwapIcon,
858 stateSaveIcon, stateLoadIcon, configIcon };
861 dst.w = dst.h = 40, dst.x = 24, dst.y = 2 + 7;
863 for(int i=0; i<7; i++)
865 SDL_RenderCopy(renderer, icons[i], NULL, &dst);
871 void GUI2::Render(SDL_Renderer * renderer)
878 if (sidebarState != SBS_HIDDEN)
879 HandleIconSelection(renderer);
881 SDL_RenderCopy(renderer, overlay, NULL, &olDst);
884 DiskSelector::Render(renderer);
893 cut into 7 pieces give ~54 pix per piece
894 So, let's try 40x40 icons, and see if that's good enough...
897 drive proportions: 1.62 : 1
910 | ^| <-- eject button
931 maybe state save/load