diff --git a/stdlib/REPL/src/TerminalMenus/AbstractMenu.jl b/stdlib/REPL/src/TerminalMenus/AbstractMenu.jl index 1b935d333013f..f6895858590c5 100644 --- a/stdlib/REPL/src/TerminalMenus/AbstractMenu.jl +++ b/stdlib/REPL/src/TerminalMenus/AbstractMenu.jl @@ -45,6 +45,7 @@ subtypes. - `header(m::AbstractMenu)` - `keypress(m::AbstractMenu, i::UInt32)` + - `cursorstart(m::AbstractMenu)` """ abstract type AbstractMenu end @@ -108,6 +109,12 @@ If `true` is returned, `request()` will exit. keypress(m::AbstractMenu, i::UInt32) = false +""" + cursorstart(m::AbstractMenu) -> Int + +Compute the starting cursor position. +""" +cursorstart(m::AbstractMenu) = 1 """ @@ -119,7 +126,7 @@ varies based on menu type. request(m::AbstractMenu) = request(terminal, m) function request(term::REPL.Terminals.TTYTerminal, m::AbstractMenu) - cursor = 1 + cursor = cursorstart(m) menu_header = header(m) !CONFIG[:supress_output] && menu_header != "" && println(term.out_stream, menu_header) @@ -234,7 +241,7 @@ function printMenu(out, m::AbstractMenu, cursor::Int; init::Bool=false) lines = m.pagesize-1 if init - m.pageoffset = 0 + m.pageoffset = min(max(cursor-m.pagesize+1, 0), length(options(m))-m.pagesize) else # Move the cursor to the beginning of where it should print print(buf, "\x1b[999D\x1b[$(lines)A") diff --git a/stdlib/REPL/src/TerminalMenus/MultiSelectMenu.jl b/stdlib/REPL/src/TerminalMenus/MultiSelectMenu.jl index 76177169384fa..cfd6aab258e59 100644 --- a/stdlib/REPL/src/TerminalMenus/MultiSelectMenu.jl +++ b/stdlib/REPL/src/TerminalMenus/MultiSelectMenu.jl @@ -37,7 +37,7 @@ end """ - MultiSelectMenu(options::Array{String,1}; pagesize::Int=10) + MultiSelectMenu(options::Array{String,1}; pagesize::Int=10, default=Set{Int}()) Create a MultiSelectMenu object. Use `request(menu::MultiSelectMenu)` to get user input. `request()` returns a `Set` containing the indices of options that @@ -47,8 +47,9 @@ were selected by the user. - `options::Array{String, 1}`: Options to be displayed - `pagesize::Int=10`: The number of options to be displayed at one time, the menu will scroll if length(options) > pagesize + - `default=Set{Int}()`: The indices of options which are first selected, must be a collection of `Int`s """ -function MultiSelectMenu(options::Array{String,1}; pagesize::Int=10) +function MultiSelectMenu(options::Array{String,1}; pagesize::Int=10, default=Int[]) length(options) < 2 && error("MultiSelectMenu must have at least two options") # if pagesize is -1, use automatic paging @@ -57,9 +58,13 @@ function MultiSelectMenu(options::Array{String,1}; pagesize::Int=10) pagesize = min(length(options), pagesize) # after other checks, pagesize must be greater than 2 pagesize < 2 && error("pagesize must be >= 2") + # defaults must be in the range of the options + if !all(d -> 0 < d <= length(options), default) + error("defaults must be > 0 and <= $(length(options))") + end pageoffset = 0 - selected = Set{Int}() # none + selected = Set{Int}(default) MultiSelectMenu(options, pagesize, pageoffset, selected) end diff --git a/stdlib/REPL/src/TerminalMenus/RadioMenu.jl b/stdlib/REPL/src/TerminalMenus/RadioMenu.jl index 5c8137ff0e61d..860bbc45babba 100644 --- a/stdlib/REPL/src/TerminalMenus/RadioMenu.jl +++ b/stdlib/REPL/src/TerminalMenus/RadioMenu.jl @@ -24,12 +24,13 @@ mutable struct RadioMenu <: AbstractMenu pagesize::Int pageoffset::Int selected::Int + default::Int end """ - RadioMenu(options::Array{String,1}; pagesize::Int=10) + RadioMenu(options::Array{String,1}; pagesize::Int=10, default::Int=1) Create a RadioMenu object. Use `request(menu::RadioMenu)` to get user input. `request()` returns an `Int` which is the index of the option selected by the @@ -39,8 +40,9 @@ user. - `options::Array{String, 1}`: Options to be displayed - `pagesize::Int=10`: The number of options to be displayed at one time, the menu will scroll if length(options) > pagesize + - `default::Int=1`: The index of the option which is first selected """ -function RadioMenu(options::Array{String,1}; pagesize::Int=10) +function RadioMenu(options::Array{String,1}; pagesize::Int=10, default::Int=1) length(options) < 2 && error("RadioMenu must have at least two options") # if pagesize is -1, use automatic paging @@ -49,11 +51,13 @@ function RadioMenu(options::Array{String,1}; pagesize::Int=10) pagesize = min(length(options), pagesize) # after other checks, pagesize must be greater than 2 pagesize < 2 && error("pagesize must be >= 2") + # default must be in the range of the options + 0 < default <= length(options) || error("default must be > 0 and <= $(length(options))") pageoffset = 0 selected = -1 # none - RadioMenu(options, pagesize, pageoffset, selected) + RadioMenu(options, pagesize, pageoffset, selected, default) end @@ -66,9 +70,11 @@ options(m::RadioMenu) = m.options cancel(m::RadioMenu) = m.selected = -1 +cursorstart(m::RadioMenu) = m.default + function pick(menu::RadioMenu, cursor::Int) menu.selected = cursor - return true #break out of the menu + return true # break out of the menu end function writeLine(buf::IOBuffer, menu::RadioMenu, idx::Int, cursor::Bool) diff --git a/stdlib/REPL/test/TerminalMenus/multiselect_menu.jl b/stdlib/REPL/test/TerminalMenus/multiselect_menu.jl index 5fb97c91bf00b..9b9e2e13626f7 100644 --- a/stdlib/REPL/test/TerminalMenus/multiselect_menu.jl +++ b/stdlib/REPL/test/TerminalMenus/multiselect_menu.jl @@ -6,6 +6,8 @@ # Invalid Menu Params @test_throws ErrorException MultiSelectMenu(["one"]) @test_throws ErrorException MultiSelectMenu(["one", "two", "three"], pagesize=1) +@test_throws ErrorException MultiSelectMenu(["one", "two", "three"], default=[0, 1, 2]) +@test_throws ErrorException MultiSelectMenu(["one", "two", "three"], default=[2, 3, 4]) # Constructor @test MultiSelectMenu(["one", "two", "three"]).pagesize == 3 @@ -13,7 +15,8 @@ @test MultiSelectMenu(string.(1:4), pagesize=10).pagesize == 4 @test MultiSelectMenu(string.(1:100)).pagesize == 10 -multi_menu = MultiSelectMenu(string.(1:20)) +multi_menu = MultiSelectMenu(string.(1:20), default=[2, 4, 6]) +@test multi_menu.selected == Set([2, 4, 6]) @test TerminalMenus.options(multi_menu) == string.(1:20) @test TerminalMenus.header(multi_menu) == "[press: d=done, a=all, n=none]" diff --git a/stdlib/REPL/test/TerminalMenus/radio_menu.jl b/stdlib/REPL/test/TerminalMenus/radio_menu.jl index d94622b3efb8d..2a495868e6aa5 100644 --- a/stdlib/REPL/test/TerminalMenus/radio_menu.jl +++ b/stdlib/REPL/test/TerminalMenus/radio_menu.jl @@ -6,12 +6,15 @@ # Invalid Menu Params @test_throws ErrorException RadioMenu(["one"]) @test_throws ErrorException RadioMenu(["one", "two", "three"], pagesize=1) +@test_throws ErrorException RadioMenu(["one"], default=0) +@test_throws ErrorException RadioMenu(["one"], default=2) # Constructor @test RadioMenu(["one", "two", "three"]).pagesize == 3 @test RadioMenu(string.(1:30), pagesize=-1).pagesize == 30 @test RadioMenu(string.(1:4), pagesize=10).pagesize == 4 @test RadioMenu(string.(1:100)).pagesize == 10 +@test RadioMenu(string.(1:20), default=10).default == 10 radio_menu = RadioMenu(string.(1:20)) @test TerminalMenus.options(radio_menu) == string.(1:20)