MSGBOX Library (clone of VBA's MsgBox function)
Posted: Sat Aug 25, 2018 1:11 am
				
				Overview
This library is a built-from-scratch reproduction of VBA's MsgBox function, which is used for displaying messages/prompts to users in Microsoft Office. (See an overview of the VBA version here.)
MSGBOX lets you quickly and easily display a windowed message and 1 to 3 buttons for soliciting a user response to the message. The function returns an enumerated value that lets you determine what button was pressed. You can also choose to display an icon or image to display in the box, and you can customize pretty much anything about the message box's appearance to fit your program's style. Check out the demo script included with the code for lots of examples.
Here is a simple example prompting the user to decide if they want to save before quitting:

Functions provided:
Inputs and Customization
Required inputs:
Other parameters:
In addition to the above inputs, there are many other parameters that can be set before calling the MSGBOX function that controls the appearance of the message box. You can change the color and alpha of anything, including the background behind the message box, and you can also control the duration of the fade-in animation. A list of parameters is given in the "msgbox" file (see the header info at the top of the file), and you can also find many examples in the demo script included with the code.
To give you an example, here are two clean, semi-transparent themes: one light and one dark:


Code
PLEASE READ THE BELOW INFO ON SETTING UP THE LIBRARY!
All code can be found here: https://www.dropbox.com/sh/udk7bhjic863 ... lq_ra?dl=0
IMPORTANT: The location of the msgbox_icons folder must be hardcoded. You must update the variable MSGBOX.dir$ at the top of the "msgbox" file with wherever the library is located. (If anyone knows of a better way to do this, please let me know!)
The MSGBOX library depends on a few other libraries I have written. Those libraries are:
			This library is a built-from-scratch reproduction of VBA's MsgBox function, which is used for displaying messages/prompts to users in Microsoft Office. (See an overview of the VBA version here.)
MSGBOX lets you quickly and easily display a windowed message and 1 to 3 buttons for soliciting a user response to the message. The function returns an enumerated value that lets you determine what button was pressed. You can also choose to display an icon or image to display in the box, and you can customize pretty much anything about the message box's appearance to fit your program's style. Check out the demo script included with the code for lots of examples.
Here is a simple example prompting the user to decide if they want to save before quitting:
Code: Select all
{{/lib/interface/msgbox}}
SET TOOLBAR OFF
GRAPHICS CLEAR
GRAPHICS
text$ = "You have unsaved changes. Would you like to save before quitting?"
response = MSGBOX(text$, MSGBOX.YesNo, MSGBOX.Exclamation)
IF response = MSGBOX.Yes THEN
  ' code to save
END IF
Functions provided:
- MSGBOX (text$, type, icon)
 - MSGBOX_RESTORE_PARAM_DEFAULTS ()
 
Inputs and Customization
Required inputs:
- text$ — This is a string of the message you want to be displayed in the message box. If can include linefeeds using CHR$(10) and can be any length. The text is put into a field, so it is automatically scrollable if the message is very long.
 - type — This is an enumerated value telling the library what type of message box you want (full list below). This determines what buttons are presented to the user for their response.
 - icon — This is an enumerated value telling the library what icon to display in the message box, if any (full list below).
 
- MSGBOX.OkOnly = 0 . . . . . . . . . one button: "Ok"
 - MSGBOX.OkCancel = 1 . . . . . . . . two buttons: "Ok" and "Cancel"
 - MSGBOX.YesNo = 2 . . . . . . . . . . two buttons: "Yes" and "No"
 - MSGBOX.YesNoCancel = 3 . . . . . . three buttons: "Yes", "No", and "Cancel"
 - MSGBOX.RetryCancel = 4 . . . . . . two buttons: "Retry" and "Cancel"
 - MSGBOX.AbortRetryIgnore = 5 . . . three buttons: "Abort", "Retry", and "Ignore"
 - MSGBOX.CustomBttns1 = 6 . . . . . one button: uses parameter MSGBOX.bttn1$
 - MSGBOX.CustomBttns2 = 7 . . . . . two buttons: uses parameters MSGBOX.bttn1$ and .bttn2$
 - MSGBOX.CustomBttns3 = 8 . . . . . three buttons: uses parameters MSGBOX.bttn1$, .bttn2$, and .bttn3$
 
- MSGBOX.NoIcon = 0 . . . . . . . no icon (text fills the entire message box)
 - MSGBOX.Critical = 1 . . . . . . . red "X" in circle
 - MSGBOX.Exclamation = 2 . . . . yellow "!" in triangle
 - MSGBOX.Question = 3 . . . . . . blue "?" in circle
 - MSGBOX.Information = 4 . . . . blue "i" in circle
 - MSGBOX.CustomImage = 5 . . . uses parameter MSGBOX.iconImagePath$ (path of image file to use)
 - MSGBOX.CustomAscii = 6 . . . . uses parameter MSGBOX.iconAsciiNum (decimal value of unicode character to use)
 
- MSGBOX.Ok = 0
 - MSGBOX.Cancel = 1
 - MSGBOX.Yes = 2
 - MSGBOX.No = 3
 - MSGBOX.Abort = 4
 - MSGBOX.Retry = 5
 - MSGBOX.Ignore = 6
 - MSGBOX.Button1 = 7 (for custom MSGBOX types)
 - MSGBOX.Button2 = 8 (for custom MSGBOX types)
 - MSGBOX.Button3 = 9 (for custom MSGBOX types)
 
Code: Select all
' This line of code:
response = MSGBOX("Test", MSGBOX.YesNo, MSGBOX.Question)
' is equivalent to this line of code:
response = MSGBOX("Test", 2, 3)In addition to the above inputs, there are many other parameters that can be set before calling the MSGBOX function that controls the appearance of the message box. You can change the color and alpha of anything, including the background behind the message box, and you can also control the duration of the fade-in animation. A list of parameters is given in the "msgbox" file (see the header info at the top of the file), and you can also find many examples in the demo script included with the code.
To give you an example, here are two clean, semi-transparent themes: one light and one dark:


Code
PLEASE READ THE BELOW INFO ON SETTING UP THE LIBRARY!
All code can be found here: https://www.dropbox.com/sh/udk7bhjic863 ... lq_ra?dl=0
IMPORTANT: The location of the msgbox_icons folder must be hardcoded. You must update the variable MSGBOX.dir$ at the top of the "msgbox" file with wherever the library is located. (If anyone knows of a better way to do this, please let me know!)
The MSGBOX library depends on a few other libraries I have written. Those libraries are:
- graphics_modes — Functions for detecting, setting, saving, and restoring the current graphics mode. (Posted independently here.)
 - draw_fill_settings — Functions for detecting, setting, saving, and restoring the current draw and fill graphics settings. (Inspired by and an adaption of Joel's library)
 - option_sprite_pos — Function for detecting the current OPTION SPRITE POS setting.
 - roundrect — Functions for filling and drawing a rectangle with rounded corners.
 
- /lib/
- graphics/
- draw_fill_settings
 - graphics_modes
 - roundrect
 
 
- interface/
- msgbox
 - msgbox_icons/
 
 - sprites/
- option_sprite_pos
 
 
 - graphics/
 
Code: Select all
MSGBOX.dir$ = "/lib/interface/"    ' needed for icons
'g'
/*
    MSGBOX (text$, type, icon)
    MSGBOX_RESTORE_PARAM_DEFAULTS ()
Function for displaying a windowed message and 1 to 3 buttons for soliciting a user response.
NOTE: You must update MSGBOX.dir$ above for icons to work.
    Uses: FILL_ROUNDRECT, DRAW_ROUNDRECT,
          SAVE_DRAW_AND_FILL, RESTORE_DRAW_AND_FILL,
          IS_OPTION_SPRITE_POS_CENTRAL
'==============================================================
MSGBOX displays a message, provides buttons for user response, and returns an enumerated value corresponding to the user's response. The required inputs include the text to be displayed, the message box type (which determines the buttons to display), and the icon to be displayed. There are also many parameters that can be set before calling the MSGBOX function for more advanced customization.
  Inputs
  ------
    text$    Text to display in the message box
     type    Message box type (enumerated)
               MSGBOX.OkOnly           = 0
               MSGBOX.OkCancel         = 1
               MSGBOX.YesNo            = 2
               MSGBOX.YesNoCancel      = 3
               MSGBOX.RetryCancel      = 4
               MSGBOX.AbortRetryIgnore = 5
               MSGBOX.CustomBttns1     = 6  (one button)
               MSGBOX.CustomBttns2     = 7  (two buttons)
               MSGBOX.CustomBttns3     = 8  (three buttons)
               For custom types, see the parameter info below
     icon    Icon to display next to the text (enumerated)
               MSGBOX.NoIcon      = 0
               MSGBOX.Critical    = 1
               MSGBOX.Exclamation = 2
               MSGBOX.Question    = 3
               MSGBOX.Information = 4
               MSGBOX.CustomImage = 5
               MSGBOX.CustomAscii = 6
               For custom icons, see the parameter info below
  Output
  ------
    MSGBOX returns an enumerated value corresponding to the button hit by the user. Therefore, the possible return values are dependent on the message box type:
      Responses for type MSGBOX.OkOnly:
        MSGBOX.Ok = 0
      Responses for type MSGBOX.OKCancel:
        MSGBOX.Ok     = 0
        MSGBOX.Cancel = 1
      Responses for type MSGBOX.YesNo:
        MSGBOX.Yes = 2
        MSGBOX.No  = 3
      Responses for type MSGBOX.YesNoCancel:
        MSGBOX.Yes    = 2
        MSGBOX.No     = 3
        MSGBOX.Cancel = 1
      Responses for type MSGBOX.RetryCancel:
        MSGBOX.Retry  = 5
        MSGBOX.Cancel = 1
      Responses for type MSGBOX.AbortRetryIgnore:
        MSGBOX.Abort  = 4
        MSGBOX.Retry  = 5
        MSGBOX.Ignore = 6
      Responses for type MSGBOX.Custom1:
        MSGBOX.Button1 = 7
      Responses for type MSGBOX.Custom2:
        MSGBOX.Button1 = 7
        MSGBOX.Button2 = 8
      Responses for type MSGBOX.Custom3:
        MSGBOX.Button1 = 7
        MSGBOX.Button2 = 8
        MSGBOX.Button3 = 9
  Parameters
  ----------
    NOTE: The option SET BUTTONS CUSTOM can be used in conjunction with FILL COLOR, FILL ALPHA, DRAW COLOR, and DRAW ALPHA for controlling the appearance of the buttons in the message box. This will not interfere with the below parameters that set color and alpha values for other elements of the message box. The options SET BUTTONS FONT NAME and SET BUTTONS FONT SIZE can also be used as they would normally.
    Fade-in animation:
    ------------------
    MSGBOX.fadeDuration sets the length of time in seconds of the message box's fade-in animation. Set to 0 to disable the fade-in animation.
    Message box background:
    -----------------------
    MSGBOX.bgR, .bgG, .bgB, and .bgA set the background color and alpha that appears behind the message box.
    Message box and text:
    ---------------------
    MSGBOX.boxFillR, .boxFillG, .boxFillB, and .boxFillA set the fill color and alpha of the message box.
    MSGBOX.boxOutlineR, .boxOutlineG, .boxOutlineB, and .boxOutlineA set the color and alpha of the message box's outline.
    MSGBOX.txtR, .txtG, .txtB, and .txtA set the color and alpha of the message box's text.
    MSGBOX.txtFont$ sets the font name to use for the message box's text. Set to "default" to use the default field font.
    MSGBOX.txtSize sets the font size to use for the message box's text. Set to -1 to use the default field font size.
    Message box icon and buttons:
    -----------------------------
    MSGBOX.iconImagePath$ specifies the image file to use as the message box's icon when the input "icon" is set to MSGBOX.CustomImage.
    MSGBOX.iconAsciiNum specifies the Unicode character to use as the message box's icon when the input "icon" is set to MSGBOX.CustomAscii.
    MSGBOX.iconR, .iconG, .iconB, and .iconA set the color of alpha of the message box's icon. The RGB values only apply to ASCII icons. The alpha value applies to all icons.
    MSGBOX.bttn1$, .bttn2$, and .bttn3$ set the text to use in the user response buttons when the input "type" is set to MSGBOX.CustomBttns1 (one button), MSGBOX.CustomBttns2 (two buttons), or MSGBOX.CustomBttns3 (three buttons).
'==============================================================
MSGBOX_RESTORE_PARAM_DEFAULTS returns all parameters to their initial values.
'==============================================================
*/
''
'b'
'==============================================================
'  Initialization
'==============================================================
''
'  Types (enumerated)
' ----------------------
MSGBOX.OkOnly = 0
MSGBOX.OkCancel = 1
MSGBOX.YesNo = 2
MSGBOX.YesNoCancel = 3
MSGBOX.RetryCancel = 4
MSGBOX.AbortRetryIgnore = 5
MSGBOX.CustomBttns1 = 6
MSGBOX.CustomBttns2 = 7
MSGBOX.CustomBttns3 = 8
MSGBOX.NUM_TYPES = 9
DIM MSGBOX.numButtons(MSGBOX.NUM_TYPES)
MSGBOX.numButtons(MSGBOX.OkOnly) = 1
MSGBOX.numButtons(MSGBOX.OkCancel) = 2
MSGBOX.numButtons(MSGBOX.YesNo) = 2
MSGBOX.numButtons(MSGBOX.YesNoCancel) = 3
MSGBOX.numButtons(MSGBOX.RetryCancel) = 2
MSGBOX.numButtons(MSGBOX.AbortRetryIgnore) = 3
MSGBOX.numButtons(MSGBOX.CustomBttns1) = 1
MSGBOX.numButtons(MSGBOX.CustomBttns2) = 2
MSGBOX.numButtons(MSGBOX.CustomBttns3) = 3
'  Icons (enumerated)
' --------------------
MSGBOX.NoIcon = 0
MSGBOX.Critical = 1
MSGBOX.Exclamation = 2
MSGBOX.Question = 3
MSGBOX.Information = 4
MSGBOX.CustomImage = 5
MSGBOX.CustomAscii = 6
'  Responses (enumerated)
' ------------------------
MSGBOX.Ok = 0
MSGBOX.Cancel = 1
MSGBOX.Yes = 2
MSGBOX.No = 3
MSGBOX.Abort = 4
MSGBOX.Retry = 5
MSGBOX.Ignore = 6
MSGBOX.Button1 = 7    ' for custom types
MSGBOX.Button2 = 8
MSGBOX.Button3 = 9
'  Default Parameter Values
' --------------------------
MSGBOX_RESTORE_PARAM_DEFAULTS()
DEF MSGBOX_RESTORE_PARAM_DEFAULTS ()
  MSGBOX.fadeDuration = 0.2
  MSGBOX.bgR = 0
  MSGBOX.bgG = 0
  MSGBOX.bgB = 0
  MSGBOX.bgA = 0.2
  MSGBOX.boxFillR = 0.9
  MSGBOX.boxFillG = 0.9
  MSGBOX.boxFillB = 0.9
  MSGBOX.boxFillA = 1
  MSGBOX.boxOutlineR = 0
  MSGBOX.boxOutlineG = 0
  MSGBOX.boxOutlineB = 0
  MSGBOX.boxOutlineA = 1
  MSGBOX.txtR = 0
  MSGBOX.txtG = 0
  MSGBOX.txtB = 0
  MSGBOX.txtA = 1
  MSGBOX.txtFont$ = "default"
  MSGBOX.txtSize = -1            ' set to -1 for default
  MSGBOX.iconR = 0
  MSGBOX.iconG = 0
  MSGBOX.iconB = 0
  MSGBOX.iconA = 1
  MSGBOX.bttn1$ = "1"
  MSGBOX.bttn2$ = "2"
  MSGBOX.bttn3$ = "3"
END DEF
'b'
'==============================================================
'  Main Function
'==============================================================
''
DEF MSGBOX (text$, type, icon)
'  Setup
' -------
  GET SCREEN SIZE scrW, scrH
  GET_DRAW_AND_FILL()          ' Also stores the graphics mode
  dr = GET_DRAW_AND_FILL.dr
  dg = GET_DRAW_AND_FILL.dg
  db = GET_DRAW_AND_FILL.db
  da = GET_DRAW_AND_FILL.da
  fr = GET_DRAW_AND_FILL.fr
  fg = GET_DRAW_AND_FILL.fg
  fb = GET_DRAW_AND_FILL.fb
  fa = GET_DRAW_AND_FILL.fa
  fs = GET_DRAW_AND_FILL.fs
  tp = GET_DRAW_AND_FILL.tpCentral
  gm$ = GET_DRAW_AND_FILL.gm$
  GRAPHICS MODE NORMAL
  sp = IS_OPTION_SPRITE_POS_CENTRAL()
  OPTION SPRITE POS NORMAL
  nPg$ = "msgbox_page"
  nBox$ = "msgbox_box"
  nIco$ = "msgbox_icon"
  nTxt$ = "msgbox_text"
  nBn1$ = "msgbox_bttn_1"
  nBn2$ = "msgbox_bttn_2"
  nBn3$ = "msgbox_bttn_3"
'  Page
' ------
  PAGE nPg$ SET
  PAGE nPg$ FRAME 0, 0, scrW, scrH
  PAGE nPg$ COLOR 0, 0, 0, 0
  PAGE nPg$ ALPHA 0
  PAGE nPg$ SHOW
'  Background and Box Sprite
' ---------------------------
  wBox = INT(scrW*0.8)
  hBox = INT(scrH*0.7)
  IF wBox*0.75 < hBox THEN
    hBox = INT(wBox*0.7)
  ELSE
    wBox = INT(hBox*1.6)
  END IF
  crBox = INT(MIN(wBox, hBox)*0.1)
  mX = INT((scrW - wBox)/2)
  mY = INT((scrH - hBox)/2)
  SPRITE nBox$ BEGIN scrW, scrH
    GRAPHICS CLEAR bgR, bgG, bgB, bgA
    GRAPHICS MODE COPY
    FILL COLOR boxFillR, boxFillG, boxFillB
    FILL ALPHA boxFillA
    FILL_ROUNDRECT(mX, mY, mX+wBox, mY+hBox, crBox)
    DRAW COLOR boxOutlineR, boxOutlineG, boxOutlineB
    DRAW ALPHA boxOutlineA
    DRAW_ROUNDRECT(mX, mY, mX+wBox, mY+hBox, crBox)
  SPRITE END
  SPRITE nBox$ SHOW
  GRAPHICS MODE NORMAL
  margin = INT(hBox*0.1)    ' controls spacing of box elements
'  Icon
' ------
  m = 0
  IF icon = Critical THEN
    SPRITE nIco$ LOAD dir$ & "/msgbox_icons/critical.png"
  ELSE ! IF icon = Exclamation THEN
    SPRITE nIco$ LOAD dir$ & "/msgbox_icons/exclamation.png"
  ELSE ! IF icon = Question THEN
    SPRITE nIco$ LOAD dir$ & "/msgbox_icons/question.png"
  ELSE ! IF icon = Information THEN
    SPRITE nIco$ LOAD dir$ & "/msgbox_icons/information.png"
  ELSE ! IF icon = CustomImage THEN
    IF FILE_EXISTS(iconImagePath$) THEN
      SPRITE nIco$ LOAD iconImagePath$
    ELSE
      icon = NoIcon
    END IF
  ELSE ! IF icon = CustomAscii THEN
    IF iconAsciiNum >= 0 AND iconAsciiNum <= 65535 THEN
      m = 5
      wIco = hBox*0.2
      hIco = hBox*0.2
      SPRITE nIco$ BEGIN wIco+2*m, hIco+2*m
        OPTION TEXT POS CENTRAL
        DRAW COLOR iconR, iconG, iconB
        DRAW ALPHA 1
        DRAW FONT SIZE hIco
        DRAW TEXT CHR$(iconAsciiNum) AT m+wIco/2, m+hIco/2
      SPRITE END
    ELSE
      icon = NoIcon
    END IF
  ELSE
    icon = NoIcon
  END IF ! END IF ! END IF ! END IF ! END IF ! END IF
  IF icon <> NoIcon THEN
    GET SPRITE nIco$ SIZE wIco, hIco
    scIco = hBox*0.2/wIco
    xIco = mX + margin - (wIco/2 - margin)
    yIco = mY + margin - (hIco/2 - margin)
    SPRITE nIco$ AT xIco, yIco SCALE scIco
    SPRITE nIco$ ALPHA iconA
    SPRITE nIco$ SHOW
  END IF
'  Buttons
' ---------
  SET_DRAW(dr, dg, db, da)    ' Support SET BUTTONS CUSTOM
  SET_FILL(fr, fg, fb, fa)
  DRAW FONT SIZE fs
  IF type < 0 OR type >= NUM_TYPES THEN type = OkOnly
  hBn = hBox*0.2
  yBn = (scrH+hBox)/2 - hBn - margin/2
  IF numButtons(type) = 1 THEN
    wBn = wBox/2
    xBn1 = scrW/2 - wBn/2
    IF type = OkOnly THEN tBn1$ = "Ok"
    IF type = CustomBttns1 THEN tBn1$ = bttn1$
    BUTTON nBn1$ TEXT tBn1$ AT xBn1, yBn SIZE wBn, hBn
  ELSE ! IF numButtons(type) = 2 THEN
    wBn = wBox*0.42
    xBn1 = scrW/2 - wBn/2 - wBn/2 - margin/6
    xBn2 = scrW/2 - wBn/2 + wBn/2 + margin/6
    IF type = OkCancel THEN
      tBn1$ = "Ok"
      tBn2$ = "Cancel"
    END IF
    IF type = YesNo THEN
      tBn1$ = "Yes"
      tBn2$ = "No"
    END IF
    IF type = RetryCancel THEN
      tBn1$ = "Retry"
      tBn2$ = "Cancel"
    END IF
    IF type = CustomBttns2 THEN
      tBn1$ = bttn1$
      tBn2$ = bttn2$
    END IF
    BUTTON nBn1$ TEXT tBn1$ AT xBn1, yBn SIZE wBn, hBn
    BUTTON nBn2$ TEXT tBn2$ AT xBn2, yBn SIZE wBn, hBn
  ELSE ' numButtons(type) = 3
    wBn = wBox*0.3
    xBn1 = scrW/2 - wBn/2 - wBn - margin/6
    xBn2 = scrW/2 - wBn/2
    xBn3 = scrW/2 - wBn/2 + wBn + margin/6
    IF type = YesNoCancel THEN
      tBn1$ = "Yes"
      tBn2$ = "No"
      tBn3$ = "Cancel"
    END IF
    IF type = AbortRetryIgnore THEN
      tBn1$ = "Abort"
      tBn2$ = "Retry"
      tBn3$ = "Ignore"
    END IF
    IF type = CustomBttns3 THEN
      tBn1$ = bttn1$
      tBn2$ = bttn2$
      tBn3$ = bttn3$
    END IF
    BUTTON nBn1$ TEXT tBn1$ AT xBn1, yBn SIZE wBn, hBn
    BUTTON nBn2$ TEXT tBn2$ AT xBn2, yBn SIZE wBn, hBn
    BUTTON nBn3$ TEXT tBn3$ AT xBn3, yBn SIZE wBn, hBn
  END IF ! END IF
'  Text Field
' ------------
  mAdj = margin/4    ' counteracts field padding
  IF icon <> NoIcon THEN
    wTxt = wBox - 3*margin - scIco*wIco + 2*mAdj
    xTxt = mX + 2*margin + scIco*wIco - mAdj
    yTxt = mY + margin - 2*mAdj
  ELSE
    wTxt = wBox - 2*margin + 2*mAdj
    xTxt = (scrW-wBox)/2 + margin - mAdj
    yTxt = (scrH-hBox)/2 + margin - 2*mAdj
  END IF
  hTxt = hBox - 1.5*margin - hBn
  FIELD nTxt$ TEXT text$ AT xTxt, yTxt SIZE wTxt, hTxt RO ML
  FIELD nTxt$ BACK ALPHA 0
  FIELD nTxt$ FONT COLOR txtR, txtG, txtB
  FIELD nTxt$ FONT ALPHA txtA
  IF txtFont$ <> "default" THEN
    FIELD nTxt$ FONT NAME txtFont$
  END IF
  IF txtSize > -1 THEN
    FIELD nTxt$ FONT SIZE txtSize
  END IF
  FIELD nTxt$ SELECT 0, 0    ' Scroll to top in case text is long
'  Show message box
' ------------------
  IF fadeDuration > 0 THEN
    tFadeStart = TIME()
    tFadeCurr = tFadeStart
    tFadeEnd = tFadeStart + fadeDuration
    WHILE tFadeCurr <= tFadeEnd
      tFadeCurr = TIME()
      v = (tFadeCurr - tFadeStart)/fadeDuration
      PAGE nPg$ ALPHA v
    END WHILE
  END IF
  PAGE nPg$ ALPHA 1
  ' Ignore any button pressed up until this point
  z = BUTTON_PRESSED(nBn1$)
  IF numButtons(type) > 1 THEN z = BUTTON_PRESSED(nBn2$)
  IF numButtons(type) > 2 THEN z = BUTTON_PRESSED(nBn3$)
'  Get User Response
' -------------------
  response = -1
  DO
    SLOWDOWN
    IF numButtons(type) = 1 THEN
      IF BUTTON_PRESSED(nBn1$) THEN
        IF type = OkOnly       THEN response = Ok
        IF type = CustomBttns1 THEN response = Button1
      END IF
    ELSE ! IF numButtons(type) = 2 THEN
      IF BUTTON_PRESSED(nBn1$) THEN
        IF type = OkCancel     THEN response = Ok
        IF type = YesNo        THEN response = Yes
        IF type = RetryCancel  THEN response = Retry
        IF type = CustomBttns2 THEN response = Button1
      END IF
      IF BUTTON_PRESSED(nBn2$) THEN
        IF type = OkCancel     THEN response = Cancel
        IF type = YesNo        THEN response = No
        IF type = RetryCancel  THEN response = Cancel
        IF type = CustomBttns2 THEN response = Button2
      END IF
    ELSE ' numButtons(type) = 3 THEN
      IF BUTTON_PRESSED(nBn1$) THEN
        IF type = YesNoCancel      THEN response = Yes
        IF type = AbortRetryIgnore THEN response = Abort
        IF type = CustomBttns3     THEN response = Button1
      END IF
      IF BUTTON_PRESSED(nBn2$) THEN
        IF type = YesNoCancel      THEN response = No
        IF type = AbortRetryIgnore THEN response = Retry
        IF type = CustomBttns3     THEN response = Button2
      END IF
      IF BUTTON_PRESSED(nBn3$) THEN
        IF type = YesNoCancel      THEN response = Cancel
        IF type = AbortRetryIgnore THEN response = Ignore
        IF type = CustomBttns3     THEN response = Button3
      END IF
    END IF ! END IF
  UNTIL response > -1
'  Cleanup
' ---------
  SPRITE nBox$ DELETE
  FIELD nTxt$ DELETE
  BUTTON nBn1$ DELETE
  IF numButtons(type) > 1 THEN BUTTON nBn2$ DELETE
  IF numButtons(type) > 2 THEN BUTTON nBn3$ DELETE
  IF icon <> NoIcon THEN SPRITE nIco$ DELETE
  PAGE nPg$ HIDE
  SET_GRAPHICS_MODE(gm$)
  IF tp = 0 THEN
    OPTION TEXT POS NORMAL
  ELSE
    OPTION TEXT POS CENTRAL
  END IF
  IF sp = 0 THEN
    OPTION SPRITE POS NORMAL
  ELSE
    OPTION SPRITE POS CENTRAL
  END IF
  
  RETURN response
END DEF
'==============================================================
'g'
/*
    GET_GRAPHICS_MODE$ ()
    SET_GRAPHICS_MODE (gm$)
Functions for detecting and setting the graphics mode.
Note: GET_GRAPHICS_MODE$ cannot be called while editing a sprite's graphics layer.
'==============================================================
GET_GRAPHICS_MODE$ detects the current graphics mode and returns it as a string (e.g. "NORMAL", "MULTIPLY", "SCREEN", etc.).
'==============================================================
SET_GRAPHICS_MODE sets the graphics mode based on the input.
  Inputs
  ------
    gm$    Graphics mode string (e.g. "MULTIPLY")
'==============================================================
*/
''
DEF GET_GRAPHICS_MODE$ ()
  r1 = 0.7  !  r2 = 0.1
  g1 = 0.0  !  g2 = 0.3
  b1 = 1.0  !  b2 = 0.7
  a1 = 0.2  !  a2 = 0.9
  nScan$ = "save_graphics_mode_screen"
  SPRITE nScan$ SCAN 0, 0, 2, 2
  DRAW PIXEL 0, 0 COLOR r1, g1, b1, a1
  nStamp$ = "save_graphics_mode_stamp"
  SPRITE nStamp$ BEGIN 1, 1
    GRAPHICS CLEAR r2, g2, b2, a2
  SPRITE END
  SPRITE nStamp$ STAMP
  GET PIXEL 0, 0 COLOR r, g, b, a
  r$ = INT(r*255)
  g$ = INT(g*255)
  b$ = INT(b*255)
  a$ = INT(a*255)
  c$ = r$ & "-" & g$ & "-" & b$ & "-" & a$
  IF c$ = "28-75-180-235" THEN gm$ = "NORMAL"
  IF c$ = "26-60-180-235" THEN gm$ = "MULTIPLY"
  IF c$ = "59-75-194-235" THEN gm$ = "SCREEN"
  IF c$ = "44-60-194-235" THEN gm$ = "OVERLAY"
  IF c$ = "28-60-180-235" THEN gm$ = "DARKEN"
  IF c$ = "58-75-194-235" THEN gm$ = "LIGHTEN"
  IF c$ = "61-60-255-235" THEN gm$ = "COLORDODGE"
  IF c$ = "0-0-194-235"   THEN gm$ = "COLORBURN"
  IF c$ = "49-60-194-235" THEN gm$ = "SOFTLIGHT"
  IF c$ = "30-60-194-235" THEN gm$ = "HARDLIGHT"
  IF c$ = "52-75-160-235" THEN gm$ = "DIFFERENCE"
  IF c$ = "55-75-160-235" THEN gm$ = "EXCLUSION"
  IF c$ = "23-76-194-235" THEN gm$ = "HUE"
  IF c$ = "50-66-181-235" THEN gm$ = "SATURATION"
  IF c$ = "29-76-181-235" THEN gm$ = "COLOR"
  IF c$ = "52-60-187-235" THEN gm$ = "LUMINOSITY"
  IF c$ = "0-0-0-0"       THEN gm$ = "CLEAR"
  IF c$ = "26-77-179-230" THEN gm$ = "COPY"
  IF c$ = "28-78-183-46"  THEN gm$ = "SOURCEIN"
  IF c$ = "26-78-179-184" THEN gm$ = "SOURCEOUT"
  IF c$ = "40-65-185-51"  THEN gm$ = "SOURCEATOP"
  IF c$ = "58-60-194-235" THEN gm$ = "DESTOVER"
  IF c$ = "177-0-255-46"  THEN gm$ = "DESTIN"
  IF c$ = "204-0-255-5"   THEN gm$ = "DESTOUT"
  IF c$ = "54-61-193-230" THEN gm$ = "DESTATOP"
  IF c$ = "28-74-179-189" THEN gm$ = "XOR"
  IF c$ = "32-43-186-255" THEN gm$ = "PLUSDARKER"
  IF c$ = "58-69-212-255" THEN gm$ = "PLUSLIGHTER"
  GRAPHICS MODE COPY
  SPRITE nScan$ STAMP
  SET_GRAPHICS_MODE(gm$)
  RETURN gm$
END DEF
'==============================================================
DEF SET_GRAPHICS_MODE (gm$)
  gm$ = CAPSTR$(gm$)
  IF gm$ = "NORMAL"      THEN GRAPHICS MODE NORMAL
  IF gm$ = "MULTIPLY"    THEN GRAPHICS MODE MULTIPLY
  IF gm$ = "SCREEN"      THEN GRAPHICS MODE SCREEN
  IF gm$ = "OVERLAY"     THEN GRAPHICS MODE OVERLAY
  IF gm$ = "DARKEN"      THEN GRAPHICS MODE DARKEN
  IF gm$ = "LIGHTEN"     THEN GRAPHICS MODE LIGHTEN
  IF gm$ = "COLORDODGE"  THEN GRAPHICS MODE COLORDODGE
  IF gm$ = "COLORBURN"   THEN GRAPHICS MODE COLORBURN
  IF gm$ = "SOFTLIGHT"   THEN GRAPHICS MODE SOFTLIGHT
  IF gm$ = "HARDLIGHT"   THEN GRAPHICS MODE HARDLIGHT
  IF gm$ = "DIFFERENCE"  THEN GRAPHICS MODE DIFFERENCE
  IF gm$ = "EXCLUSION"   THEN GRAPHICS MODE EXCLUSION
  IF gm$ = "HUE"         THEN GRAPHICS MODE HUE
  IF gm$ = "SATURATION"  THEN GRAPHICS MODE SATURATION
  IF gm$ = "COLOR"       THEN GRAPHICS MODE COLOR
  IF gm$ = "LUMINOSITY"  THEN GRAPHICS MODE LUMINOSITY
  IF gm$ = "CLEAR"       THEN GRAPHICS MODE CLEAR
  IF gm$ = "COPY"        THEN GRAPHICS MODE COPY
  IF gm$ = "SOURCEIN"    THEN GRAPHICS MODE SOURCEIN
  IF gm$ = "SOURCEOUT"   THEN GRAPHICS MODE SOURCEOUT
  IF gm$ = "SOURCEATOP"  THEN GRAPHICS MODE SOURCEATOP
  IF gm$ = "DESTOVER"    THEN GRAPHICS MODE DESTOVER
  IF gm$ = "DESTIN"      THEN GRAPHICS MODE DESTIN
  IF gm$ = "DESTOUT"     THEN GRAPHICS MODE DESTOUT
  IF gm$ = "DESTATOP"    THEN GRAPHICS MODE DESTATOP
  IF gm$ = "XOR"         THEN GRAPHICS MODE XOR
  IF gm$ = "PLUSDARKER"  THEN GRAPHICS MODE PLUSDARKER
  IF gm$ = "PLUSLIGHTER" THEN GRAPHICS MODE PLUSLIGHTER
END DEF
'==============================================================
'g'
/*
    GET_DRAW_AND_FILL ()
    SET_DRAW (r, g, b, a)
    SET_FILL (r, g, b, a)
Functions for detecting and setting the current draw and fill settings.
Note: GET_DRAW_AND_FILL cannot be called while editing a sprite's graphics layer.
Inspired by and an adaption of Joel's library file for saving and restoring graphics parameters (https://kibernetik.pro/forum/viewtopic.php?f=70&t=1730#p10857)
    Uses: GET_GRAPHICS_MODE$, SET_GRAPHICS_MODE
'==============================================================
GET_DRAW_AND_FILL detects the following graphics settings:
    DRAW COLOR
    DRAW ALPHA
    FILL COLOR
    FILL ALPHA
    DRAW SIZE
    DRAW FONT SIZE
    OPTION TEXT POS
The values of the detected settings can be retrieved from these variables after calling the function:
    GET_DRAW_AND_FILL.dr    Draw color (red)
    GET_DRAW_AND_FILL.dg    Draw color (green)
    GET_DRAW_AND_FILL.db    Draw color (blue)
    GET_DRAW_AND_FILL.da    Draw alpha
    GET_DRAW_AND_FILL.fr    Fill color (red)
    GET_DRAW_AND_FILL.fg    Fill color (green)
    GET_DRAW_AND_FILL.fb    Fill color (blue)
    GET_DRAW_AND_FILL.fa    Fill alpha
    GET_DRAW_AND_FILL.ds    Draw size
    GET_DRAW_AND_FILL.fs    Draw font size
    GET_DRAW_AND_FILL.tpCentral    Text pos central (boolean)
    GET_DRAW_AND_FILL.tpNormal     Text pos normal (boolean)
NOTE: There is a bug that causes the DRAW LINECAP option (possible values "RECT" and "ROUND") to always be reset to RECT when a SPRITE BEGIN command is called. This means that the DRAW LINECAP option cannot be detected without making certain assumptions about graphics settings. The SPRITE BEGIN command is unavoidable: If draw settings are tested and detected inside a sprite graphics layer (the current method employed), then obviously a SPRITE BEGIN command is necessary. If draw settings are tested and detected inside the main graphics layer after a SPRITE SCAN command, the option is still reset during the call to GET_GRAPHICS_MODE$ since a SPRITE BEGIN command is necessary for detecting the graphics mode.
'==============================================================
SET_DRAW sets the draw RGBA settings based on the inputs.
  Inputs
  ------
    r    Draw color (red)
    g    Draw color (green)
    b    Draw color (blue)
    a    Draw alpha
'==============================================================
SET_FILL sets the fill RGBA settings based on the inputs.
  Inputs
  ------
    r    Fill color (red)
    g    Fill color (green)
    b    Fill color (blue)
    a    Fill alpha
'==============================================================
*/
''
DEF GET_DRAW_AND_FILL ()
  scrH = SCREEN_HEIGHT()
  gm$ = GET_GRAPHICS_MODE$()
  n$ = "save_draw_and_fill_settings"
  SPRITE n$ BEGIN 2, scrH
    GRAPHICS MODE COPY
    box$ = CHR$(9608)    ' box$ = "█"
    ' Draw font size
    fs = FONT_SIZE()
    ' Draw color and alpha
    DRAW FONT SIZE 20                  ' Doesn't depend on
    DRAW TEXT box$ AT -1, -1           ' DRAW SIZE setting
    GET PIXEL 1, 1 COLOR dr,dg,db,da
    IF da < 1 THEN                     ' GET PIXEL is not 100%
      DRAW ALPHA 1                     ' accurate for alpha
      DRAW TEXT box$ AT -1, -1         ' values less than 1
      GET PIXEL 1, 1 COLOR dr,dg,db
    END IF
    ' Text position option
    GRAPHICS CLEAR
    DRAW ALPHA 1
    DRAW TEXT box$ AT 5, 5
    GET PIXEL 0, 0 COLOR v, v, v, v
    IF v THEN tpCentral = 1 ELSE tpCentral = 0
    tpNormal = 1 - tpCentral
    ' Draw size
    GRAPHICS CLEAR 1, 1, 1
    DRAW COLOR 0, 0, 0
    DRAW ALPHA 1
    DRAW LINE 0, 1 TO 1, 1
    FOR p = 1 TO scrH*2-1
      GET PIXEL 0, p COLOR v, v, v
      IF v THEN BREAK p
    NEXT p
    IF p = 1 THEN ds = 0
    IF p = 2 THEN ds = 0.5
    IF p > 2 THEN ds = p - 2
    ' Fill color and alpha
    FILL RECT 0, 0 TO 2, 2
    GET PIXEL 1, 1 COLOR fr,fg,fb,fa
    IF a < 1 THEN                      ' GET PIXEL is not 100%
      FILL ALPHA 1                     ' accurate for alpha
      FILL RECT 0, 0 TO 2, 2           ' values less than 1
      GET PIXEL 1, 1 COLOR fr,fg,fb
    END IF
  SPRITE END
  DRAW COLOR dr, dg, db
  DRAW ALPHA da
  DRAW FONT SIZE fs
  FILL ALPHA fa
  SET_GRAPHICS_MODE(gm$)
END DEF
'==============================================================
DEF SET_DRAW (r, g, b, a)
  DRAW COLOR r, g, b
  DRAW ALPHA a
END DEF
'==============================================================
DEF SET_FILL (r, g, b, a)
  FILL COLOR r, g, b
  FILL ALPHA a
END DEF
'==============================================================
'g'
/*
    IS_OPTION_SPRITE_POS_CENTRAL ()
Functions for determining the current state of OPTION SPRITE POS.
'==============================================================
IS_OPTION_SPRITE_POS_CENTRAL returns 1 if OPTION SPRITE POS is set to CENTRAL. Otherwise returns 0.
'==============================================================
*/
'' 
DEF IS_OPTION_SPRITE_POS_CENTRAL ()
  t$ = TIME()
  n1$ = "test_option_sprite_pos_1"
  n2$ = "test_option_sprite_pos_2"
  SPRITE n1$ BEGIN 5,5
    GRAPHICS CLEAR 0,0,0
  SPRITE END
  SPRITE n1$ ALPHA 0.01
  SPRITE n1$ SHOW
  SPRITE n1$ COPY n2$
  SPRITE n2$ AT 6,6
  SPRITE n2$ SHOW
  IF SPRITES_COLLIDE(n1$, n2$) THEN
    IS_OPTION_SPRITE_POS_CENTRAL = 1
  ELSE
    IS_OPTION_SPRITE_POS_CENTRAL = 0
  END IF
  SPRITE n1$ DELETE
  SPRITE n2$ DELETE
END DEF
'==============================================================
'g'
/*
    DRAW_ROUNDRECT (x1, y1, x2, y2, r)
    FILL_ROUNDRECT (x1, y1, x2, y2, r)
Functions for drawing rectangles with rounded corners.
    Uses: .pi
'==============================================================
Both functions take an input of the rectangle's top-left x,y coordinates and bottom-right x,y coordinates, and the corner radius.
  Inputs
  ------
    x1, y1    x,y-coordinates of the top-left corner
    x2, y2    x,y-coordinates of the bottom-right corner
    r         corner radius
'==============================================================
DRAW_ROUNDRECT draws a rectangle with rounded corners. This function uses only DRAW LINE and DRAW ARC commands.
FILL_ROUNDRECT fills a rectangle with rounded corners. This function uses only FILL CIRCLE and FILL RECT commands. Note: This function uses multiple fill commands that overlap in some areas. For alpha values that are less than one and non-normal graphics modes, run this command with FILL ALPHA 1 and GRAPHICS MODE NORMAL in a sprite and then stamp the sprite to the final destination with the desired graphics settings.
'==============================================================
*/
''
DEF DRAW_ROUNDRECT (x1, y1, x2, y2, r)
  pi = 2*ACOS(0)
  DRAW LINE x1+r, y1   TO x2-r, y1          ' top
  DRAW LINE x2-r, y2   TO x1+r, y2          ' bottom
  DRAW LINE x2  , y1+r TO x2  , y2-r        ' right
  DRAW LINE x1  , y2-r TO x1  , y1+r        ' left
  DRAW ARC x1+r, y1+r, r, pi    , pi*3/2    ' top-left
  DRAW ARC x2-r, y1+r, r, pi*3/2,  0        ' top-right
  DRAW ARC x2-r, y2-r, r, 0     , pi/2      ' bottom-right
  DRAW ARC x1+r, y2-r, r, pi/2  , pi        ' bottom-left
END DEF
'==============================================================
DEF FILL_ROUNDRECT (x1, y1, x2, y2, r)
  FILL CIRCLE x1+r, y1+r SIZE r         ' top-left
  FILL CIRCLE x2-r, y1+r SIZE r         ' top-right
  FILL CIRCLE x2-r, y2-r SIZE r         ' bottom-right
  FILL CIRCLE x1+r, y2-r SIZE r         ' bottom-left
  FILL RECT x1+r, y1   TO x2-r, y2      ' vertical
  FILL RECT x1  , y1+r TO x2  , y2-r    ' horizontal
END DEF
'==============================================================