mirror of
https://github.com/irmen/prog8.git
synced 2025-01-10 20:30:23 +00:00
improving fileselector
This commit is contained in:
parent
c84cc8f8c9
commit
10b9162dc5
@ -1,18 +1,18 @@
|
||||
<component name="libraryTable">
|
||||
<library name="michael.bull.kotlin.result.jvm" type="repository">
|
||||
<properties maven-id="com.michael-bull.kotlin-result:kotlin-result-jvm:2.0.0" />
|
||||
<properties maven-id="com.michael-bull.kotlin-result:kotlin-result-jvm:2.0.1" />
|
||||
<CLASSES>
|
||||
<root url="jar://$MAVEN_REPOSITORY$/com/michael-bull/kotlin-result/kotlin-result-jvm/2.0.0/kotlin-result-jvm-2.0.0.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/com/michael-bull/kotlin-result/kotlin-result-jvm/2.0.1/kotlin-result-jvm-2.0.1.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib/1.9.22/kotlin-stdlib-1.9.22.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/annotations/13.0/annotations-13.0.jar!/" />
|
||||
</CLASSES>
|
||||
<JAVADOC>
|
||||
<root url="jar://$MAVEN_REPOSITORY$/com/michael-bull/kotlin-result/kotlin-result-jvm/2.0.0/kotlin-result-jvm-2.0.0-javadoc.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/com/michael-bull/kotlin-result/kotlin-result-jvm/2.0.1/kotlin-result-jvm-2.0.1-javadoc.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib/1.9.22/kotlin-stdlib-1.9.22-javadoc.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/annotations/13.0/annotations-13.0-javadoc.jar!/" />
|
||||
</JAVADOC>
|
||||
<SOURCES>
|
||||
<root url="jar://$MAVEN_REPOSITORY$/com/michael-bull/kotlin-result/kotlin-result-jvm/2.0.0/kotlin-result-jvm-2.0.0-sources.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/com/michael-bull/kotlin-result/kotlin-result-jvm/2.0.1/kotlin-result-jvm-2.0.1-sources.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib/1.9.22/kotlin-stdlib-1.9.22-sources.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/annotations/13.0/annotations-13.0-sources.jar!/" />
|
||||
</SOURCES>
|
||||
|
@ -7,7 +7,7 @@ plugins {
|
||||
dependencies {
|
||||
// should have no dependencies to other modules
|
||||
// implementation("org.jetbrains.kotlin:kotlin-stdlib-jdk8")
|
||||
implementation("com.michael-bull.kotlin-result:kotlin-result-jvm:2.0.0")
|
||||
implementation("com.michael-bull.kotlin-result:kotlin-result-jvm:2.0.1")
|
||||
}
|
||||
|
||||
sourceSets {
|
||||
@ -21,4 +21,4 @@ sourceSets {
|
||||
}
|
||||
}
|
||||
|
||||
// note: there are no unit tests in this module!
|
||||
// note: there are no unit tests in this module!
|
||||
|
@ -8,7 +8,7 @@ dependencies {
|
||||
implementation(project(":codeCore"))
|
||||
// implementation("org.jetbrains.kotlin:kotlin-stdlib-jdk8")
|
||||
// implementation "org.jetbrains.kotlin:kotlin-reflect"
|
||||
implementation("com.michael-bull.kotlin-result:kotlin-result-jvm:2.0.0")
|
||||
implementation("com.michael-bull.kotlin-result:kotlin-result-jvm:2.0.1")
|
||||
|
||||
testImplementation("io.kotest:kotest-runner-junit5-jvm:5.9.1")
|
||||
testImplementation("io.kotest:kotest-framework-datatest:5.9.1")
|
||||
@ -43,4 +43,4 @@ tasks.test {
|
||||
testLogging {
|
||||
events("skipped", "failed")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -10,7 +10,7 @@ dependencies {
|
||||
implementation(project(":codeGenIntermediate"))
|
||||
// implementation("org.jetbrains.kotlin:kotlin-stdlib-jdk8")
|
||||
// implementation "org.jetbrains.kotlin:kotlin-reflect"
|
||||
implementation("com.michael-bull.kotlin-result:kotlin-result-jvm:2.0.0")
|
||||
implementation("com.michael-bull.kotlin-result:kotlin-result-jvm:2.0.1")
|
||||
}
|
||||
|
||||
sourceSets {
|
||||
@ -24,4 +24,4 @@ sourceSets {
|
||||
}
|
||||
}
|
||||
|
||||
// note: there are no unit tests in this module!
|
||||
// note: there are no unit tests in this module!
|
||||
|
@ -10,7 +10,7 @@ dependencies {
|
||||
implementation(project(":intermediate"))
|
||||
// implementation("org.jetbrains.kotlin:kotlin-stdlib-jdk8")
|
||||
// implementation "org.jetbrains.kotlin:kotlin-reflect"
|
||||
implementation("com.michael-bull.kotlin-result:kotlin-result-jvm:2.0.0")
|
||||
implementation("com.michael-bull.kotlin-result:kotlin-result-jvm:2.0.1")
|
||||
|
||||
testImplementation("io.kotest:kotest-runner-junit5-jvm:5.9.1")
|
||||
testImplementation("org.junit.jupiter:junit-jupiter:5.9.1")
|
||||
@ -45,4 +45,4 @@ tasks.test {
|
||||
testLogging {
|
||||
events("skipped", "failed")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -8,7 +8,7 @@ dependencies {
|
||||
implementation(project(":codeCore"))
|
||||
implementation(project(":compilerAst"))
|
||||
// implementation("org.jetbrains.kotlin:kotlin-stdlib-jdk8")
|
||||
implementation("com.michael-bull.kotlin-result:kotlin-result-jvm:2.0.0")
|
||||
implementation("com.michael-bull.kotlin-result:kotlin-result-jvm:2.0.1")
|
||||
// implementation "org.jetbrains.kotlin:kotlin-reflect"
|
||||
}
|
||||
|
||||
@ -23,4 +23,4 @@ sourceSets {
|
||||
}
|
||||
}
|
||||
|
||||
// note: there are no unit tests in this module!
|
||||
// note: there are no unit tests in this module!
|
||||
|
@ -21,7 +21,7 @@ dependencies {
|
||||
// implementation("org.jetbrains.kotlin:kotlin-stdlib-jdk8")
|
||||
// implementation("org.jetbrains.kotlin:kotlin-reflect")
|
||||
implementation("org.jetbrains.kotlinx:kotlinx-cli:0.3.6")
|
||||
implementation("com.michael-bull.kotlin-result:kotlin-result-jvm:2.0.0")
|
||||
implementation("com.michael-bull.kotlin-result:kotlin-result-jvm:2.0.1")
|
||||
|
||||
testImplementation(project(":codeCore"))
|
||||
testImplementation(project(":intermediate"))
|
||||
@ -94,4 +94,4 @@ tasks.compileKotlin {
|
||||
|
||||
tasks.compileJava {
|
||||
dependsOn(tasks.createVersionFile)
|
||||
}
|
||||
}
|
||||
|
@ -162,6 +162,9 @@ io_error:
|
||||
; After the last filename one additional 0 byte is placed to indicate the end of the list.
|
||||
; Returns number of files (it skips 'dir' entries i.e. subdirectories).
|
||||
; Also sets carry on exit: Carry clear = all files returned, Carry set = directory has more files that didn't fit in the buffer.
|
||||
; Note that no list of pointers of some form is returned, the names are just squashed together.
|
||||
; If you really need a list of pointers to the names, that is pretty straightforward to construct by iterating over the names
|
||||
; and registering when the next one starts after the 0-byte separator.
|
||||
uword buffer_start = filenames_buffer
|
||||
ubyte files_found = 0
|
||||
filenames_buffer[0]=0
|
||||
|
@ -140,7 +140,10 @@ io_error:
|
||||
; After the last filename one additional 0 byte is placed to indicate the end of the list.
|
||||
; Returns number of files (it skips 'dir' entries i.e. subdirectories).
|
||||
; Also sets carry on exit: Carry clear = all files returned, Carry set = directory has more files that didn't fit in the buffer.
|
||||
uword buffer_start = filenames_buffer
|
||||
; Note that no list of pointers of some form is returned, the names are just squashed together.
|
||||
; If you really need a list of pointers to the names, that is pretty straightforward to construct by iterating over the names
|
||||
; and registering when the next one starts after the 0-byte separator.
|
||||
uword buffer_start = filenames_buffer
|
||||
ubyte files_found = 0
|
||||
if lf_start_list(pattern_ptr) {
|
||||
while lf_next_entry() {
|
||||
|
@ -24,6 +24,9 @@ diskio {
|
||||
; After the last filename one additional 0 byte is placed to indicate the end of the list.
|
||||
; Returns number of files (it skips 'dir' entries i.e. subdirectories).
|
||||
; Also sets carry on exit: Carry clear = all files returned, Carry set = directory has more files that didn't fit in the buffer.
|
||||
; Note that no list of pointers of some form is returned, the names are just squashed together.
|
||||
; If you really need a list of pointers to the names, that is pretty straightforward to construct by iterating over the names
|
||||
; and registering when the next one starts after the 0-byte separator.
|
||||
txt.print("@TODO: list_filenames\n")
|
||||
sys.clear_carry()
|
||||
return 0
|
||||
|
@ -8,7 +8,7 @@ dependencies {
|
||||
implementation(project(":codeCore"))
|
||||
// implementation("org.jetbrains.kotlin:kotlin-stdlib-jdk8")
|
||||
implementation("org.antlr:antlr4-runtime:4.13.2")
|
||||
implementation("com.michael-bull.kotlin-result:kotlin-result-jvm:2.0.0")
|
||||
implementation("com.michael-bull.kotlin-result:kotlin-result-jvm:2.0.1")
|
||||
implementation(project(":parser"))
|
||||
}
|
||||
|
||||
@ -22,4 +22,4 @@ sourceSets {
|
||||
srcDir("${project.projectDir}/src")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -589,6 +589,7 @@ private fun ExpressionContext.toAst(insideParentheses: Boolean=false) : Expressi
|
||||
val addressOf = addressof()
|
||||
val identifier = addressOf.scoped_identifier()
|
||||
val msb = addressOf.ADDRESS_OF_MSB()!=null
|
||||
// note: &< (ADDRESS_OF_LSB) is equivalent to a regular &.
|
||||
return if (identifier != null)
|
||||
AddressOf(addressof().scoped_identifier().toAst(), null, msb, toPosition())
|
||||
else {
|
||||
|
@ -965,6 +965,7 @@ Provides string manipulation routines.
|
||||
``pattern_match (string, pattern) -> bool`` (not on Virtual target)
|
||||
Returns true if the string matches the pattern, false if not.
|
||||
'?' in the pattern matches any one character. '*' in the pattern matches any substring.
|
||||
An empty pattern matches nothing. If you need everything to match, use a single '*'.
|
||||
|
||||
``hash (string) -> ubyte``
|
||||
Returns a simple 8 bit hash value for the given string.
|
||||
|
@ -1,16 +1,19 @@
|
||||
%import diskio
|
||||
%import textio
|
||||
%import strings
|
||||
%zeropage basicsafe
|
||||
|
||||
; A "GUI" for an interactive file selector, that scrolls the selection list if it doesn't fit on the screen.
|
||||
; A "TUI" for an interactive file selector, that scrolls the selection list if it doesn't fit on the screen.
|
||||
; Depends a lot on diskio routines, and uses the drive set in the diskio.drivenumber variable (usually just 8)
|
||||
|
||||
; TODO sort entries alphabetically?
|
||||
; TODO sort entries alphabetically? Or not, because C64/C128 directories tend to be not very large.
|
||||
|
||||
|
||||
main {
|
||||
sub start() {
|
||||
|
||||
uword chosen = fileselector.select(8)
|
||||
;; fileselector.configure(1, 5, 5)
|
||||
uword chosen = fileselector.select("*")
|
||||
txt.nl()
|
||||
if chosen!=0 {
|
||||
txt.print("\nchosen: ")
|
||||
@ -25,96 +28,102 @@ main {
|
||||
|
||||
fileselector {
|
||||
|
||||
uword filenamesbuf
|
||||
const uword filenamesbuf_size = $1000
|
||||
const ubyte DIALOG_TOPX = 4
|
||||
const ubyte DIALOG_TOPY = 1
|
||||
const ubyte MAX_LINES = 15
|
||||
uword filenamesbuffer
|
||||
uword filename_ptrs_start ; array of 127 string pointers for each of the names in the buffer. ends with $0000.
|
||||
const uword filenamesbuf_size = $0f00
|
||||
ubyte dialog_topx = 4
|
||||
ubyte dialog_topy = 1
|
||||
ubyte max_lines = 15
|
||||
|
||||
str chosen_filename = "?" * 32
|
||||
uword name_ptr
|
||||
ubyte num_visible_files
|
||||
|
||||
sub select(ubyte drive) -> uword {
|
||||
sub configure(ubyte column, ubyte row, ubyte max_entries) {
|
||||
dialog_topx = column
|
||||
dialog_topy = row
|
||||
max_lines = max_entries
|
||||
}
|
||||
|
||||
sub select(str pattern) -> uword {
|
||||
if sys.target==64 {
|
||||
; c64 has memory at $c000
|
||||
filenamesbuf = $c000
|
||||
filenamesbuffer = $c000
|
||||
filename_ptrs_start = $cf00
|
||||
} else if sys.target==128 {
|
||||
; c128 has memory up to $c000 (if you use prog8's init code)
|
||||
filenamesbuf = $b000
|
||||
filenamesbuffer = $b000
|
||||
filename_ptrs_start = $bf00
|
||||
} else {
|
||||
; assume PET32, memory upto $8000
|
||||
filenamesbuf = $7000
|
||||
filenamesbuffer = $7000
|
||||
filename_ptrs_start = $7f00
|
||||
}
|
||||
|
||||
num_visible_files = 0
|
||||
chosen_filename[0] = 0
|
||||
diskio.drivenumber = drive
|
||||
name_ptr = diskio.diskname()
|
||||
if name_ptr==0 or cbm.READST()!=0
|
||||
return 0
|
||||
|
||||
txt.cls()
|
||||
txt.plot(DIALOG_TOPX, DIALOG_TOPY)
|
||||
txt.plot(dialog_topx, dialog_topy)
|
||||
txt.print("┌")
|
||||
linepart()
|
||||
txt.print("┐\n")
|
||||
txt.column(DIALOG_TOPX)
|
||||
txt.column(dialog_topx)
|
||||
txt.print("│ drive ")
|
||||
txt.print_ub(diskio.drivenumber)
|
||||
txt.print(": '")
|
||||
txt.print(name_ptr)
|
||||
txt.chrout('\'')
|
||||
txt.column(DIALOG_TOPX+31)
|
||||
txt.column(dialog_topx+31)
|
||||
txt.print("│\n")
|
||||
txt.column(DIALOG_TOPX)
|
||||
txt.column(dialog_topx)
|
||||
txt.print("│ scanning directory... │\n")
|
||||
txt.column(DIALOG_TOPX)
|
||||
txt.column(dialog_topx)
|
||||
footerline()
|
||||
|
||||
ubyte num_files = diskio.list_filenames(0, filenamesbuf, filenamesbuf_size) ; use Hiram bank to store the files
|
||||
ubyte num_files = diskio.list_filenames(pattern, filenamesbuffer, filenamesbuf_size) ; use Hiram bank to store the files
|
||||
ubyte selected_line
|
||||
ubyte top_index
|
||||
uword[MAX_LINES] visible_names
|
||||
uword filename_ptrs
|
||||
|
||||
txt.plot(DIALOG_TOPX+2, DIALOG_TOPY+2)
|
||||
construct_name_ptr_array()
|
||||
; initial display
|
||||
txt.plot(dialog_topx+2, dialog_topy+2)
|
||||
txt.print("select file: (")
|
||||
txt.print_ub(num_files)
|
||||
txt.print(" total)")
|
||||
txt.column(DIALOG_TOPX+31)
|
||||
txt.column(dialog_topx+31)
|
||||
txt.print("│\n")
|
||||
txt.column(DIALOG_TOPX)
|
||||
txt.column(dialog_topx)
|
||||
txt.print("│ stop or q to abort |\n")
|
||||
txt.column(DIALOG_TOPX)
|
||||
txt.column(dialog_topx)
|
||||
txt.print("├")
|
||||
linepart()
|
||||
txt.print("┤\n")
|
||||
print_up_indicator(false)
|
||||
name_ptr = filenamesbuf
|
||||
for selected_line in 0 to min(MAX_LINES, num_files)-1 {
|
||||
if cbm.STOP2() break
|
||||
|
||||
txt.column(DIALOG_TOPX)
|
||||
txt.print("│ ")
|
||||
|
||||
visible_names[selected_line] = name_ptr ; keep pointer to name
|
||||
while @(name_ptr)!=0 {
|
||||
txt.chrout(@(name_ptr))
|
||||
name_ptr++
|
||||
if num_files>0 {
|
||||
for selected_line in 0 to min(max_lines, num_files)-1 {
|
||||
txt.column(dialog_topx)
|
||||
txt.print("│ ")
|
||||
print_filename(peekw(filename_ptrs_start+selected_line*$0002))
|
||||
txt.column(dialog_topx+31)
|
||||
txt.print("│\n")
|
||||
num_visible_files++
|
||||
}
|
||||
txt.column(DIALOG_TOPX+31)
|
||||
} else {
|
||||
txt.column(dialog_topx)
|
||||
txt.print("│ no matches.")
|
||||
txt.column(dialog_topx+31)
|
||||
txt.print("│\n")
|
||||
name_ptr++
|
||||
num_visible_files++
|
||||
}
|
||||
print_down_indicator(false)
|
||||
txt.column(DIALOG_TOPX)
|
||||
txt.column(dialog_topx)
|
||||
footerline()
|
||||
|
||||
name_ptr = filenamesbuf
|
||||
selected_line = 0
|
||||
invert(selected_line)
|
||||
|
||||
print_up_and_down()
|
||||
|
||||
repeat {
|
||||
@ -125,41 +134,48 @@ fileselector {
|
||||
when key {
|
||||
3, 27, 'q' -> return 0 ; STOP or Q aborts (and ESC?)
|
||||
'\n',' ' -> {
|
||||
void strings.copy(visible_names[selected_line], chosen_filename)
|
||||
return chosen_filename
|
||||
if num_files>0 {
|
||||
void strings.copy(peekw(filename_ptrs_start + (top_index+selected_line)*$0002), chosen_filename)
|
||||
return chosen_filename
|
||||
}
|
||||
return 0
|
||||
}
|
||||
'[' -> {
|
||||
'[', 157 -> { ; cursor left
|
||||
; previous page of lines
|
||||
invert(selected_line)
|
||||
if selected_line==0
|
||||
repeat MAX_LINES scroll_list_backward()
|
||||
repeat max_lines scroll_list_backward()
|
||||
selected_line = 0
|
||||
invert(selected_line)
|
||||
print_up_and_down()
|
||||
}
|
||||
']' -> {
|
||||
; next page of lines
|
||||
invert(selected_line)
|
||||
if selected_line == MAX_LINES-1
|
||||
repeat MAX_LINES scroll_list_forward()
|
||||
selected_line = num_visible_files-1
|
||||
invert(selected_line)
|
||||
print_up_and_down()
|
||||
']', 29 -> { ; cursor right
|
||||
if num_files>0 {
|
||||
; next page of lines
|
||||
invert(selected_line)
|
||||
if selected_line == max_lines-1
|
||||
repeat max_lines scroll_list_forward()
|
||||
selected_line = num_visible_files-1
|
||||
invert(selected_line)
|
||||
print_up_and_down()
|
||||
}
|
||||
}
|
||||
17 -> { ; down
|
||||
invert(selected_line)
|
||||
if selected_line<num_visible_files-1
|
||||
selected_line++
|
||||
else if num_files>MAX_LINES
|
||||
scroll_list_forward()
|
||||
invert(selected_line)
|
||||
print_up_and_down()
|
||||
if num_files>0 {
|
||||
invert(selected_line)
|
||||
if selected_line<num_visible_files-1
|
||||
selected_line++
|
||||
else if num_files>max_lines
|
||||
scroll_list_forward()
|
||||
invert(selected_line)
|
||||
print_up_and_down()
|
||||
}
|
||||
}
|
||||
145 -> { ; up
|
||||
invert(selected_line)
|
||||
if selected_line>0
|
||||
selected_line--
|
||||
else if num_files>MAX_LINES
|
||||
else if num_files>max_lines
|
||||
scroll_list_backward()
|
||||
invert(selected_line)
|
||||
print_up_and_down()
|
||||
@ -169,25 +185,40 @@ fileselector {
|
||||
|
||||
ubyte x,y
|
||||
|
||||
sub scroll_list_forward() {
|
||||
if top_index+MAX_LINES< num_files {
|
||||
top_index++
|
||||
; scroll the displayed list up 1
|
||||
scroll_txt_up(DIALOG_TOPX+2, DIALOG_TOPY+6, 28, MAX_LINES, sc:' ')
|
||||
; shift the name pointer array 1 to the left
|
||||
for y in 0 to MAX_LINES-2
|
||||
visible_names[y]=visible_names[y+1]
|
||||
|
||||
; set name pointer array last element with newly appeared name
|
||||
name_ptr = visible_names[MAX_LINES-1]
|
||||
sub construct_name_ptr_array() {
|
||||
filename_ptrs = filename_ptrs_start
|
||||
name_ptr = filenamesbuffer
|
||||
repeat num_files {
|
||||
pokew(filename_ptrs, name_ptr)
|
||||
while @(name_ptr)!=0
|
||||
name_ptr++
|
||||
name_ptr++
|
||||
visible_names[MAX_LINES-1] = name_ptr
|
||||
filename_ptrs+=2
|
||||
}
|
||||
pokew(filename_ptrs, 0)
|
||||
}
|
||||
|
||||
; print that new name at the bottom of the list
|
||||
txt.plot(DIALOG_TOPX+2, DIALOG_TOPY+6+MAX_LINES-1)
|
||||
txt.print(name_ptr)
|
||||
sub print_filename(uword name) {
|
||||
repeat 28 { ; maximum length displayed
|
||||
cx16.r0L = @(name)
|
||||
if_z
|
||||
break
|
||||
if strings.isprint(cx16.r0L) ; filter out control characters
|
||||
txt.chrout(@(name))
|
||||
else
|
||||
txt.chrout('?')
|
||||
name++
|
||||
}
|
||||
}
|
||||
|
||||
sub scroll_list_forward() {
|
||||
if top_index+max_lines< num_files {
|
||||
top_index++
|
||||
; scroll the displayed list up 1
|
||||
scroll_txt_up(dialog_topx+2, dialog_topy+6, 28, max_lines, sc:' ')
|
||||
; print new name at the bottom of the list
|
||||
txt.plot(dialog_topx+2, dialog_topy+6+max_lines-1)
|
||||
print_filename(peekw(filename_ptrs_start + (top_index+ selected_line)*$0002))
|
||||
}
|
||||
}
|
||||
|
||||
@ -195,22 +226,10 @@ fileselector {
|
||||
if top_index>0 {
|
||||
top_index--
|
||||
; scroll the displayed list down 1
|
||||
scroll_txt_down(DIALOG_TOPX+2, DIALOG_TOPY+6, 28, MAX_LINES, sc:' ')
|
||||
; shift the name pointer array 1 to the right
|
||||
for y in MAX_LINES-1 downto 1
|
||||
visible_names[y]=visible_names[y-1]
|
||||
|
||||
; set name pointer array element 0 with newly appeared name
|
||||
name_ptr = visible_names[0]-2
|
||||
while name_ptr>filenamesbuf and @(name_ptr)!=0
|
||||
name_ptr--
|
||||
if @(name_ptr)==0
|
||||
name_ptr++
|
||||
visible_names[0] = name_ptr
|
||||
|
||||
; print that new name at the top of the list
|
||||
txt.plot(DIALOG_TOPX+2, DIALOG_TOPY+6)
|
||||
txt.print(name_ptr)
|
||||
scroll_txt_down(dialog_topx+2, dialog_topy+6, 28, max_lines, sc:' ')
|
||||
; print new name at the top of the list
|
||||
txt.plot(dialog_topx+2, dialog_topy+6)
|
||||
print_filename(peekw(filename_ptrs_start + top_index * $0002))
|
||||
}
|
||||
}
|
||||
|
||||
@ -244,16 +263,16 @@ fileselector {
|
||||
}
|
||||
|
||||
sub print_up_and_down() {
|
||||
if num_files<=MAX_LINES
|
||||
if num_files<=max_lines
|
||||
return
|
||||
print_up_indicator(top_index>0)
|
||||
print_down_indicator(num_visible_files < num_files)
|
||||
print_down_indicator(top_index + num_visible_files < num_files)
|
||||
}
|
||||
|
||||
sub print_up_indicator(bool shown) {
|
||||
txt.plot(DIALOG_TOPX, DIALOG_TOPY+5)
|
||||
txt.plot(dialog_topx, dialog_topy+5)
|
||||
txt.chrout('│')
|
||||
txt.column(30)
|
||||
txt.column(dialog_topx+26)
|
||||
if shown
|
||||
txt.print("(up) │\n")
|
||||
else
|
||||
@ -261,9 +280,9 @@ fileselector {
|
||||
}
|
||||
|
||||
sub print_down_indicator(bool shown) {
|
||||
txt.plot(DIALOG_TOPX, DIALOG_TOPY+6+num_visible_files)
|
||||
txt.plot(dialog_topx, dialog_topy+6+num_visible_files)
|
||||
txt.chrout('│')
|
||||
txt.column(28)
|
||||
txt.column(dialog_topx+24)
|
||||
if shown
|
||||
txt.print("(down) │\n")
|
||||
else
|
||||
@ -281,9 +300,9 @@ fileselector {
|
||||
}
|
||||
|
||||
sub invert(ubyte line) {
|
||||
cx16.r1L = DIALOG_TOPY+6+line
|
||||
cx16.r1L = dialog_topy+6+line
|
||||
ubyte charpos
|
||||
for charpos in DIALOG_TOPX+1 to DIALOG_TOPX+30 {
|
||||
for charpos in dialog_topx+1 to dialog_topx+30 {
|
||||
txt.setchr(charpos, cx16.r1L, txt.getchr(charpos, cx16.r1L) ^ 128)
|
||||
}
|
||||
}
|
||||
|
@ -1,19 +1,24 @@
|
||||
%import diskio
|
||||
%import textio
|
||||
%import sorting
|
||||
%import strings
|
||||
%zeropage basicsafe
|
||||
|
||||
; A "GUI" for an interactive file selector, that scrolls the selection list if it doesn't fit on the screen.
|
||||
; A "TUI" for an interactive file selector, that scrolls the selection list if it doesn't fit on the screen.
|
||||
; Depends a lot on diskio routines, and uses the drive set in the diskio.drivenumber variable (usually just 8)
|
||||
|
||||
; TODO sort entries alphabetically
|
||||
; TODO also show directories (how to distinguish them? what with the result value?)
|
||||
; TODO also show directories (how to distinguish them? what with the result value? start with a slash , so they sort together too?)
|
||||
; TODO joystick control? mouse control?
|
||||
; TODO keyboard typing; jump to the first entry that starts with that character
|
||||
; TODO keyboard typing; jump to the first entry that starts with that character?
|
||||
|
||||
|
||||
main {
|
||||
sub start() {
|
||||
|
||||
uword chosen = showdir.show(8, 2) ; use hiram bank 2 for buffers
|
||||
fileselector.configure(20, 10, 20, 2)
|
||||
uword chosen = fileselector.select("*")
|
||||
txt.nl()
|
||||
txt.print_ub(cx16.getrambank())
|
||||
txt.nl()
|
||||
if chosen!=0 {
|
||||
txt.print("chosen: ")
|
||||
@ -26,91 +31,103 @@ main {
|
||||
}
|
||||
}
|
||||
|
||||
showdir {
|
||||
fileselector {
|
||||
|
||||
const uword filenamesbuf = $a000 ; use HIRAM bank
|
||||
const uword filenamesbuf_size = $2000
|
||||
const ubyte DIALOG_TOPX = 4
|
||||
const ubyte DIALOG_TOPY = 2
|
||||
const ubyte MAX_LINES = 20
|
||||
const uword filenamesbuffer = $a000 ; use a HIRAM bank
|
||||
const uword filenamesbuf_size = $1e00 ; leaves room for a 256 entry string pointer table at $be00-$bfff
|
||||
const uword filename_ptrs_start = $be00 ; array of 256 string pointers for each of the names in the buffer. ends with $0000.
|
||||
ubyte dialog_topx = 4
|
||||
ubyte dialog_topy = 10
|
||||
ubyte max_lines = 20
|
||||
ubyte buffer_rambank = 1 ; default hiram bank to use for the data buffers
|
||||
|
||||
str chosen_filename = "?" * 32
|
||||
uword name_ptr
|
||||
ubyte num_visible_files
|
||||
|
||||
sub show(ubyte drive, ubyte rambank) -> uword {
|
||||
|
||||
sub configure(ubyte column, ubyte row, ubyte max_entries, ubyte rambank) {
|
||||
dialog_topx = column
|
||||
dialog_topy = row
|
||||
max_lines = max_entries
|
||||
buffer_rambank = rambank
|
||||
}
|
||||
|
||||
sub select(str pattern) -> uword {
|
||||
ubyte old_bank = cx16.getrambank()
|
||||
cx16.rambank(rambank)
|
||||
cx16.rambank(buffer_rambank)
|
||||
defer cx16.rambank(old_bank)
|
||||
|
||||
; if pattern!=0 and pattern[0]==0
|
||||
; pattern = 0 ; force pattern to be 0 instead of empty string, to be compatible with prog8 11.0 or older
|
||||
|
||||
num_visible_files = 0
|
||||
chosen_filename[0] = 0
|
||||
diskio.drivenumber = drive
|
||||
name_ptr = diskio.diskname()
|
||||
if name_ptr==0 or cbm.READST()!=0
|
||||
return 0
|
||||
|
||||
txt.cls()
|
||||
txt.plot(DIALOG_TOPX, DIALOG_TOPY)
|
||||
txt.plot(dialog_topx, dialog_topy)
|
||||
txt.print("┌")
|
||||
linepart()
|
||||
txt.print("┐\n")
|
||||
txt.column(DIALOG_TOPX)
|
||||
txt.column(dialog_topx)
|
||||
txt.print("│ drive ")
|
||||
txt.print_ub(diskio.drivenumber)
|
||||
txt.print(": '")
|
||||
txt.print(name_ptr)
|
||||
txt.chrout('\'')
|
||||
txt.column(DIALOG_TOPX+31)
|
||||
txt.column(dialog_topx+31)
|
||||
txt.print("│\n")
|
||||
txt.column(DIALOG_TOPX)
|
||||
txt.column(dialog_topx)
|
||||
txt.print("│ scanning directory... │\n")
|
||||
txt.column(DIALOG_TOPX)
|
||||
txt.column(dialog_topx)
|
||||
footerline()
|
||||
|
||||
ubyte num_files = diskio.list_filenames(0, filenamesbuf, filenamesbuf_size) ; use Hiram bank to store the files
|
||||
ubyte num_files = diskio.list_filenames(pattern, filenamesbuffer, filenamesbuf_size) ; use Hiram bank to store the files
|
||||
ubyte selected_line
|
||||
ubyte top_index
|
||||
uword[MAX_LINES] visible_names
|
||||
uword filename_ptrs
|
||||
|
||||
txt.plot(DIALOG_TOPX+2, DIALOG_TOPY+2)
|
||||
construct_name_ptr_array()
|
||||
; sort alphabetically
|
||||
sorting.shellsort_pointers(filename_ptrs_start, num_files, sorting.string_comparator)
|
||||
|
||||
; initial display
|
||||
txt.plot(dialog_topx+2, dialog_topy+2)
|
||||
txt.print("select file: (")
|
||||
txt.print_ub(num_files)
|
||||
txt.print(" total)")
|
||||
txt.column(DIALOG_TOPX+31)
|
||||
txt.column(dialog_topx+31)
|
||||
txt.print("│\n")
|
||||
txt.column(DIALOG_TOPX)
|
||||
txt.column(dialog_topx)
|
||||
txt.print("│ stop or q to abort |\n")
|
||||
txt.column(DIALOG_TOPX)
|
||||
txt.column(dialog_topx)
|
||||
txt.print("├")
|
||||
linepart()
|
||||
txt.print("┤\n")
|
||||
print_up_indicator(false)
|
||||
name_ptr = filenamesbuf
|
||||
for selected_line in 0 to min(MAX_LINES, num_files)-1 {
|
||||
if cbm.STOP2() break
|
||||
|
||||
txt.column(DIALOG_TOPX)
|
||||
txt.print("│ ")
|
||||
|
||||
visible_names[selected_line] = name_ptr ; keep pointer to name
|
||||
while @(name_ptr)!=0 {
|
||||
txt.chrout(@(name_ptr))
|
||||
name_ptr++
|
||||
if num_files>0 {
|
||||
for selected_line in 0 to min(max_lines, num_files)-1 {
|
||||
txt.column(dialog_topx)
|
||||
txt.print("│ ")
|
||||
print_filename(peekw(filename_ptrs_start+selected_line*$0002))
|
||||
txt.column(dialog_topx+31)
|
||||
txt.print("│\n")
|
||||
num_visible_files++
|
||||
}
|
||||
txt.column(DIALOG_TOPX+31)
|
||||
} else {
|
||||
txt.column(dialog_topx)
|
||||
txt.print("│ no matches.")
|
||||
txt.column(dialog_topx+31)
|
||||
txt.print("│\n")
|
||||
name_ptr++
|
||||
num_visible_files++
|
||||
}
|
||||
print_down_indicator(false)
|
||||
txt.column(DIALOG_TOPX)
|
||||
txt.column(dialog_topx)
|
||||
footerline()
|
||||
|
||||
name_ptr = filenamesbuf
|
||||
selected_line = 0
|
||||
invert(selected_line)
|
||||
|
||||
print_up_and_down()
|
||||
|
||||
repeat {
|
||||
@ -121,41 +138,48 @@ showdir {
|
||||
when key {
|
||||
3, 27, 'q' -> return 0 ; STOP or Q aborts (and ESC?)
|
||||
'\n',' ' -> {
|
||||
void strings.copy(visible_names[selected_line], chosen_filename)
|
||||
return chosen_filename
|
||||
if num_files>0 {
|
||||
void strings.copy(peekw(filename_ptrs_start + (top_index+selected_line)*$0002), chosen_filename)
|
||||
return chosen_filename
|
||||
}
|
||||
return 0
|
||||
}
|
||||
'[',130 -> { ; PAGEUP
|
||||
'[',130,157 -> { ; PAGEUP, cursor left
|
||||
; previous page of lines
|
||||
invert(selected_line)
|
||||
if selected_line==0
|
||||
repeat MAX_LINES scroll_list_backward()
|
||||
repeat max_lines scroll_list_backward()
|
||||
selected_line = 0
|
||||
invert(selected_line)
|
||||
print_up_and_down()
|
||||
}
|
||||
']',2 -> { ; PAGEDOWN
|
||||
; next page of lines
|
||||
invert(selected_line)
|
||||
if selected_line == MAX_LINES-1
|
||||
repeat MAX_LINES scroll_list_forward()
|
||||
selected_line = num_visible_files-1
|
||||
invert(selected_line)
|
||||
print_up_and_down()
|
||||
']',2,29 -> { ; PAGEDOWN, cursor right
|
||||
if num_files>0 {
|
||||
; next page of lines
|
||||
invert(selected_line)
|
||||
if selected_line == max_lines-1
|
||||
repeat max_lines scroll_list_forward()
|
||||
selected_line = num_visible_files-1
|
||||
invert(selected_line)
|
||||
print_up_and_down()
|
||||
}
|
||||
}
|
||||
17 -> { ; down
|
||||
invert(selected_line)
|
||||
if selected_line<num_visible_files-1
|
||||
selected_line++
|
||||
else if num_files>MAX_LINES
|
||||
scroll_list_forward()
|
||||
invert(selected_line)
|
||||
print_up_and_down()
|
||||
if num_files>0 {
|
||||
invert(selected_line)
|
||||
if selected_line<num_visible_files-1
|
||||
selected_line++
|
||||
else if num_files>max_lines
|
||||
scroll_list_forward()
|
||||
invert(selected_line)
|
||||
print_up_and_down()
|
||||
}
|
||||
}
|
||||
145 -> { ; up
|
||||
invert(selected_line)
|
||||
if selected_line>0
|
||||
selected_line--
|
||||
else if num_files>MAX_LINES
|
||||
else if num_files>max_lines
|
||||
scroll_list_backward()
|
||||
invert(selected_line)
|
||||
print_up_and_down()
|
||||
@ -165,25 +189,37 @@ showdir {
|
||||
|
||||
ubyte x,y
|
||||
|
||||
sub scroll_list_forward() {
|
||||
if top_index+MAX_LINES< num_files {
|
||||
top_index++
|
||||
; scroll the displayed list up 1
|
||||
scroll_txt_up(DIALOG_TOPX+2, DIALOG_TOPY+6, 28, MAX_LINES, sc:' ')
|
||||
; shift the name pointer array 1 to the left
|
||||
for y in 0 to MAX_LINES-2
|
||||
visible_names[y]=visible_names[y+1]
|
||||
|
||||
; set name pointer array last element with newly appeared name
|
||||
name_ptr = visible_names[MAX_LINES-1]
|
||||
sub construct_name_ptr_array() {
|
||||
filename_ptrs = filename_ptrs_start
|
||||
name_ptr = filenamesbuffer
|
||||
repeat num_files {
|
||||
pokew(filename_ptrs, name_ptr)
|
||||
while @(name_ptr)!=0
|
||||
name_ptr++
|
||||
name_ptr++
|
||||
visible_names[MAX_LINES-1] = name_ptr
|
||||
filename_ptrs+=2
|
||||
}
|
||||
pokew(filename_ptrs, 0)
|
||||
}
|
||||
|
||||
; print that new name at the bottom of the list
|
||||
txt.plot(DIALOG_TOPX+2, DIALOG_TOPY+6+MAX_LINES-1)
|
||||
txt.print(name_ptr)
|
||||
sub print_filename(uword name) {
|
||||
repeat 28 { ; maximum length displayed
|
||||
if @(name)==0
|
||||
break
|
||||
txt.chrout(128) ; don't print control characters
|
||||
txt.chrout(@(name))
|
||||
name++
|
||||
}
|
||||
}
|
||||
|
||||
sub scroll_list_forward() {
|
||||
if top_index+max_lines< num_files {
|
||||
top_index++
|
||||
; scroll the displayed list up 1
|
||||
scroll_txt_up(dialog_topx+2, dialog_topy+6, 28, max_lines, sc:' ')
|
||||
; print new name at the bottom of the list
|
||||
txt.plot(dialog_topx+2, dialog_topy+6+max_lines-1)
|
||||
print_filename(peekw(filename_ptrs_start + (top_index+ selected_line)*$0002))
|
||||
}
|
||||
}
|
||||
|
||||
@ -191,22 +227,10 @@ showdir {
|
||||
if top_index>0 {
|
||||
top_index--
|
||||
; scroll the displayed list down 1
|
||||
scroll_txt_down(DIALOG_TOPX+2, DIALOG_TOPY+6, 28, MAX_LINES, sc:' ')
|
||||
; shift the name pointer array 1 to the right
|
||||
for y in MAX_LINES-1 downto 1
|
||||
visible_names[y]=visible_names[y-1]
|
||||
|
||||
; set name pointer array element 0 with newly appeared name
|
||||
name_ptr = visible_names[0]-2
|
||||
while name_ptr>filenamesbuf and @(name_ptr)!=0
|
||||
name_ptr--
|
||||
if @(name_ptr)==0
|
||||
name_ptr++
|
||||
visible_names[0] = name_ptr
|
||||
|
||||
; print that new name at the top of the list
|
||||
txt.plot(DIALOG_TOPX+2, DIALOG_TOPY+6)
|
||||
txt.print(name_ptr)
|
||||
scroll_txt_down(dialog_topx+2, dialog_topy+6, 28, max_lines, sc:' ')
|
||||
; print new name at the top of the list
|
||||
txt.plot(dialog_topx+2, dialog_topy+6)
|
||||
print_filename(peekw(filename_ptrs_start + top_index * $0002))
|
||||
}
|
||||
}
|
||||
|
||||
@ -234,16 +258,16 @@ showdir {
|
||||
}
|
||||
|
||||
sub print_up_and_down() {
|
||||
if num_files<=MAX_LINES
|
||||
if num_files<=max_lines
|
||||
return
|
||||
print_up_indicator(top_index>0)
|
||||
print_down_indicator(num_visible_files < num_files)
|
||||
print_down_indicator(top_index + num_visible_files < num_files)
|
||||
}
|
||||
|
||||
sub print_up_indicator(bool shown) {
|
||||
txt.plot(DIALOG_TOPX, DIALOG_TOPY+5)
|
||||
txt.plot(dialog_topx, dialog_topy+5)
|
||||
txt.chrout('│')
|
||||
txt.column(30)
|
||||
txt.column(dialog_topx+26)
|
||||
if shown
|
||||
txt.print("(up) │\n")
|
||||
else
|
||||
@ -251,9 +275,9 @@ showdir {
|
||||
}
|
||||
|
||||
sub print_down_indicator(bool shown) {
|
||||
txt.plot(DIALOG_TOPX, DIALOG_TOPY+6+num_visible_files)
|
||||
txt.plot(dialog_topx, dialog_topy+6+num_visible_files)
|
||||
txt.chrout('│')
|
||||
txt.column(28)
|
||||
txt.column(dialog_topx+24)
|
||||
if shown
|
||||
txt.print("(down) │\n")
|
||||
else
|
||||
@ -271,9 +295,9 @@ showdir {
|
||||
}
|
||||
|
||||
sub invert(ubyte line) {
|
||||
cx16.r1L = DIALOG_TOPY+6+line
|
||||
cx16.r1L = dialog_topy+6+line
|
||||
ubyte charpos
|
||||
for charpos in DIALOG_TOPX+1 to DIALOG_TOPX+30 {
|
||||
for charpos in dialog_topx+1 to dialog_topx+30 {
|
||||
txt.setchr(charpos, cx16.r1L, txt.getchr(charpos, cx16.r1L) ^ 128)
|
||||
}
|
||||
}
|
||||
|
@ -1,56 +1,18 @@
|
||||
%import textio
|
||||
%import strings
|
||||
%import diskio
|
||||
%zeropage basicsafe
|
||||
%option no_sysinit
|
||||
|
||||
main {
|
||||
sub start() {
|
||||
uword[8] @nosplit warr
|
||||
uword[8] @split swarr
|
||||
|
||||
uword @shared ptr = $1000
|
||||
const uword cptr = $2000
|
||||
txt.print_uwhex(&ptr[2], true)
|
||||
txt.spc()
|
||||
txt.print_uwhex(&cptr[2], true)
|
||||
txt.nl()
|
||||
|
||||
txt.print_uwhex(&warr, true)
|
||||
txt.spc()
|
||||
txt.print_uwhex(&warr[2], true)
|
||||
txt.nl()
|
||||
|
||||
txt.print("addresses of split word array:\n")
|
||||
txt.print_uwhex(&swarr, true)
|
||||
txt.spc()
|
||||
txt.print_uwhex(&<swarr, true)
|
||||
txt.spc()
|
||||
txt.print_uwhex(&>swarr, true)
|
||||
txt.nl()
|
||||
txt.print("addresses of normal word array:\n")
|
||||
txt.print_uwhex(&warr, true)
|
||||
txt.spc()
|
||||
txt.print_uwhex(&<warr, true)
|
||||
txt.nl()
|
||||
|
||||
txt.print("addresses of split word array element 4:\n")
|
||||
txt.print_uwhex(&swarr[4], true)
|
||||
txt.spc()
|
||||
txt.print_uwhex(&<swarr[4], true)
|
||||
txt.spc()
|
||||
txt.print_uwhex(&>swarr[4], true)
|
||||
txt.nl()
|
||||
txt.print("addresses of split word array element 4 via var:\n")
|
||||
cx16.r0L=4
|
||||
txt.print_uwhex(&swarr[cx16.r0L], true)
|
||||
txt.spc()
|
||||
txt.print_uwhex(&<swarr[cx16.r0L], true)
|
||||
txt.spc()
|
||||
txt.print_uwhex(&>swarr[cx16.r0L], true)
|
||||
txt.nl()
|
||||
txt.print("addresses of normal word array element 4:\n")
|
||||
txt.print_uwhex(&warr[4], true)
|
||||
txt.spc()
|
||||
txt.print_uwhex(&<warr[4], true)
|
||||
txt.nl()
|
||||
diskio.lf_start_list("")
|
||||
while diskio.lf_next_entry() {
|
||||
txt.print_uw(diskio.list_blocks)
|
||||
txt.spc()
|
||||
txt.print(diskio.list_filename)
|
||||
txt.nl()
|
||||
}
|
||||
diskio.lf_end_list()
|
||||
}
|
||||
}
|
||||
|
@ -9,7 +9,7 @@ dependencies {
|
||||
implementation(project(":codeCore"))
|
||||
implementation(project(":intermediate"))
|
||||
// implementation("org.jetbrains.kotlin:kotlin-stdlib-jdk8")
|
||||
implementation("com.michael-bull.kotlin-result:kotlin-result-jvm:2.0.0")
|
||||
implementation("com.michael-bull.kotlin-result:kotlin-result-jvm:2.0.1")
|
||||
testImplementation("io.kotest:kotest-runner-junit5-jvm:5.9.1")
|
||||
testImplementation("io.kotest:kotest-framework-datatest:5.9.1")
|
||||
testImplementation("org.junit.jupiter:junit-jupiter:5.9.1")
|
||||
@ -41,4 +41,4 @@ tasks.test {
|
||||
testLogging {
|
||||
events("skipped", "failed")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user