mirror of
https://github.com/curioustorvald/tsvm.git
synced 2026-06-06 13:38:30 +09:00
wintex default theme changes
This commit is contained in:
@@ -442,7 +442,7 @@ _fsh.redrawAll = function() {
|
||||
_fsh.openAddTodoDialog = function() {
|
||||
let res = win.showDialog({
|
||||
title: "New Todo",
|
||||
fields: [{label: "Text", initial: "", width: _fsh.TODO_TEXT_WIDTH}],
|
||||
fields: [{label: "Text:", initial: "", width: _fsh.TODO_TEXT_WIDTH}],
|
||||
allowDelete: false
|
||||
})
|
||||
_fsh.redrawAll()
|
||||
@@ -459,7 +459,7 @@ _fsh.openEditTodoDialog = function(index) {
|
||||
if (!entry) return
|
||||
let res = win.showDialog({
|
||||
title: "Edit Todo",
|
||||
fields: [{label: "Text", initial: entry[0], width: _fsh.TODO_TEXT_WIDTH}],
|
||||
fields: [{label: "Text:", initial: entry[0], width: _fsh.TODO_TEXT_WIDTH}],
|
||||
allowDelete: true
|
||||
})
|
||||
_fsh.redrawAll()
|
||||
@@ -479,8 +479,8 @@ _fsh.openAddQaDialog = function() {
|
||||
let res = win.showDialog({
|
||||
title: "New Quick Access",
|
||||
fields: [
|
||||
{label: "Label", initial: "", width: _fsh.QA_LABEL_WIDTH},
|
||||
{label: "Command", initial: "", width: _fsh.QA_CMD_WIDTH}
|
||||
{label: "Label:", initial: "", width: _fsh.QA_LABEL_WIDTH},
|
||||
{label: "Command:", initial: "", width: _fsh.QA_CMD_WIDTH}
|
||||
],
|
||||
allowDelete: false
|
||||
})
|
||||
@@ -500,8 +500,8 @@ _fsh.openEditQaDialog = function(index) {
|
||||
let res = win.showDialog({
|
||||
title: "Edit Quick Access",
|
||||
fields: [
|
||||
{label: "Label", initial: entry[0], width: _fsh.QA_LABEL_WIDTH},
|
||||
{label: "Command", initial: entry[1], width: _fsh.QA_CMD_WIDTH}
|
||||
{label: "Label:", initial: entry[0], width: _fsh.QA_LABEL_WIDTH},
|
||||
{label: "Command:", initial: entry[1], width: _fsh.QA_CMD_WIDTH}
|
||||
],
|
||||
allowDelete: true
|
||||
})
|
||||
|
||||
@@ -597,6 +597,181 @@ function showMessagePopup(title, message) {
|
||||
})
|
||||
}
|
||||
|
||||
// Vertical-list popup: items are stacked rows, navigable with arrow keys /
|
||||
// mouse, selection (Enter / left-click on row) returns that item's action.
|
||||
// A single Close button sits below the list; Esc and Close both yield 'close'.
|
||||
function showActionListPopup(opts) {
|
||||
const title = opts.title || ''
|
||||
const items = opts.items || []
|
||||
const closeLabel = opts.closeLabel || 'Close'
|
||||
const message = opts.message
|
||||
const messageLines = !message ? []
|
||||
: Array.isArray(message) ? message
|
||||
: ('' + message).split('\n')
|
||||
|
||||
const fg = 254
|
||||
const bg = 243
|
||||
const dimFg = 249
|
||||
const hlFg = 230
|
||||
const itemSelBg = 81
|
||||
|
||||
const longestItem = items.reduce((m, it) => Math.max(m, it.label.length), 0)
|
||||
const longestMsg = messageLines.reduce((m, l) => Math.max(m, l.length), 0)
|
||||
const titleW = title.length + 4
|
||||
const closeBtnW = closeLabel.length + 4
|
||||
const w = Math.max(longestItem + 8, titleW + 4, longestMsg + 6, closeBtnW + 4, 24)
|
||||
|
||||
const msgRows = messageLines.length + (messageLines.length > 0 ? 1 : 0)
|
||||
const itemsRowOff = 1 + msgRows
|
||||
const buttonsRowOff = itemsRowOff + items.length + 1
|
||||
const h = buttonsRowOff + 2
|
||||
const screen = con.getmaxyx()
|
||||
const row = Math.max(2, Math.floor((screen[0] - h) / 2))
|
||||
const col = Math.max(2, Math.floor((screen[1] - w) / 2))
|
||||
|
||||
let oldFG = con.get_color_fore()
|
||||
let oldBG = con.get_color_back()
|
||||
|
||||
// Initial focus: first 'default: true' item, otherwise first item, otherwise close button.
|
||||
let focusIdx = -1
|
||||
for (let i = 0; i < items.length; i++) {
|
||||
if (items[i].default) { focusIdx = i; break }
|
||||
}
|
||||
if (focusIdx < 0) focusIdx = (items.length > 0) ? 0 : items.length
|
||||
const totalFocus = items.length + 1
|
||||
let done = null
|
||||
|
||||
function itemRow(i) { return row + itemsRowOff + i }
|
||||
function itemCol() { return col + 1 }
|
||||
function itemWidth() { return w - 2 }
|
||||
function closeBtnRow(){ return row + buttonsRowOff }
|
||||
function closeBtnCol(){ return col + Math.floor((w - closeBtnW) / 2) }
|
||||
|
||||
function drawFrameBox() {
|
||||
con.color_pair(fg, bg)
|
||||
for (let r = row; r < row + h; r++) {
|
||||
con.move(r, col)
|
||||
print(' '.repeat(w))
|
||||
}
|
||||
const wo = new win.WindowObject(col, row, w, h, ()=>{}, ()=>{}, title)
|
||||
wo.isHighlighted = true
|
||||
wo.titleBack = bg
|
||||
wo.drawFrame()
|
||||
con.color_pair(fg, bg)
|
||||
}
|
||||
|
||||
function drawMessage() {
|
||||
if (messageLines.length === 0) return
|
||||
con.color_pair(dimFg, bg)
|
||||
for (let i = 0; i < messageLines.length; i++) {
|
||||
con.move(row + 1 + i, col + 2)
|
||||
print(messageLines[i].padEnd(w - 4, ' '))
|
||||
}
|
||||
con.color_pair(fg, bg)
|
||||
}
|
||||
|
||||
function drawItem(i) {
|
||||
const focused = (focusIdx === i)
|
||||
const useFg = focused ? hlFg : fg
|
||||
const useBg = focused ? itemSelBg : bg
|
||||
con.color_pair(useFg, useBg)
|
||||
con.move(itemRow(i), itemCol())
|
||||
print(' ' + items[i].label.padEnd(itemWidth() - 2, ' '))
|
||||
con.color_pair(fg, bg)
|
||||
}
|
||||
|
||||
function drawCloseBtn() {
|
||||
const focused = (focusIdx === items.length)
|
||||
const useFg = focused ? hlFg : fg
|
||||
con.color_pair(useFg, bg)
|
||||
con.move(closeBtnRow(), closeBtnCol())
|
||||
print('[ ' + closeLabel + ' ]')
|
||||
con.color_pair(fg, bg)
|
||||
}
|
||||
|
||||
function render() {
|
||||
drawFrameBox()
|
||||
drawMessage()
|
||||
for (let i = 0; i < items.length; i++) drawItem(i)
|
||||
drawCloseBtn()
|
||||
con.curs_set(0)
|
||||
}
|
||||
|
||||
function hitTest(ev) {
|
||||
const [cy, cx] = pixelToCell(ev[1], ev[2])
|
||||
for (let i = 0; i < items.length; i++) {
|
||||
if (cy === itemRow(i) && cx >= itemCol() && cx < itemCol() + itemWidth()) {
|
||||
return { kind: 'item', idx: i }
|
||||
}
|
||||
}
|
||||
const cbx = closeBtnCol()
|
||||
if (cy === closeBtnRow() && cx >= cbx && cx < cbx + closeBtnW) {
|
||||
return { kind: 'close' }
|
||||
}
|
||||
return null
|
||||
}
|
||||
|
||||
function moveFocus(dir) {
|
||||
focusIdx = (focusIdx + dir + totalFocus) % totalFocus
|
||||
render()
|
||||
}
|
||||
|
||||
render()
|
||||
|
||||
let eventJustReceived = true
|
||||
while (done === null) {
|
||||
input.withEvent(ev => {
|
||||
if (eventJustReceived && (ev[0] === 'key_down' || ev[0] === 'mouse_down')) {
|
||||
eventJustReceived = false; return
|
||||
}
|
||||
|
||||
if (ev[0] === 'mouse_move') {
|
||||
const hit = hitTest(ev)
|
||||
let newFocus = null
|
||||
if (hit && hit.kind === 'item') newFocus = hit.idx
|
||||
else if (hit && hit.kind === 'close') newFocus = items.length
|
||||
if (newFocus !== null && newFocus !== focusIdx) {
|
||||
focusIdx = newFocus
|
||||
for (let i = 0; i < items.length; i++) drawItem(i)
|
||||
drawCloseBtn()
|
||||
}
|
||||
return
|
||||
}
|
||||
if (ev[0] === 'mouse_down') {
|
||||
if (ev[3] !== 1) return
|
||||
const hit = hitTest(ev)
|
||||
if (!hit) return
|
||||
if (hit.kind === 'item') {
|
||||
focusIdx = hit.idx; render()
|
||||
done = { action: items[hit.idx].action }
|
||||
return
|
||||
}
|
||||
if (hit.kind === 'close') {
|
||||
focusIdx = items.length; render()
|
||||
done = { action: 'close' }
|
||||
return
|
||||
}
|
||||
return
|
||||
}
|
||||
if (ev[0] !== 'key_down') return
|
||||
if (1 !== ev[2]) return
|
||||
const ks = ev[1]
|
||||
|
||||
if (ks === '<ESC>') { done = { action: 'close' }; return }
|
||||
if (ks === '\t' || ks === '<TAB>' || ks === '<DOWN>') { moveFocus(+1); return }
|
||||
if (ks === '<UP>') { moveFocus(-1); return }
|
||||
if (ks === '\n' || ks === ' ') {
|
||||
if (focusIdx < items.length) done = { action: items[focusIdx].action }
|
||||
else done = { action: 'close' }
|
||||
return
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
con.color_pair(oldFG, oldBG)
|
||||
return done
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
const windows = [
|
||||
@@ -891,17 +1066,22 @@ function actRename() {
|
||||
function actMore() {
|
||||
if (path[windowMode].length === 0) return
|
||||
const cache = filePanelCache[windowMode][cursor[windowMode]]
|
||||
if (!cache || !cache.file || cache.isDirectory) return
|
||||
if (!cache || !cache.file) return
|
||||
|
||||
const res = win.showDialog({
|
||||
const items = cache.isDirectory
|
||||
? [
|
||||
{ label: 'Open terminal here', action: 'terminal', default: true },
|
||||
]
|
||||
: [
|
||||
{ label: 'Execute', action: 'execute', default: true },
|
||||
{ label: 'Edit', action: 'edit' },
|
||||
{ label: 'Open terminal here', action: 'terminal' },
|
||||
]
|
||||
|
||||
const res = showActionListPopup({
|
||||
title: 'More',
|
||||
message: cache.file.name,
|
||||
fields: [],
|
||||
buttons: [
|
||||
{ label: 'Execute', action: 'execute', default: true },
|
||||
{ label: 'Edit', action: 'edit' },
|
||||
{ label: 'Close', action: 'close' },
|
||||
],
|
||||
items: items,
|
||||
})
|
||||
_redraw()
|
||||
|
||||
@@ -930,7 +1110,42 @@ function actMore() {
|
||||
refreshFilePanelCache(windowMode)
|
||||
pendingPostExecDrain = true
|
||||
redraw()
|
||||
return
|
||||
}
|
||||
if (res.action === 'terminal') {
|
||||
actTerminal(cache)
|
||||
}
|
||||
}
|
||||
|
||||
function actTerminal(cache) {
|
||||
const targetDir = (cache && cache.isDirectory && cache.file)
|
||||
? cache.file.fullPath
|
||||
: getCurrentDirStr(windowMode)
|
||||
if (!targetDir || targetDir.length === 0) return
|
||||
|
||||
// TVDOS shell.parse has no working escape inside quotes (the `^` ESCAPE
|
||||
// state is a TODO), so we can't pass a quoted path through `command -k
|
||||
// "cd \"X\""`. The outer quotes carry the whole `cd <path>` as one token;
|
||||
// shell.execute then re-parses it. This works for paths without spaces;
|
||||
// paths with spaces will only cd to the first component.
|
||||
let errorlevel = 0
|
||||
con.curs_set(1); clearScr(); con.move(1, 1)
|
||||
try {
|
||||
errorlevel = _G.shell.execute(`command -k "cd ${targetDir}"`)
|
||||
}
|
||||
catch (e) {
|
||||
println(e)
|
||||
errorlevel = 1
|
||||
}
|
||||
if (errorlevel) {
|
||||
println("Hit Return/Enter key to continue . . . .")
|
||||
sys.read()
|
||||
}
|
||||
firstRunLatch = true
|
||||
con.curs_set(0); clearScr()
|
||||
refreshFilePanelCache(windowMode)
|
||||
pendingPostExecDrain = true
|
||||
redraw()
|
||||
}
|
||||
function actQuit() { exit = true }
|
||||
|
||||
|
||||
@@ -65,12 +65,12 @@ class WindowObject {
|
||||
}
|
||||
if (this.titleRight !== undefined) {
|
||||
let tt = ''+this.titleRight
|
||||
con.move(this.y, this.x + this.width - tt.length - 2)
|
||||
con.move(this.y + this.height - 1, this.x + this.width - tt.length - 2)
|
||||
print(`\x84${charset[4]}u`)
|
||||
if (this.titleBackRight !== undefined) print(`\x1B[48;5;${this.titleBackRight}m`)
|
||||
print(`\x1B[38;5;${colourText}m${tt}`)
|
||||
if (this.titleBackRight !== undefined) print(`\x1B[48;5;${oldBack}m`)
|
||||
print(`\x1B[38;5;${colour}m\x84${charset[1]}u`)
|
||||
print(`\x1B[38;5;${colour}m\x84${charset[3]}u`)
|
||||
}
|
||||
|
||||
|
||||
@@ -186,7 +186,7 @@ function scrollHorz(dx, stringSize, stringViewSize, currentCursorPos, currentScr
|
||||
// opts = {
|
||||
// title: string,
|
||||
// message: string | string[]? -- optional body text drawn above fields
|
||||
// fields: [{label, initial?, width}, ...] -- omit / [] for no input field
|
||||
// fields: [{label, initial?, width}, ...] -- omit / [] for no input field. Label does NOT get auto-colon
|
||||
// buttons: [{label, action, default?}, ...] -- defaults to [OK, Cancel] (+ Delete
|
||||
// if `allowDelete:true`)
|
||||
// allowDelete: bool, -- inserts a Delete button (fsh compat)
|
||||
@@ -208,7 +208,7 @@ function scrollHorz(dx, stringSize, stringViewSize, currentCursorPos, currentScr
|
||||
// hidden when a button has focus.
|
||||
// - Mouse: left-click on a button activates it; click on a field puts focus
|
||||
// on that field and positions the caret under the click. Mouse hover on a
|
||||
// button highlights it.
|
||||
// button moves focus to it (the same focus the keyboard uses).
|
||||
const _dialogScreen = con.getmaxyx()
|
||||
const _dialogPixDim = graphics.getPixelDimension()
|
||||
const _CELL_PW = (_dialogPixDim[0] / _dialogScreen[1]) | 0
|
||||
@@ -243,14 +243,15 @@ function showDialog(opts) {
|
||||
const bg = (c.bg != null) ? c.bg : 243
|
||||
const fieldBg = (c.fieldBg != null) ? c.fieldBg : 240
|
||||
const dimFg = (c.dimFg != null) ? c.dimFg : 249
|
||||
const hlFg = (c.hlFg != null) ? c.hlFg : 230
|
||||
const focusBg = (c.focusBg != null) ? c.focusBg : bg
|
||||
const hlFg = (c.hlFg != null) ? c.hlFg : 240
|
||||
const focusBg = (c.focusBg != null) ? c.focusBg : 253
|
||||
|
||||
// Layout
|
||||
const buttonGap = 3
|
||||
const maxFieldW = fields.reduce((m, f) => Math.max(m, f.width), 16)
|
||||
const longestMsg = messageLines.reduce((m, l) => Math.max(m, l.length), 0)
|
||||
const titleW = title.length + 4
|
||||
const btnRowW = buttons.reduce((s, b) => s + b.label.length + 5, 0) - 1
|
||||
const btnRowW = buttons.reduce((s, b) => s + b.label.length + 4, 0) + buttonGap * Math.max(0, buttons.length - 1)
|
||||
const w = Math.max(maxFieldW + 6, titleW + 4, longestMsg + 6, btnRowW + 4, 24)
|
||||
const msgTopOff = (messageLines.length > 0) ? 1 : 0
|
||||
const msgRows = messageLines.length + (messageLines.length > 0 ? 1 : 0)
|
||||
@@ -268,7 +269,6 @@ function showDialog(opts) {
|
||||
}
|
||||
if (focusIdx < 0) focusIdx = fields.length > 0 ? 0 : fields.length
|
||||
const totalFocus = fields.length + buttons.length
|
||||
let hoverBtn = -1
|
||||
let done = null
|
||||
|
||||
function fieldScroll(cur, fw) { return cur < fw ? 0 : cur - fw + 1 }
|
||||
@@ -282,7 +282,7 @@ function showDialog(opts) {
|
||||
let bx = col + Math.floor((w - btnRowW) / 2)
|
||||
return buttons.map(b => {
|
||||
const r = { x: bx, y: row + buttonsRowOff, w: b.label.length + 4 }
|
||||
bx += b.label.length + 5
|
||||
bx += b.label.length + 4 + buttonGap
|
||||
return r
|
||||
})
|
||||
}
|
||||
@@ -320,27 +320,29 @@ function showDialog(opts) {
|
||||
// Label
|
||||
con.color_pair(fg, bg)
|
||||
con.move(fieldLabelRow(i), fbCol)
|
||||
print(f.label + ':')
|
||||
print(f.label)
|
||||
|
||||
// Top border
|
||||
// Top border (3px padding w/ TSVM chr rom)
|
||||
con.color_pair(fieldBg, bg)
|
||||
con.move(fbRow, fbCol)
|
||||
print('\u00EC' + '\u00A9'.repeat(fw) + '\u00ED')
|
||||
|
||||
// Side borders + content
|
||||
// Left border (3px padding w/ TSVM chr rom)
|
||||
con.move(fbRow + 1, fbCol)
|
||||
print('\u00AB')
|
||||
|
||||
// the content
|
||||
con.color_pair(fg, fieldBg)
|
||||
const s = fieldScroll(cursors[i], fw)
|
||||
const vis = values[i].substring(s, s + fw)
|
||||
print(vis.padEnd(fw, ' '))
|
||||
|
||||
// Right border (3px padding w/ TSVM chr rom)
|
||||
con.color_pair(fieldBg, bg)
|
||||
con.move(fbRow + 1, fbCol + fw + 1)
|
||||
print('\u00AA')
|
||||
|
||||
// Bottom border
|
||||
// Bottom border (3px padding w/ TSVM chr rom)
|
||||
con.move(fbRow + 2, fbCol)
|
||||
print('\u00F4' + '\u00AC'.repeat(fw) + '\u00F5')
|
||||
con.color_pair(fg, bg)
|
||||
@@ -350,16 +352,21 @@ function showDialog(opts) {
|
||||
const b = buttons[i]
|
||||
const bIdx = fields.length + i
|
||||
const focused = (focusIdx === bIdx)
|
||||
const hovered = (hoverBtn === i)
|
||||
const r = regions[i]
|
||||
let useFg, useBg
|
||||
if (focused && hovered) { useFg = hlFg; useBg = focusBg }
|
||||
else if (focused) { useFg = hlFg; useBg = focusBg }
|
||||
else if (hovered) { useFg = hlFg; useBg = bg }
|
||||
else { useFg = fg; useBg = bg }
|
||||
const useFg = focused ? hlFg : fg
|
||||
const useBg = focused ? focusBg : bg
|
||||
con.color_pair(useFg, useBg)
|
||||
con.move(r.y, r.x)
|
||||
print('[ ' + b.label + ' ]')
|
||||
con.move(r.y, r.x-1)
|
||||
if (focused) {
|
||||
con.color_pair(useBg, bg)
|
||||
print('\u00DE')
|
||||
con.color_pair(useFg, useBg)
|
||||
print('[ ' + b.label + ' ]')
|
||||
con.color_pair(useBg, bg)
|
||||
print('\u00DD')
|
||||
}
|
||||
else
|
||||
print(' [ ' + b.label + ' ] ')
|
||||
con.color_pair(fg, bg)
|
||||
}
|
||||
|
||||
@@ -418,12 +425,12 @@ function showDialog(opts) {
|
||||
|
||||
if (ev[0] === 'mouse_move') {
|
||||
const hit = hitTestMouse(ev)
|
||||
const newHover = (hit && hit.kind === 'button') ? hit.idx : -1
|
||||
if (newHover !== hoverBtn) {
|
||||
hoverBtn = newHover
|
||||
const regs = buttonRegions()
|
||||
for (let i = 0; i < buttons.length; i++) drawButton(i, regs)
|
||||
positionCaret()
|
||||
if (hit && hit.kind === 'button') {
|
||||
const newFocus = fields.length + hit.idx
|
||||
if (newFocus !== focusIdx) {
|
||||
focusIdx = newFocus
|
||||
render()
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user