Qbasic Highlighted Menu - loops

Does anyone have an example code or instructions for making this work? I've just never been able to accomplish a highlighted menu that uses the arrow keys and enter for selections. Thanks in advance!
I anticipate this working by drawing boxes for each option, and redrawing the box in color while coloring the text when an option is selected. I'm just unsure how to design a loop to accomplish this. I'm pretty comfortable with the INKEY$ function and the SELECT CASE statement, but I don't know how to factor them in.

A highlighted menu will draw the menu and wait for a key press in a loop or using SLEEP. A common alternative was to simply change the text color of one of the first few letters, informing the user to press the corresponding key to select the corresponding menu option. For example, the letters Q in "Quit" and N in "New Game" would be a different color from the rest of the text in the line.
However, you're asking to use the arrow keys, so clearly you don't want to do it that way. How you highlight the current menu item depends on the screen mode in use. Screen modes 11, 12, and 13 don't allow you to specify a background color, and I can't get DOSBox to render a background with modes 7, 8, and 9. As a workaround to this issue, you could instead simply draw a box next to the current selection and erase the box (draw a black one or whatever your screen's background color is). Or you could just use an asterisk to avoid graphics/text size issues and simplify the code. Here's an example of the box style with arrow keys, WASD keys, and Vim-style keys (H=Left, J=Down, K=Up, L=Right) all supported, assuming a US-QWERTY keyboard is used. If you only wanted arrow keys, then you'd merely need to change the first (outer) SELECT CASE...END SELECT block to simply IF LEFT$(k$, 1) = CHR$(0) THEN...END IF while preserving the inner SELECT CASE...END SELECT block that works with extended keys.
'size% is used in the selIncDec subroutine.
DIM text$(0 TO 3)
DIM SHARED size%
size% = UBOUND(text$) - LBOUND(text$) + 1
selected% = 0
text$(0) = "Example 1"
text$(1) = "Example 2"
text$(2) = "Example 3"
text$(3) = "Example 4"
SCREEN 12
' Width and height of a text cell in pixels.
' I use 8x8 text cells for max screen compatibility, despite 8x16 looking better.
xpxText% = 8
ypxText% = 8
' See the documentation for SCREEN to determine which screen sizes are
' available with the screen mode you want to use.
' 80x60 for mode 12 results in 8x8 text cells. 80x30 results in 8x16 text cells.
WIDTH 80, 60
DO
LOCATE 1, 1
FOR i% = LBOUND(text$) TO UBOUND(text$)
PRINT TAB(3); text$(i%)
' selected% = i%
' is an equality comparison, resulting in -1 for true and 0 for false.
' If false, -(0) * 2 = 0; if true, -(-1) * 2 = 2.
LINE (0, i% * ypxText%)-STEP(xpxText% - 1, xpxText% - 1), -(selected% = i%) * 2, BF
NEXT i%
SLEEP
k$ = INKEY$
SELECT CASE UCASE$(LEFT$(k$, 1))
' Left -- does nothing
CASE "H", "A"
' Right -- does nothing
CASE "L", "D"
' Up
CASE "K", "W"
CALL selIncDec(selected%, -1)
' Down
CASE "J", "S"
CALL selIncDec(selected%, 1)
' Enter key
CASE CHR$(13)
EXIT DO
' Extended key, such as arrows.
CASE CHR$(0)
SELECT CASE RIGHT$(k$, 1)
' Left
CASE "K"
' Right
CASE "M"
' Up
CASE "H"
CALL selIncDec(selected%, -1)
' Down
CASE "P"
CALL selIncDec(selected%, 1)
END SELECT
END SELECT
LOOP
PRINT USING "You selected option #"; selected% + 1
END
SUB selIncDec (selected%, amtInc%)
selected% = selected% + amtInc%
IF selected% >= size% THEN
selected% = selected% - size%
ELSEIF selected% < 0 THEN
selected% = selected% + size%
END IF
END SUB
If you were using a screen mode that supports background colors or highlighting in some form, such as screen 0, you might be able to get away with simply "highlighting" the entire line's background in text mode. You need not specify the width of the screen to have a "reverse video" effect acting as highlighting, but it looks better when you have an entire line highlighted rather than just the text. After that menu item is printed, just change the colors back to the default and continue printing as usual. Below shows a few changes to the above code (screen mode, screen width setting, and menu display code), but it remains the same otherwise:
SCREEN 0
'8x8 text cells in SCREEN 0 for VGA adapters.
WIDTH 80, 43
...
FOR i% = LBOUND(text$) TO UBOUND(text$)
' "Reverse video" highlighting.
IF selected% = i% THEN COLOR 0, 7 ELSE COLOR 7, 0
PRINT TAB(3); text$(i%); SPACE$(78 - LEN(text$(i%)))
NEXT i%
' The screen will turn "white" when the last menu item is selected.
' This fixes the issue.
COLOR 7, 0
SLEEP
...
Note that I assumed a VGA adapter with a color display for all of the code above, which has long since been superseded by various other display adapter standards that are in use even on devices as small as smart watches.
You should be able to adapt the code to fit your needs. I designed it such that you could simply add menu items as you wished. Also, the display code itself is contained entirely in the FOR...NEXT loop with the functionality immediately following, so all you'd need to change is the stuff inside the FOR...NEXT loop to change how things are displayed.

I'm in the same boat, wanting to use SCREEN 12 for a menu, with background colour. As per http://www.qb64.net/wiki/index_title_COLOR/
SCREEN modes 12 and 13 can use the foreground parameter only in QB 4.5! Background color 0 can be changed using OUT.
I also came across this SUB:
SCREEN 12
ColourPrint "Hello", 4, 9
PRINT
ColourPrint "bob", 3, 10
SUB ColourPrint (t$, fg%, bg%)
' t$ = printing text
' fg% = foreground colour
' bg% = background colour
DIM h%(1 + 32 * LEN(t$))
x1% = 8 * (POS(0) - 1)
y1% = 16 * (CSRLIN - 1)
x2% = x1% + 8 * LEN(t$) - 1
y2% = y1% + 15
LINE (x1%, y1%)-(x2%, y2%), bg%, BF
GET (x1%, y1%)-(x2%, y2%), h%()
COLOR fg% XOR bg%
PRINT t$;
PUT (x1%, y1%), h%(), XOR
ERASE h%
END SUB
This site has a bunch of menu examples that might help too:
http://www.brisray.com/qbasic/qmenu.htm

Basically you need a loop that:
Keeps track of the current menu item selected (usually an index of the menu item: 1, 2, 3, ...) within a variable - let's call it SELECTED
Draws the menu on the screen with a highlight for the selected item (you'll need to iterate over all menu items and highlight when SELECTED = current menu item index)
Wait for a key to be pressed:
If the key is UP then select previous menu item (SELECTED = SELECTED - 1)
If the key is DOWN then select next menu item (SELECTED = SELECTED + 1)
If the key is ENTER then exit menu and return the selected item
I have a full example here but for a text-based menu. You can adapt it to your problem. The main functionality is on the "menu" function:
DECLARE FUNCTION countItems% (items$)
DECLARE FUNCTION menu% (row%, col%, items$)
DECLARE FUNCTION widestItemLength% (items$)
DEFINT A-Z
CONST SEPARATOR = ";"
CLS
selectedMenuOption = menu(1, 1, "APPLE;BANANA;ORANGE;EXIT")
PRINT
PRINT
PRINT "Selected menu option:"; selectedMenuOption
' Returns how much items we have in the menu
'
FUNCTION countItems (items$)
count = 1
DO
i = INSTR(i + 1, items$, SEPARATOR)
IF i > 0 THEN count = count + 1
LOOP UNTIL i = 0
countItems = count
END FUNCTION
' Main menu functionality
' 1. shows the menu highlighting the selected item
' 2. wait for a key to be pressed
' a. UP -> select previous item
' b. DOWN -> select next
' c. ENTER -> exit and returns the index of the current selected item
'
FUNCTION menu (row%, col%, items$)
widestLength = widestItemLength(items$)
itemCount = countItems(items$)
selected = 1 ' menu item index starting by 1
DO
' Prints the menu on the screen
itemEnd = 0
LOCATE row%, col%
FOR i = 1 TO itemCount
itemStart = itemEnd + 1
itemEnd = INSTR(itemStart, items$, SEPARATOR)
IF itemEnd = 0 THEN itemEnd = LEN(items$) + 1
item$ = MID$(items$, itemStart, itemEnd - itemStart)
item$ = " " + item$ + SPACE$(widestLength - LEN(item$)) + " "
IF selected = i THEN COLOR 0, 7 ELSE COLOR 7, 0
LOCATE CSRLIN + 1, col: PRINT item$;
NEXT
WHILE INKEY$ <> "": WEND ' Clears the keyboard buffer
DO: k$ = INKEY$: LOOP WHILE k$ = "" ' Waits for a key to be pressed
SELECT CASE k$
' Up key pressed - select previous menu item
CASE CHR$(0) + "H": IF selected > 1 THEN selected = selected - 1
' Down key pressed - select next menu item
CASE CHR$(0) + "P": IF selected < itemCount THEN selected = selected + 1
END SELECT
LOOP UNTIL k$ = CHR$(13) ' Loops until the key pressed is enter
menu = selected ' returns the selected menu item index
COLOR 7, 0
END FUNCTION
' Returns the size of the widest menu item
'
FUNCTION widestItemLength (items$)
widestLen = 0
itemEnd = 0
DO
itemStart = itemEnd + 1
itemEnd = INSTR(itemStart, items$, SEPARATOR)
IF itemEnd = 0 THEN itemEnd = LEN(items$) + 1
itemLen = itemEnd - itemStart
IF itemLen > widestLen THEN widestLen = itemLen
LOOP UNTIL itemEnd = LEN(items$) + 1
widestItemLength = widestLen
END FUNCTION

Related

Why is a random part of the code highlighted?

We have an online editor made by monaco-editor, here is the link: https://v3.10studio.tech/#/formula-editor-addin?app=formula-editor-addin. Users could enter an Excel formula like =1+2+3+4+5, then click on the Format button to see the formatted formula.
What is odd is that, after clicking on Format button, a random part of the formula is often highlighted in gray:
Does anyone know what may be the cause?
PS: The current options setting are as follows:
const monacoOptions: monacoEditor.editor.IEditorConstructionOptions = {
lineNumbers: 'off',
selectionHighlight: false,
glyphMargin: false, //left side,
lineDecorationsWidth: 0, // width between line number and content,
renderIndentGuides: false, // no indent guide lines
minimap: { enabled: false },
};
When you set the value of the model ie editor.getModel().setValue('FORMATTED-CODE') you should have to set the position of the cursor manually.
The selection is actually not random. Monaco will select all the extra texts you have added. For example -
Before : 1+2+3+4+5 - here last position is line 1 column 10
Format: 1 + 2 + 3 + 4 + 5 - here last position is line 1 column 18
So the extra text + 4 + 5 is selected, it means column 11 to 18 is selected
To set the position of the cursor at where it was before
const pos = editor.getPosition()
editor.getModel().setValue('FORMATTED-CODE')
editor.setPosition(pos)
To set the position of the cursor at Line 1 Column 1
editor.getModel().setValue('FORMATTED-CODE')
editor.setPosition({ lineNumber: 1, column: 1 })
To set the position of the cursor at last (using offset)
const formatted = 'FORMATTED-CODE'
const offset = formatted.length
const pos = editor.getModel().getPositionAt(offset)
editor.getModel().setValue(formatted)
editor.setPosition(pos)
You can also set selection
editor.setSelection({
startLineNumber: 1,
startColumn: 1,
endLineNumber: 1,
endColumn: 5,
})
For more informations you can follow these APIs -
setPosition - To set cursor position
setSelection - To set selection
setSelections - To set multiple positions & selections

Small Basic: How can I split words in an external text file into a list?

I'm trying to bring in a simple list of 10 words (without commas) on 10 lines and save them as a list or array in Small Basic.
I know I need to loop through all the lines in the file but I can only get it to do it with individual letters.
I've got this far so far
OpenFile = File.ReadContents("example.txt")
For i = 1 To Text.GetLength(OpenFile)
WordList[i] = Text.GetSubText(OpenFile, i, 5)
EndFor
TextWindow.Write(WordList)
I haven't got any further than this and not sure where to go to from here.
YOu could use readline to get all the characters/words/sentence in a line, this is an example, its not complete but it gives the idea of what it is you need,
'SAVE THE PROGRAM FIRST!!!!!
'DO NOT RUN UNTIL YOU DO THAT.
makesaves()
getsaves()
printsaves()
Sub makesaves ' where you make saves. its reusable.
PATH = Program.Directory + "\animals\" ' SAVE THIS IN A FOLDER YOU WILL FIND IN
'ELSE IT WILL SAVE IN A DUMP FOLDER WHICH IS NEARLY IMPOSSIBLE TO FIND
NAME = "Animal"
EXT = ".txt"
filesave["1"] = "Cheetah"
filesave[2] = "horse"
filesave[3] = "dog"
filesave[4] = "cat"
filesave[5] = "mouse"
filesave[6] = "turtle"
filesave[7] ="Bird"
filesave[8] = "snake"
filesave[9] = "snail"
filesave[10] = "Rat"
'makes the saves
File.CreateDirectory(PATH) ' makes the path.
File.WriteContents(PATH+NAME+EXT, filesave)
filesave = "" ' cleans the file so you dont get repeats. e.i. - save dog. read dog, save dog, read dog dog.
'this makes it so you see dog once. its an override.
filesave = File.ReadContents(PATH + NAME + EXT) 'reads the content
endsub
Sub getsaves
filesave = File.ReadContents(PATH+NAME+EXT) ' how this writes is cheetah; horse;
cheetah = filesave[1]
horse = filesave[2]
dog = filesave[3]
cat = filesave[4]
mouse = filesave[5] 'mouse and turtle as different color because they can be used as functions. ignore
turtle = filesave[6]
bird = filesave[7]
snake = filesave[8]
snail = filesave[9]
rat = filesave[10]
EndSub
Sub printsaves
i = 1
While i < 11
TextWindow.WriteLine(filesave[i])
i = i+1
endwhile
endsub
I know I'm probably much too late but in case you're still going (and yes, I'm going to assume that you're not taking an AQA GCSE in Computer Science but instead like to code in Small Basic for fun), but you should be looking at using this code instead as this is much more efficient.
fpath = "\\Downloads\file.txt"
For i = 1 To 10
line[i] = File.ReadLine(fpath, i)
EndFor
For i = 1 To 10
TextWindow.WriteLine("Line " + i + " contains: " + line[i])
EndFor
(You'll need to change the fpath variable to wherever your file is). This then also prints out the array just to check but for your task you'll need to get rid of that.

Using 2D Array for Tic Tac Toe in Visual Basic

I have to make a tic tac toe game for my Visual Basic class that uses an array to keep track of the state of the game, but I have no idea how to set up this array. The program is supposed to have 2 views (a GUI which I have already done, and a text view using the console which I have no idea how to do), has two controllers (the user can click the button where they want to play, or they can use the keys 1-9 on the keyboard to choose their position), and is to be played between a human and the computer.
It's not much, but here's what I have so far:
Module Module1
Const intMAX_ROWS As Integer = 2
Const intMAX_COL As Integer = 2
Public gameArray(intMAX_ROWS, intMAX_COL) As String
Button1.Text = gameArray(0,0)
Button2.Text = gameArray(0,1)
Button3.Text = gameArray(0,2)
Button4.Text = gameArray(1,0)
Button5.Text = gameArray(1,1)
Button6.Text = gameArray(1,2)
Button7.Text = gameArray(2,0)
Button8.Text = gameArray(2,1)
Button9.Text = gameArray(2,2)
End Module
I am getting an error on all of the Button.Text lines saying
Declaration expected
Any ideas on how to fix that?
Any help or suggestions would be greatly appreciated.
For the program to compile the statements where you assign values to buttons need to be inside a function:
Sub SetButtons()
Button1.Text = gameArray(0, 0)
Button2.Text = gameArray(0, 1)
Button3.Text = gameArray(0, 2)
Button4.Text = gameArray(1, 0)
Button5.Text = gameArray(1, 1)
Button6.Text = gameArray(1, 2)
Button7.Text = gameArray(2, 0)
Button8.Text = gameArray(2, 1)
Button9.Text = gameArray(2, 2)
End Sub
As you need 2 views, a GUI and a text view, I'd recommend ending up with 3 projects:
a TicTacToe library for the game logic (to be shared)
a WinForms or WPF application for the GUI view (that references the library)
a Console application for the text view (that references the library)
Here's an example of drawing a board in a console application:
Dim board(3, 3) As Char
' Set a O in the middle
board(1, 1) = "O"
' Set an X at the bottom right
board(2, 2) = "X"
' Show board
Console.WriteLine(board(0, 0) + "|" + board(1, 0) + "|" + board(2, 0))
Console.WriteLine("-----")
Console.WriteLine(board(0, 1) + "|" + board(1, 1) + "|" + board(2, 1))
Console.WriteLine("-----")
Console.WriteLine(board(0, 2) + "|" + board(1, 2) + "|" + board(2, 2))
which gives:
| |
-----
|O|
-----
| |X
For a little inspiration on the GUI side, here's a short Silverlight sample (written in F#): Tic-Tac-Toe

how can i use checkboxes which gets value from database in a loop?

I have a table in database.In that table , there is an unknown number of rows.I want to put
checkboxes but number of checkboxes should be equal to the Number of rows. Thus i tried to do it in while loop. I found number of rows which exists in table.It's rowNumber.Then , i did a while loop.At this point , there is a serial failure. Because when i clicked any checkbox , the row below is called with last m value.
cbox[m] = checkBox.new(checkX, txtY, sendRows[m], col,
Because after exiting while loop , m equals rowNumber. And when any checkboxes is clicked , it doesn't know which row is clicked because m = rowNumber. I tried many things but i can't do it. What should i do ?? Should i give up using loop? IF i give up using it how can do it?
Here is the code which i can't fixed
local m = 1
while(m<rowNumber)
cbox[m] = checkBox.new(checkX, txtY, sendRows[m], col,
function(this)
local state = this:getCheck()
if state then
print(m .. " checked")
gonder[m] = 1
else
print(m .. " does not checked")
gonder[m] = 0
end
end
)
screen:addChild(cbox[m])
cbox[m]:setCheck(settings.getMusicState())
txtY = txtY + gap
print ("gonder[m] = " .. gonder[m])
end
If the code is written as in the below,it runs correctly:
cbox[1] = checkBox.new(checkX, txtY, sendRows[1], col,
function(this)
local state = this:getCheck()
if state then
print(1.. " checked")
gonder[1] = 1
else
print(1 .. " does not checked")
gonder[1] = 0
end
end
)
screen:addChild(cbox[1])
cbox[1]:setCheck(settings.getMusicState())
txtY = txtY + gap
cbox[2] = checkBox.new(checkX, txtY, sendRows[2], col,
function(this)
local state = this:getCheck()
if state then
print(2.. " checked")
gonder[2] = 1
else
print(2 .. " does not checked")
gonder[2] = 0
end
end
)
screen:addChild(cbox[2])
cbox[2]:setCheck(settings.getMusicState())
txtY = txtY + gap
.
.
.
.
It is solved after changing the inside of while as this:
while( m<rowNumber + 1)
do
cbox[m] = checkBox.new(checkX, txtY, sendRows[m], col,
function(this)
end
)
screen:addChild(cbox[m])
txtY = txtY + gap
m = m + 1
end
And the important point is here. Here , we are sending what we want:
submitt:addEventListener("click",
function()
local mydata={}
local i=1
while (i<rowNumber+1) do
mydata[3*i-2]=cbox[i].checked--1. Holds whether it is true or false
mydata[3*i-1]=sendRows2[i]
mydata[3*i] = sendURL[i]
i=i+1
end
sceneManager:changeScene("results", 1, SceneManager.flipWithFade, easing.outBack,mydata)
stage:removeChild(submitt)--after clicked , the button disappears
end)

Getting current position of one of the multiple objects in a figure?

I wrote a script that returns several text boxes in a figure. The text boxes are moveable (I can drag and drop them), and their positions are predetermined by the data in an input matrix (the data from the input matrix is applied to the respective positions of the boxes by nested for loop). I want to create a matrix which is initially a copy of the input matrix, but is UPDATED as I change the positions of the boxes by dragging them around. How would I update their positions? Here's the entire script
function drag_drop=drag_drop(tsinput,infoinput)
[x,~]=size(tsinput);
dragging = [];
orPos = [];
fig = figure('Name','Docker Tool','WindowButtonUpFcn',#dropObject,...
'units','centimeters','WindowButtonMotionFcn',#moveObject,...
'OuterPosition',[0 0 25 30]);
% Setting variables to zero for the loop
plat_qty=0;
time_qty=0;
k=0;
a=0;
% Start loop
z=1:2
for idx=1:x
if tsinput(idx,4)==1
color='red';
else
color='blue';
end
a=tsinput(idx,z);
b=a/100;
c=floor(b); % hours
d=c*100;
e=a-d; % minutes
time=c*60+e; % time quantity to be used in 'position'
time_qty=time/15;
plat_qty=tsinput(idx,3)*2;
box=annotation('textbox','units','centimeters','position',...
[time_qty plat_qty 1.5 1.5],'String',infoinput(idx,z),...
'ButtonDownFcn',#dragObject,'BackgroundColor',color);
% need to new=get(box,'Position'), fill out matrix OUT of loop
end
fillmenu=uicontextmenu;
hcb1 = 'set(gco, ''BackgroundColor'', ''red'')';
hcb2 = 'set(gco, ''BackgroundColor'', ''blue'')';
item1 = uimenu(fillmenu, 'Label', 'Train Full', 'Callback', hcb1);
item2 = uimenu(fillmenu, 'Label', 'Train Empty', 'Callback', hcb2);
hbox=findall(fig,'Type','hggroup');
for jdx=1:x
set(hbox(jdx),'uicontextmenu',fillmenu);
end
end
new_arr=tsinput;
function dragObject(hObject,eventdata)
dragging = hObject;
orPos = get(gcf,'CurrentPoint');
end
function dropObject(hObject,eventdata,box)
if ~isempty(dragging)
newPos = get(gcf,'CurrentPoint');
posDiff = newPos - orPos;
set(dragging,'Position',get(dragging,'Position') + ...
[posDiff(1:2) 0 0]);
dragging = [];
end
end
function moveObject(hObject,eventdata)
if ~isempty(dragging)
newPos = get(gcf,'CurrentPoint');
posDiff = newPos - orPos;
orPos = newPos;
set(dragging,'Position',get(dragging,'Position') + [posDiff(1:2) 0 0]);
end
end
end
% Testing purpose input matrices:
% tsinput=[0345 0405 1 1 ; 0230 0300 2 0; 0540 0635 3 1; 0745 0800 4 1]
% infoinput={'AJ35 NOT' 'KL21 MAN' 'XPRES'; 'ZW31 MAN' 'KM37 NEW' 'VISTA';
% 'BC38 BIR' 'QU54 LON' 'XPRES'; 'XZ89 LEC' 'DE34 MSF' 'DERP'}
If I understand you correctly (and please post some code if I'm not), then all you need is indeed a set/get combination.
If boxHandle is a handle to the text-box object, then you get its current position by:
pos = get (boxHandle, 'position')
where pos is the output array of [x, y, width, height].
In order to set to a new position, you use:
set (boxHandle, 'position', newPos)
where newPos is the array of desired position (with the same structure as pos).
EDIT
Regarding to updating your matrix, since you have the handle of the object you move, you actually DO have access to the specific text box.
When you create each text box, set a property called 'UserData' with the associated indices of tsinput used for that box. In your nested for loop add this
set (box, 'UserData', [idx, z]);
after the box is created, and in your moveObject callback get the data by
udata = get(dragging,'UserData');
Then udata contains the indices of the elements you want to update.

Resources