From 39101de3b4a6b8ab392075be58d99449246cf5e5 Mon Sep 17 00:00:00 2001 From: 4am Date: Wed, 28 Feb 2018 15:43:14 -0500 Subject: [PATCH] add support for radio buttons --- Documentation.md | 50 ++++++++++++++++++++-- WeeGUI_MLI.s | 2 + applesoft.s | 69 +++++++++++++++++++++++++------ macros.s | 4 ++ memory.s | 1 + rects.s | 33 +++++++++++---- views.s | 105 ++++++++++++++++++++++++++++++++++++++++++++++- weegui.dsk | Bin 143360 -> 143360 bytes weegui.s | 2 + zeropage.s | 1 + 10 files changed, 239 insertions(+), 28 deletions(-) diff --git a/Documentation.md b/Documentation.md index 496b46c..99d6af5 100644 --- a/Documentation.md +++ b/Documentation.md @@ -97,9 +97,9 @@ Calling WeeGUI ###Applesoft -WeeGUI is accessed from Applesoft through ampersand extensions. For example, the DESK command clears the screen and draws a desktop background: +WeeGUI is accessed from Applesoft through ampersand extensions. For example, the HOME command clears the screen and can optionally draw a desktop background: - 10 &DESK + 10 &HOME(1) If the call requires parameters, you can make a C-style function call. For example, to select view #6, you would call: @@ -425,6 +425,32 @@ Configuration block consists of five bytes: +####WGCreateRadio +Creates a new WeeGUI radio button view. This is a specialized version of WGCreateView, and its parameters are similar. + +Radio buttons act as a group; selecting one radio button deselects all other radio buttons. In WeeGUI, all radio buttons function as a single group. If you need multiple radio groups, rethink your design until you realize you don't need that. + + + +
AssemblyApplesoft
+X:        WGCreateRadio
+PARAM0: Pointer to configuration block (LSB)
+PARAM1:    Pointer to configuration block (MSB)
+
+Configuration block consists of five bytes:
+0:    View ID (0-15)
+1:    X position of checkbox
+2:    Y position of checkbox
+3:     Pointer to null-terminated string label (LSB)
+4:    Pointer to null-terminated string label (MSB)
+
+&RADIO(    View ID,
+        X position,
+        Y position,
+"Label")
+
+ + ####WGCreateProgress Creates a new WeeGUI progress bar view. This is a specialized version of WGCreateView, and its parameters are similar. @@ -899,7 +925,7 @@ Clears the screen to black. Unlike Applesoft HOME, this version always clears to AssemblyApplesoft
 X:		WGClearScreen
 
-&HOME
+&HOME(0)
 
@@ -911,7 +937,7 @@ Paints a desktop background on the screen. AssemblyApplesoft
 X:		WGDesktop
 
-&DESK
+&HOME(1)
 
@@ -972,6 +998,22 @@ PARAM3: Height +####WGStrokeRoundRect +Draws the outline of a "round" rectangle, with left and right edges curved instead of straight. The rectangle is drawn one pixel outside the region you specify. So, *do not* attempt to stroke a rectangle in row 0, row 23, column 0, or column 79. You may overwrite screen holes and cause system malfunctions. + + + +
AssemblyApplesoft
+X:		WGStrokeRoundRect
+PARAM0: Left edge
+PARAM1:	Top edge
+PARAM2: Width
+PARAM3: Height
+
+&DRAW(left,top,width,height)
+
+ + ####WGFillRect Fills a rectangle with a given character diff --git a/WeeGUI_MLI.s b/WeeGUI_MLI.s index e9ece09..1d3bac3 100644 --- a/WeeGUI_MLI.s +++ b/WeeGUI_MLI.s @@ -71,4 +71,6 @@ WGSetState = 74 WGViewSetRawTitle = 76 WGSetContentWidth = 78 WGSetContentHeight = 80 +WGStrokeRoundRect = 82 +WGCreateRadio = 84 diff --git a/applesoft.s b/applesoft.s index 11423a0..acb9bd1 100644 --- a/applesoft.s +++ b/applesoft.s @@ -386,21 +386,25 @@ WGAmpersandTempStrArgument_error: ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; WGAmpersand_HOME -; Clears the screen -; &HOME +; Clears the screen to black (if 0) or "desktop" fill (if 1) +; &HOME(0 or 1) WGAmpersand_HOME: - jsr WGClearScreen - jsr WGBottomCursor + jsr WGAmpersandBeginArguments + jsr WGAmpersandIntArgument + pha + jsr WGAmpersandEndArguments + pla - rts + beq WGAmpersand_HOMEClearScreen - -;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -; WGAmpersand_DESK -; Render the desktop -; &DESK -WGAmpersand_DESK: jsr WGDesktop + bra WGAmpersand_HOMEBottomCursor + +WGAmpersand_HOMEClearScreen: + jsr WGClearScreen + ; execution falls through here + +WGAmpersand_HOMEBottomCursor: jsr WGBottomCursor rts @@ -492,6 +496,45 @@ WGAmpersand_CHKBX: rts +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +; WGAmpersand_RADIO +; Create a radio button +; &RADIO(id,x,y,"title") +WGAmpersand_RADIO: + jsr WGAmpersandBeginArguments + + jsr WGAmpersandIntArgument + sta WGAmpersandCommandBuffer+0 + jsr WGAmpersandNextArgument + + jsr WGAmpersandIntArgument + sta WGAmpersandCommandBuffer+1 + jsr WGAmpersandNextArgument + + jsr WGAmpersandIntArgument + sta WGAmpersandCommandBuffer+2 + jsr WGAmpersandNextArgument + + jsr WGAmpersandStrArgument + stx WGAmpersandCommandBuffer+3 + sty WGAmpersandCommandBuffer+4 + + jsr WGAmpersandEndArguments + + CALL16 WGCreateRadio,WGAmpersandCommandBuffer + + LDY_ACTIVEVIEW ; Flag this as an Applesoft-created view + lda #VIEW_STYLE_APPLESOFT + ora WG_VIEWRECORDS+4,y + sta WG_VIEWRECORDS+4,y + + jsr WGPaintView + jsr WGBottomCursor + + rts + + + ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; WGAmpersand_PROG ; Create a progress bar @@ -1249,8 +1292,8 @@ WGAmpersandCommandTable: .byte TOKEN_HOME,0,0,0,0,0 .addr WGAmpersand_HOME -.byte "DESK",0,0 -.addr WGAmpersand_DESK +.byte "RADIO",0 +.addr WGAmpersand_RADIO .byte "WINDW",0 .addr WGAmpersand_WINDW diff --git a/macros.s b/macros.s index a3f5d3f..aafd3bd 100644 --- a/macros.s +++ b/macros.s @@ -93,10 +93,14 @@ pha lda SCRATCH1 pha + lda SCRATCH2 + pha .endmacro .macro RESTORE_ZPS ; Restores Zero Page locations we use for scratch + pla + sta SCRATCH2 pla sta SCRATCH1 pla diff --git a/memory.s b/memory.s index 6a03eb8..0a6e2a8 100644 --- a/memory.s +++ b/memory.s @@ -19,6 +19,7 @@ VIEW_STYLE_FANCY = $02 VIEW_STYLE_PROGRESS = $03 VIEW_STYLE_CHECK = $04 VIEW_STYLE_BUTTON = $05 +VIEW_STYLE_RADIO = $06 VIEW_STYLE_TAKESFOCUS = $04 ; Styles >= this one are selectable diff --git a/rects.s b/rects.s index 97d3708..a1dee4d 100644 --- a/rects.s +++ b/rects.s @@ -159,7 +159,7 @@ WGFillRect_horzLoopOddAlignedOneWidth: ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -; WGStrokeRect +; WGStrokeRect / WGStrokeRoundRect ; Strokes a rectangle (assumes 80 cols) ; PARAM0: Left edge ; PARAM1: Top edge @@ -171,6 +171,8 @@ CH_TOP = '_'+$80 CH_BOTTOM = 'L' CH_LEFT = 'Z' CH_RIGHT = '_' +CH_ROUND_LEFT = '('+$80 +CH_ROUND_RIGHT = ')'+$80 CH_DOUBLE = '\' .macro PLOTHLINE @@ -194,8 +196,21 @@ CH_DOUBLE = '\' WGStrokeRect: - SAVE_AXY - SAVE_ZPS + pha + lda #CH_LEFT + sta SCRATCH1 + lda #CH_RIGHT + bra WGStrokeRect_common +WGStrokeRoundRect: + pha + lda #CH_ROUND_LEFT + sta SCRATCH1 + lda #CH_ROUND_RIGHT +WGStrokeRect_common: + sta SCRATCH2 + pla + SAVE_AXY + SAVE_ZPS ; Top and bottom edges ; @@ -374,7 +389,7 @@ WGStrokeRect_vertLoop: ; CASE 1: Left edge even-aligned, even width SETSWITCH PAGE2ON ldy #$0 - lda #CH_LEFT ; Plot the left edge + lda SCRATCH1 ; Plot the left edge sta (BASL),y lda PARAM2 ; Is width even? @@ -390,7 +405,7 @@ WGStrokeRect_vertLoop: dec tay SETSWITCH PAGE2OFF - lda #CH_RIGHT ; Plot the right edge + lda SCRATCH2 ; Plot the right edge sta (BASL),y jmp WGStrokeRect_vertLoopEvenAlignedNextRow @@ -402,7 +417,7 @@ WGStrokeRect_vertLoopEvenAlignedOddWidth: inc lsr tay - lda #CH_RIGHT ; Plot the right edge + lda SCRATCH2 ; Plot the right edge sta (BASL),y WGStrokeRect_vertLoopEvenAlignedNextRow: @@ -417,7 +432,7 @@ WGStrokeRect_vertLoopOdd: ; CASE 2: Left edge odd-aligned, even width SETSWITCH PAGE2OFF ldy #$0 - lda #CH_LEFT ; Plot the left edge + lda SCRATCH1 ; Plot the left edge sta (BASL),y lda PARAM2 ; Is width even? @@ -432,7 +447,7 @@ WGStrokeRect_vertLoopOdd: lsr tay SETSWITCH PAGE2ON - lda #CH_RIGHT ; Plot the right edge + lda SCRATCH2 ; Plot the right edge sta (BASL),y jmp WGStrokeRect_vertLoopOddAlignedNextRow @@ -444,7 +459,7 @@ WGStrokeRect_vertLoopOddAlignedOddWidth: inc lsr tay - lda #CH_RIGHT ; Plot the right edge + lda SCRATCH2 ; Plot the right edge sta (BASL),y WGStrokeRect_vertLoopOddAlignedNextRow: diff --git a/views.s b/views.s index b91375a..2b8dda1 100644 --- a/views.s +++ b/views.s @@ -156,6 +156,72 @@ WGCreateCheckbox_done: rts +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +; WGCreateRadio +; Creates a new radio button +; PARAM0: Pointer to configuration struct (LSB) +; PARAM1: Pointer to configuration struct (MSB) +; +; Configuration struct: +; ID: View ID (0-f) +; XX: Screen X origin +; YY: Screen Y origin +; SL: String pointer (LSB) +; SH: String pointer (MSB) +; +WGCreateRadio: + SAVE_AXY + + ldy #0 + lda (PARAM0),y ; Find our new view record + pha ; Cache view ID so we can select when we're done + + asl + asl + asl + asl ; Records are 16 bytes wide + tax + + iny + lda (PARAM0),y + sta WG_VIEWRECORDS+0,x ; Screen X + + iny + lda (PARAM0),y + sta WG_VIEWRECORDS+1,x ; Screen Y + + lda #1 + sta WG_VIEWRECORDS+2,x ; Initialize screen width + sta WG_VIEWRECORDS+3,x ; Initialize screen height + sta WG_VIEWRECORDS+7,x ; Initialize view width + sta WG_VIEWRECORDS+8,x ; Initialize view height + + lda #VIEW_STYLE_RADIO + sta WG_VIEWRECORDS+4,x ; Style + + stz WG_VIEWRECORDS+5,x ; Initialize scrolling + stz WG_VIEWRECORDS+6,x + + stz WG_VIEWRECORDS+9,x ; Initialize state + stz WG_VIEWRECORDS+10,x ; Initialize callback + stz WG_VIEWRECORDS+11,x + + iny + lda (PARAM0),y + sta WG_VIEWRECORDS+12,x ; Title + iny + lda (PARAM0),y + sta WG_VIEWRECORDS+13,x + + pla + jsr WGSelectView ; Leave this as the active view + +WGCreateRadio_done: + RESTORE_AXY + rts + + + ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; WGCreateProgress @@ -374,6 +440,8 @@ WGPaintView: pla ; Draw outline cmp #VIEW_STYLE_FANCY beq WGPaintView_decorated + cmp #VIEW_STYLE_RADIO + beq WGPaintView_radio jsr WGStrokeRect @@ -389,7 +457,10 @@ WGPaintView_decorated: jsr WGFancyRect jsr paintWindowTitle bra WGPaintView_done - + +WGPaintView_radio: + jsr WGStrokeRoundRect + ; execution falls through here WGPaintView_check: jsr paintCheck bra WGPaintView_done @@ -947,16 +1018,46 @@ WGViewFocusAction: SAVE_AXY lda WG_FOCUSVIEW - bmi WGViewFocusAction_done + bpl WGViewFocusAction_do + jmp WGViewFocusAction_done +WGViewFocusAction_do: LDY_FOCUSVIEW lda WG_VIEWRECORDS+4,y ; What kind of view is it? and #$f ; Mask off flag bits cmp #VIEW_STYLE_CHECK beq WGViewFocusAction_toggleCheckbox + cmp #VIEW_STYLE_RADIO + beq WGViewFocusAction_toggleRadio bra WGViewFocusAction_buttonClick ; Everything else treated like a button +WGViewFocusAction_toggleRadio: + lda #15 +WGViewFocusAction_toggleRadioLoop: + pha + cmp WG_FOCUSVIEW + beq WGViewFocusAction_toggleRadioLoopNext + LDY_AVIEW + lda WG_VIEWRECORDS+4,y + and #$f + cmp #VIEW_STYLE_RADIO + bne WGViewFocusAction_toggleRadioLoopNext + lda WG_VIEWRECORDS+9,y ; check if this radio button is selected + beq WGViewFocusAction_toggleRadioLoopNext + lda #0 + sta WG_VIEWRECORDS+9,y ; if so, deselect it and repaint + pla + pha + jsr WGSelectView + jsr WGPaintView +WGViewFocusAction_toggleRadioLoopNext: + pla + dec + bpl WGViewFocusAction_toggleRadioLoop + LDY_FOCUSVIEW + ; execution falls through here + WGViewFocusAction_toggleCheckbox: lda WG_VIEWRECORDS+9,y ; Change the checkbox's state and redraw eor #%00000001 diff --git a/weegui.dsk b/weegui.dsk index 9f47c4d34a696338517b2351a731157312d016a8..3efa95c49ca9bb3d2416317e9b9299a751397a0b 100644 GIT binary patch delta 5759 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 ZJ~aI3*(#Ii2cA2y49k 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