]> code.delx.au - refind/blobdiff - refind/menu.c
Improved submenu and information menu appearance over a full-screen
[refind] / refind / menu.c
index 7de9d8bc3b9d462a0d590984cd00bc9ef7a61287..39c9020141903f5ff40e073026ef47db6275e205 100644 (file)
@@ -48,6 +48,7 @@
 #include "menu.h"
 #include "config.h"
 #include "libeg.h"
+#include "libegint.h"
 #include "../include/refit_call_wrapper.h"
 
 #include "../include/egemb_back_selected_small.h"
@@ -68,6 +69,7 @@ typedef VOID (*MENU_STYLE_FUNC)(IN REFIT_MENU_SCREEN *Screen, IN SCROLL_STATE *S
 static CHAR16 ArrowUp[2] = { ARROW_UP, 0 };
 static CHAR16 ArrowDown[2] = { ARROW_DOWN, 0 };
 
+// Text and icon spacing constants....
 #define TEXT_YMARGIN (2)
 #define TEXT_XMARGIN (8)
 #define TEXT_LINE_HEIGHT (FONT_CELL_HEIGHT + TEXT_YMARGIN * 2)
@@ -82,9 +84,8 @@ static CHAR16 ArrowDown[2] = { ARROW_DOWN, 0 };
 #define ALIGN_RIGHT 1
 #define ALIGN_LEFT 0
 
-static EG_IMAGE *SelectionImages[4] = { NULL, NULL, NULL, NULL };
+static EG_IMAGE *SelectionImages[2] = { NULL, NULL };
 static EG_PIXEL SelectionBackgroundPixel = { 0xff, 0xff, 0xff, 0 };
-static EG_IMAGE *TextBuffer = NULL;
 
 //
 // Graphics helper functions
@@ -102,33 +103,33 @@ static VOID InitSelection(VOID)
 
     // load small selection image
     if (GlobalConfig.SelectionSmallFileName != NULL) {
-        SelectionImages[2] = egLoadImage(SelfDir, GlobalConfig.SelectionSmallFileName, FALSE);
+        SelectionImages[1] = egLoadImage(SelfDir, GlobalConfig.SelectionSmallFileName, TRUE);
     }
-    if (SelectionImages[2] == NULL)
-        SelectionImages[2] = egPrepareEmbeddedImage(&egemb_back_selected_small, FALSE);
-    SelectionImages[2] = egEnsureImageSize(SelectionImages[2],
+    if (SelectionImages[1] == NULL)
+        SelectionImages[1] = egPrepareEmbeddedImage(&egemb_back_selected_small, TRUE);
+    SelectionImages[1] = egEnsureImageSize(SelectionImages[1],
                                            ROW1_TILESIZE, ROW1_TILESIZE, &MenuBackgroundPixel);
-    if (SelectionImages[2] == NULL)
+    if (SelectionImages[1] == NULL)
         return;
 
     // load big selection image
     if (GlobalConfig.SelectionBigFileName != NULL) {
-        SelectionImages[0] = egLoadImage(SelfDir, GlobalConfig.SelectionBigFileName, FALSE);
+        SelectionImages[0] = egLoadImage(SelfDir, GlobalConfig.SelectionBigFileName, TRUE);
         SelectionImages[0] = egEnsureImageSize(SelectionImages[0],
                                                ROW0_TILESIZE, ROW0_TILESIZE, &MenuBackgroundPixel);
     }
     if (SelectionImages[0] == NULL) {
         // calculate big selection image from small one
 
-        SelectionImages[0] = egCreateImage(ROW0_TILESIZE, ROW0_TILESIZE, FALSE);
+        SelectionImages[0] = egCreateImage(ROW0_TILESIZE, ROW0_TILESIZE, TRUE);
         if (SelectionImages[0] == NULL) {
-            egFreeImage(SelectionImages[2]);
-            SelectionImages[2] = NULL;
+            egFreeImage(SelectionImages[1]);
+            SelectionImages[1] = NULL;
             return;
         }
 
         DestPtr = SelectionImages[0]->PixelData;
-        SrcPtr  = SelectionImages[2]->PixelData;
+        SrcPtr  = SelectionImages[1]->PixelData;
         for (y = 0; y < ROW0_TILESIZE; y++) {
             if (y < (ROW1_TILESIZE >> 1))
                 src_y = y;
@@ -149,11 +150,7 @@ static VOID InitSelection(VOID)
             }
         }
     }
-
-    // non-selected background images
-    SelectionImages[1] = egCreateFilledImage(ROW0_TILESIZE, ROW0_TILESIZE, FALSE, &MenuBackgroundPixel);
-    SelectionImages[3] = egCreateFilledImage(ROW1_TILESIZE, ROW1_TILESIZE, FALSE, &MenuBackgroundPixel);
-}
+} // VOID InitSelection()
 
 //
 // Scrolling functions
@@ -544,9 +541,6 @@ static VOID TextMenuStyle(IN REFIT_MENU_SCREEN *Screen, IN SCROLL_STATE *State,
                 MergeStrings(&DisplayStrings[i], Screen->Entries[i]->Title, 0);
                 if (StrLen(DisplayStrings[i]) > MenuWidth)
                    DisplayStrings[i][MenuWidth - 1] = 0;
-//                DisplayStrings[i] = AllocateZeroPool(256 * sizeof(CHAR16));
-//                SPrint(DisplayStrings[i], ((MenuWidth < 255) ? MenuWidth : 255) * sizeof(CHAR16),
-//                       L" %s ", Screen->Entries[i]->Title);
                 // TODO: use more elaborate techniques for shortening too long strings (ellipses in the middle)
                 // TODO: account for double-width characters
             } // for
@@ -641,70 +635,143 @@ static VOID TextMenuStyle(IN REFIT_MENU_SCREEN *Screen, IN SCROLL_STATE *State,
 //
 
 
-static VOID DrawMenuText(IN CHAR16 *Text, IN UINTN SelectedWidth, IN UINTN XPos, IN UINTN YPos)
+// Display a submenu
+static VOID DrawSubmenuText(IN CHAR16 *Text, IN BOOLEAN Selected, IN UINTN FieldWidth, IN UINTN XPos, IN UINTN YPos)
 {
-    if (TextBuffer == NULL)
-        TextBuffer = egCreateImage(LAYOUT_TEXT_WIDTH, TEXT_LINE_HEIGHT, FALSE);
-
-    egFillImage(TextBuffer, &MenuBackgroundPixel);
-    if (SelectedWidth > 0) {
-        // draw selection bar background
-        egFillImageArea(TextBuffer, 0, 0, SelectedWidth, TextBuffer->Height,
-                        &SelectionBackgroundPixel);
-    }
+//   UINTN TextWidth = TEXT_XMARGIN * 2 + StrLen(Text) * FONT_CELL_WIDTH;
+   EG_IMAGE *TextBuffer;
+
+   TextBuffer = egCreateImage(FieldWidth, TEXT_LINE_HEIGHT, FALSE);
+
+   egFillImage(TextBuffer, &MenuBackgroundPixel);
+   if (Selected) {
+       // draw selection bar background
+       egFillImageArea(TextBuffer, 0, 0, FieldWidth, TextBuffer->Height, &SelectionBackgroundPixel);
+   }
+
+   // render the text
+   egRenderText(Text, TextBuffer, TEXT_XMARGIN, TEXT_YMARGIN);
+   egDrawImageWithTransparency(TextBuffer, NULL, XPos, YPos, TextBuffer->Width, TextBuffer->Height);
+//    BltImage(TextBuffer, XPos, YPos);
+}
+
+static VOID DrawMainMenuText(IN CHAR16 *Text, IN UINTN XPos, IN UINTN YPos)
+{
+    UINTN TextWidth, TextPosX;
+    EG_IMAGE *TextBuffer;
+
+    TextBuffer = egCropImage(GlobalConfig.ScreenBackground, XPos, YPos, LAYOUT_TEXT_WIDTH, TEXT_LINE_HEIGHT);
 
     // render the text
-    egRenderText(Text, TextBuffer, TEXT_XMARGIN, TEXT_YMARGIN);
-    BltImage(TextBuffer, XPos, YPos);
+    egMeasureText(Text, &TextWidth, NULL);
+    if (TextWidth > TextBuffer->Width)
+       TextPosX = 0;
+    else
+       TextPosX = (TextBuffer->Width - TextWidth) / 2;
+    egRenderText(Text, TextBuffer, TextPosX, 0);
+    egDrawImageWithTransparency(TextBuffer, NULL, XPos, YPos, TextBuffer->Width, TextBuffer->Height);
 }
 
+// Compute the size & position of the window that will hold a subscreen's information.
+static VOID ComputeSubScreenWindowSize(REFIT_MENU_SCREEN *Screen, IN SCROLL_STATE *State, UINTN *XPos, UINTN *YPos, UINTN *Width, UINTN *Height, UINTN *LineWidth) {
+   UINTN i, ItemWidth, HintTop, BannerBottomEdge, TitleWidth, CenteredTop;
+
+   *Width = 1;
+   *Height = 5;
+   TitleWidth = StrLen(Screen->Title);
+   if ((Screen->TitleImage) && (TitleWidth > (Screen->TitleImage->Width / FONT_CELL_WIDTH))) {
+      TitleWidth -= (Screen->TitleImage->Width / FONT_CELL_WIDTH);
+   }
+   if (TitleWidth > *Width) {
+      *Width = TitleWidth;
+   }
+   for (i = 0; i < Screen->InfoLineCount; i++) {
+       ItemWidth = StrLen(Screen->InfoLines[i]);
+       if (*Width < ItemWidth) {
+           *Width = ItemWidth;
+       }
+       (*Height)++;
+   }
+   for (i = 0; i <= State->MaxIndex; i++) {
+       ItemWidth = StrLen(Screen->Entries[i]->Title);
+       if (*Width < ItemWidth) {
+           *Width = ItemWidth;
+       }
+       (*Height)++;
+   }
+   *Width = TEXT_XMARGIN * 2 + *Width * FONT_CELL_WIDTH;
+   *LineWidth = *Width;
+   if (Screen->TitleImage)
+      *Width += (Screen->TitleImage->Width + TITLEICON_SPACING + FONT_CELL_WIDTH);
+   else
+      *Width += FONT_CELL_WIDTH;
+
+   // Keep it within the bounds of the screen, or 2/3 of the screen's width
+   // for screens over 800 pixels wide
+   if (*Width > UGAWidth)
+      *Width = UGAWidth;
+   if ((*Width > (2 * UGAWidth) / 3) && (UGAWidth > 800))
+      *Width = (2 * UGAWidth) / 3;
+
+   *XPos = (UGAWidth - *Width) / 2;
+
+   HintTop = UGAHeight - (FONT_CELL_HEIGHT * 3); // top of hint text
+   *Height *= TEXT_LINE_HEIGHT;
+   if (Screen->TitleImage && (*Height < (Screen->TitleImage->Height + TEXT_LINE_HEIGHT * 3)))
+      *Height = Screen->TitleImage->Height + TEXT_LINE_HEIGHT * 3;
+
+   if (GlobalConfig.BannerBottomEdge >= HintTop) { // probably a full-screen image; treat it as an empty banner
+      BannerBottomEdge = 0;
+   } else {
+      BannerBottomEdge = GlobalConfig.BannerBottomEdge;
+   }
+   if (*Height > (HintTop -BannerBottomEdge - FONT_CELL_HEIGHT * 2)) {
+      BannerBottomEdge = 0;
+   }
+   if (*Height > (HintTop -BannerBottomEdge - FONT_CELL_HEIGHT * 2)) {
+      // TODO: Implement scrolling in text screen.
+      *Height = (HintTop - BannerBottomEdge - FONT_CELL_HEIGHT * 2);
+   }
+   *YPos = BannerBottomEdge + FONT_CELL_HEIGHT + (HintTop - BannerBottomEdge - *Height) / 2;
+   // Above often produces a text field that feels bottom-weighted, so adjust
+   // upward a bit, if possible....
+   CenteredTop = ((UGAHeight - *Height) / 2);
+   if ((*YPos > CenteredTop) && (CenteredTop > BannerBottomEdge))
+      *YPos = CenteredTop;
+} // VOID ComputeSubScreenWindowSize()
+
 // Displays sub-menus
 static VOID GraphicsMenuStyle(IN REFIT_MENU_SCREEN *Screen, IN SCROLL_STATE *State, IN UINTN Function, IN CHAR16 *ParamText)
 {
     INTN i;
     UINTN ItemWidth;
-    static UINTN MenuWidth, EntriesPosX, EntriesPosY, TimeoutPosY;
+    static UINTN LineWidth, MenuWidth, MenuHeight, EntriesPosX, EntriesPosY, TimeoutPosY;
+    EG_IMAGE *Window;
+    EG_PIXEL *BackgroundPixel = &(GlobalConfig.ScreenBackground->PixelData[0]);
 
     State->ScrollMode = SCROLL_MODE_TEXT;
     switch (Function) {
 
         case MENU_FUNCTION_INIT:
             InitScroll(State, Screen->EntryCount, 0);
-
-            // determine width of the menu
-            MenuWidth = 20;  // minimum
-            for (i = 0; i < (INTN)Screen->InfoLineCount; i++) {
-                ItemWidth = StrLen(Screen->InfoLines[i]);
-                if (MenuWidth < ItemWidth)
-                    MenuWidth = ItemWidth;
-            }
-            for (i = 0; i <= State->MaxIndex; i++) {
-                ItemWidth = StrLen(Screen->Entries[i]->Title);
-                if (MenuWidth < ItemWidth)
-                    MenuWidth = ItemWidth;
-            }
-            MenuWidth = TEXT_XMARGIN * 2 + MenuWidth * FONT_CELL_WIDTH;
-            if (MenuWidth > LAYOUT_TEXT_WIDTH)
-                MenuWidth = LAYOUT_TEXT_WIDTH;
-
-            if (Screen->TitleImage)
-                EntriesPosX = (UGAWidth + (Screen->TitleImage->Width + TITLEICON_SPACING) - MenuWidth) >> 1;
-            else
-                EntriesPosX = (UGAWidth - MenuWidth) >> 1;
-            EntriesPosY = ((UGAHeight - LAYOUT_TOTAL_HEIGHT) >> 1) + LAYOUT_BANNER_YOFFSET + TEXT_LINE_HEIGHT * 2;
+            ComputeSubScreenWindowSize(Screen, State, &EntriesPosX, &EntriesPosY, &MenuWidth, &MenuHeight, &LineWidth);
             TimeoutPosY = EntriesPosY + (Screen->EntryCount + 1) * TEXT_LINE_HEIGHT;
 
             // initial painting
             SwitchToGraphicsAndClear();
+            Window = egCreateFilledImage(MenuWidth, MenuHeight, FALSE, BackgroundPixel);
+            egDrawImage(Window, EntriesPosX, EntriesPosY);
             egMeasureText(Screen->Title, &ItemWidth, NULL);
-            DrawMenuText(Screen->Title, 0, ((UGAWidth - ItemWidth) >> 1) - TEXT_XMARGIN, EntriesPosY - TEXT_LINE_HEIGHT * 2);
-            if (Screen->TitleImage)
-                BltImageAlpha(Screen->TitleImage,
-                              EntriesPosX - (Screen->TitleImage->Width + TITLEICON_SPACING), EntriesPosY,
-                              &MenuBackgroundPixel);
+            DrawSubmenuText(Screen->Title, FALSE, (StrLen(Screen->Title) + 2) * FONT_CELL_WIDTH,
+                            EntriesPosX + (MenuWidth - ItemWidth) / 2, EntriesPosY += TEXT_LINE_HEIGHT);
+            if (Screen->TitleImage) {
+               BltImageAlpha(Screen->TitleImage, EntriesPosX, EntriesPosY + TEXT_LINE_HEIGHT * 2, BackgroundPixel);
+               EntriesPosX += (Screen->TitleImage->Width + TITLEICON_SPACING);
+            }
+            EntriesPosY += (TEXT_LINE_HEIGHT * 2);
             if (Screen->InfoLineCount > 0) {
                 for (i = 0; i < (INTN)Screen->InfoLineCount; i++) {
-                    DrawMenuText(Screen->InfoLines[i], 0, EntriesPosX, EntriesPosY);
+                    DrawSubmenuText(Screen->InfoLines[i], FALSE, LineWidth, EntriesPosX, EntriesPosY);
                     EntriesPosY += TEXT_LINE_HEIGHT;
                 }
                 EntriesPosY += TEXT_LINE_HEIGHT;  // also add a blank line
@@ -718,31 +785,27 @@ static VOID GraphicsMenuStyle(IN REFIT_MENU_SCREEN *Screen, IN SCROLL_STATE *Sta
 
         case MENU_FUNCTION_PAINT_ALL:
             for (i = 0; i <= State->MaxIndex; i++) {
-                DrawMenuText(Screen->Entries[i]->Title, (i == State->CurrentSelection) ? MenuWidth : 0,
-                             EntriesPosX, EntriesPosY + i * TEXT_LINE_HEIGHT);
+               DrawSubmenuText(Screen->Entries[i]->Title, (i == State->CurrentSelection), LineWidth,
+                               EntriesPosX, EntriesPosY + i * TEXT_LINE_HEIGHT);
             }
             if (!(GlobalConfig.HideUIFlags & HIDEUI_FLAG_HINTS)) {
-               if ((Screen->Hint1 != NULL) && (StrLen(Screen->Hint1) > 0)) {
-                  DrawMenuText(Screen->Hint1, 0, (UGAWidth - (StrLen(Screen->Hint1) * FONT_CELL_WIDTH)) / 2,
-                               UGAHeight - (FONT_CELL_HEIGHT * 3));
-               }
-               if ((Screen->Hint2 != NULL) && (StrLen(Screen->Hint2) > 0)) {
-                  DrawMenuText(Screen->Hint2, 0, (UGAWidth - (StrLen(Screen->Hint2) * FONT_CELL_WIDTH)) / 2,
-                               UGAHeight - (FONT_CELL_HEIGHT * 2));
-               } // if
+               if ((Screen->Hint1 != NULL) && (StrLen(Screen->Hint1) > 0))
+                  DrawMainMenuText(Screen->Hint1, (UGAWidth - LAYOUT_TEXT_WIDTH) / 2, UGAHeight - (FONT_CELL_HEIGHT * 3));
+               if ((Screen->Hint2 != NULL) && (StrLen(Screen->Hint2) > 0))
+                  DrawMainMenuText(Screen->Hint2, (UGAWidth - LAYOUT_TEXT_WIDTH) / 2, UGAHeight - (FONT_CELL_HEIGHT * 2));
             } // if
             break;
 
         case MENU_FUNCTION_PAINT_SELECTION:
             // redraw selection cursor
-            DrawMenuText(Screen->Entries[State->PreviousSelection]->Title, 0,
+            DrawSubmenuText(Screen->Entries[State->PreviousSelection]->Title, FALSE, LineWidth,
                          EntriesPosX, EntriesPosY + State->PreviousSelection * TEXT_LINE_HEIGHT);
-            DrawMenuText(Screen->Entries[State->CurrentSelection]->Title, MenuWidth,
+            DrawSubmenuText(Screen->Entries[State->CurrentSelection]->Title, TRUE, LineWidth,
                          EntriesPosX, EntriesPosY + State->CurrentSelection * TEXT_LINE_HEIGHT);
             break;
 
         case MENU_FUNCTION_PAINT_TIMEOUT:
-            DrawMenuText(ParamText, 0, EntriesPosX, TimeoutPosY);
+            DrawSubmenuText(ParamText, FALSE, LineWidth, EntriesPosX, TimeoutPosY);
             break;
 
     }
@@ -754,33 +817,20 @@ static VOID GraphicsMenuStyle(IN REFIT_MENU_SCREEN *Screen, IN SCROLL_STATE *Sta
 
 static VOID DrawMainMenuEntry(REFIT_MENU_ENTRY *Entry, BOOLEAN selected, UINTN XPos, UINTN YPos)
 {
-    UINTN ImageNum;
-
-    ImageNum = ((Entry->Row == 0) ? 0 : 2) + (selected ? 0 : 1);
-    if (SelectionImages != NULL)
-        BltImageCompositeBadge(SelectionImages[ImageNum],
-                               Entry->Image, Entry->BadgeImage, XPos, YPos);
-}
-
-static VOID DrawMainMenuText(IN CHAR16 *Text, IN UINTN XPos, IN UINTN YPos)
-{
-    UINTN TextWidth, TextPosX;
-
-    if (TextBuffer == NULL)
-        TextBuffer = egCreateImage(LAYOUT_TEXT_WIDTH, TEXT_LINE_HEIGHT, FALSE);
-
-    egFillImage(TextBuffer, &MenuBackgroundPixel);
-
-    // render the text
-    egMeasureText(Text, &TextWidth, NULL);
-    if (TextWidth > TextBuffer->Width)
-       TextPosX = 0;
-    else
-       TextPosX = (TextBuffer->Width - TextWidth) / 2;
-    egRenderText(Text, TextBuffer, TextPosX, 0);
-//    egRenderText(Text, TextBuffer, (TextBuffer->Width - TextWidth) >> 1, 0);
-    BltImage(TextBuffer, XPos, YPos);
-}
+   EG_IMAGE *Background;
+
+   if (SelectionImages != NULL) {
+      if (selected) {
+         Background = egCropImage(GlobalConfig.ScreenBackground, XPos, YPos,
+                                  SelectionImages[Entry->Row]->Width, SelectionImages[Entry->Row]->Height);
+         egComposeImage(Background, SelectionImages[Entry->Row], 0, 0);
+         BltImageCompositeBadge(Background, Entry->Image, Entry->BadgeImage, XPos, YPos);
+      } else { // Image not selected; copy background
+         egDrawImageWithTransparency(Entry->Image, Entry->BadgeImage, XPos, YPos,
+                                     SelectionImages[Entry->Row]->Width, SelectionImages[Entry->Row]->Height);
+      } // if/else
+   } // if
+} // VOID DrawMainMenuEntry()
 
 static VOID PaintAll(IN REFIT_MENU_SCREEN *Screen, IN SCROLL_STATE *State, UINTN *itemPosX,
                      UINTN row0PosY, UINTN row1PosY, UINTN textPosY) {
@@ -855,10 +905,50 @@ static VOID PaintIcon(IN EG_EMBEDDED_IMAGE *BuiltInIcon, IN CHAR16 *ExternalFile
    if (Icon != NULL) {
       if (Alignment == ALIGN_RIGHT)
          PosX -= Icon->Width;
-      BltImageAlpha(Icon, PosX, PosY - (Icon->Height / 2), &MenuBackgroundPixel);
+      egDrawImageWithTransparency(Icon, NULL, PosX, PosY - (Icon->Height / 2), Icon->Width, Icon->Height);
    }
 } // static VOID PaintIcon()
 
+inline UINTN ComputeRow0PosX(VOID) {
+//   return ((UGAHeight / 2) - (5 * ROW0_TILESIZE / 6));
+   return ((UGAHeight / 2) - ROW0_TILESIZE / 2);
+} // UINTN ComputeRow0PosX()
+
+// Display (or erase) the arrow icons to the left and right of an icon's row,
+// as appropriate.
+static VOID PaintArrows(SCROLL_STATE *State, UINTN PosX, UINTN PosY, UINTN row0Loaders) {
+   CHAR16 FileName[256];
+   EG_IMAGE *TempImage;
+   UINTN Width, Height, RightX, AdjPosY;
+
+   // NOTE: Assume that left and right arrows are of the same size....
+   Width = egemb_arrow_left.Width;
+   Height = egemb_arrow_left.Height;
+   RightX = (UGAWidth + (ROW0_TILESIZE + TILE_XSPACING) * State->MaxVisible) / 2 + TILE_XSPACING;
+   AdjPosY = PosY - (Height / 2);
+
+   // For PaintIcon() calls, the starting Y position is moved to the midpoint
+   // of the surrounding row; PaintIcon() adjusts this back up by half the
+   // icon's height to properly center it.
+   if ((State->FirstVisible > 0) && (!(GlobalConfig.HideUIFlags & HIDEUI_FLAG_ARROWS))) {
+      SPrint(FileName, 255, L"%s\\arrow_left.icns", GlobalConfig.IconsDir ? GlobalConfig.IconsDir : DEFAULT_ICONS_DIR);
+      PaintIcon(&egemb_arrow_left, FileName, PosX, PosY, ALIGN_RIGHT);
+   } else {
+      TempImage = egCropImage(GlobalConfig.ScreenBackground, PosX - Width, AdjPosY, Width, Height);
+      BltImage(TempImage, PosX - Width, AdjPosY);
+      egFreeImage(TempImage);
+   } // if/else
+
+   if ((State->LastVisible < (row0Loaders - 1)) && (!(GlobalConfig.HideUIFlags & HIDEUI_FLAG_ARROWS))) {
+      SPrint(FileName, 255, L"%s\\arrow_right.icns", GlobalConfig.IconsDir ? GlobalConfig.IconsDir : DEFAULT_ICONS_DIR);
+      PaintIcon(&egemb_arrow_right, FileName, RightX, PosY, ALIGN_LEFT);
+   } else {
+      TempImage = egCropImage(GlobalConfig.ScreenBackground, RightX, AdjPosY, Width, Height);
+      BltImage(TempImage, RightX, AdjPosY);
+      egFreeImage(TempImage);
+   } // if/else
+} // VOID PaintArrows()
+
 // Display main menu in graphics mode
 VOID MainMenuStyle(IN REFIT_MENU_SCREEN *Screen, IN SCROLL_STATE *State, IN UINTN Function, IN CHAR16 *ParamText)
 {
@@ -867,7 +957,6 @@ VOID MainMenuStyle(IN REFIT_MENU_SCREEN *Screen, IN SCROLL_STATE *State, IN UINT
     UINTN row0Count, row1Count, row1PosX, row1PosXRunning;
     static UINTN *itemPosX;
     static UINTN row0PosY, textPosY;
-    CHAR16 FileName[256];
 
     State->ScrollMode = SCROLL_MODE_ICONS;
     switch (Function) {
@@ -889,7 +978,7 @@ VOID MainMenuStyle(IN REFIT_MENU_SCREEN *Screen, IN SCROLL_STATE *State, IN UINT
                }
             }
             row0PosX = (UGAWidth + TILE_XSPACING - (ROW0_TILESIZE + TILE_XSPACING) * row0Count) >> 1;
-            row0PosY = ((UGAHeight - LAYOUT_TOTAL_HEIGHT) >> 1) + LAYOUT_BANNER_YOFFSET;
+            row0PosY = ComputeRow0PosX();
             row1PosX = (UGAWidth + TILE_XSPACING - (ROW1_TILESIZE + TILE_XSPACING) * row1Count) >> 1;
             row1PosY = row0PosY + ROW0_TILESIZE + TILE_YSPACING;
             if (row1Count > 0)
@@ -919,22 +1008,11 @@ VOID MainMenuStyle(IN REFIT_MENU_SCREEN *Screen, IN SCROLL_STATE *State, IN UINT
             break;
 
         case MENU_FUNCTION_PAINT_ALL:
-            BltClearScreen(TRUE);
             PaintAll(Screen, State, itemPosX, row0PosY, row1PosY, textPosY);
-            // For PaintIcon() calls, the starting Y position is moved to the midpoint
+            // For PaintArrows(), the starting Y position is moved to the midpoint
             // of the surrounding row; PaintIcon() adjusts this back up by half the
             // icon's height to properly center it.
-            if ((State->FirstVisible > 0) && (!(GlobalConfig.HideUIFlags & HIDEUI_FLAG_ARROWS))) {
-               SPrint(FileName, 255, L"%s\\arrow_left.icns", GlobalConfig.IconsDir ? GlobalConfig.IconsDir : DEFAULT_ICONS_DIR);
-               PaintIcon(&egemb_arrow_left, FileName, row0PosX - TILE_XSPACING,
-                         row0PosY + (ROW0_TILESIZE / 2), ALIGN_RIGHT);
-            } // if
-            if ((State->LastVisible < (row0Loaders - 1)) && (!(GlobalConfig.HideUIFlags & HIDEUI_FLAG_ARROWS))) {
-               SPrint(FileName, 255, L"%s\\arrow_right.icns", GlobalConfig.IconsDir ? GlobalConfig.IconsDir : DEFAULT_ICONS_DIR);
-               PaintIcon(&egemb_arrow_right, FileName,
-                         (UGAWidth + (ROW0_TILESIZE + TILE_XSPACING) * State->MaxVisible) / 2 + TILE_XSPACING,
-                         row0PosY + (ROW0_TILESIZE / 2), ALIGN_LEFT);
-            } // if
+            PaintArrows(State, row0PosX - TILE_XSPACING, row0PosY + (ROW0_TILESIZE / 2), row0Loaders);
             break;
 
         case MENU_FUNCTION_PAINT_SELECTION: