Files
2026-09-21 13:41:40 +09:00

404 lines
28 KiB
QML
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import QtQuick
import QtQuick.Controls
import QtQuick.Layouts
import QtQuick.Dialogs
import DocView 1.0
ApplicationWindow {
id: window
width: 1100; height: 800; minimumWidth: 480; minimumHeight: 360
visible: true
title: reader.title + (reader.title === "DocView" ? "" : " — DocView")
property bool dark: reader.settings.ui.theme === "system"
? Qt.styleHints.colorScheme !== Qt.ColorScheme.Light : reader.settings.ui.theme !== "light"
property color backgroundColor: dark ? "#181c20" : "#f1f3f4"
property color surfaceColor: dark ? "#22282e" : "#ffffff"
property color textColor: dark ? "#d8e0e8" : "#24303b"
property color mutedColor: dark ? "#97a4b1" : "#586775"
property color accentColor: dark ? "#abc8de" : "#215f8e"
property bool tocVisible: reader.settings.ui.toc_visible
property bool pagesVisible: reader.settings.ui.pages_visible
property bool statusVisible: reader.settings.ui.status_bar
property var collapsed: ({})
property var tocRows: []
readonly property int visibleCurrentOutline: {
var current = reader.currentOutline, visible = -1
for (var i = 0; i < tocRows.length; ++i) {
if (tocRows[i].originalIndex === current) return i
// A collapsed ancestor represents the current section below it.
if (tocRows[i].originalIndex < current && collapsed[tocRows[i].originalIndex]) {
var depth = tocRows[i].depth || 0, within = true
for (var j = tocRows[i].originalIndex + 1; j <= current; ++j)
if ((reader.outline[j].depth || 0) <= depth) { within = false; break }
if (within) visible = i
}
}
return visible
}
property bool searchInput: false
property string previousContext: ""
property var commandNames: ({
"file.open": qsTr("文書を開く"), "app.quit": qsTr("終了"),
"operation.cancel": qsTr("処理を取り消す"), "document.root.choose": qsTr("文書フォルダーを指定"),
"document.info": qsTr("文書情報"), "history.clear": qsTr("履歴を消去"),
"scroll.down": qsTr("下へスクロール"), "scroll.up": qsTr("上へスクロール"),
"scroll.left": qsTr("左へスクロール"), "scroll.right": qsTr("右へスクロール"),
"scroll.half_down": qsTr("半画面進む"), "scroll.half_up": qsTr("半画面戻る"),
"scroll.page_down": qsTr("一画面進む"), "scroll.page_up": qsTr("一画面戻る"),
"nav.document_start": qsTr("文書の先頭"), "nav.document_end": qsTr("文書の末尾"),
"nav.page": qsTr("指定ページへ移動"), "nav.page_next": qsTr("次のPDFページ"),
"nav.page_previous": qsTr("前のPDFページ"), "nav.heading_next": qsTr("次の見出し"),
"nav.heading_previous": qsTr("前の見出し"), "nav.chapter_next": qsTr("次の章"),
"nav.chapter_previous": qsTr("前の章"), "panel.toc.toggle": qsTr("目次の表示切替"),
"panel.pages.toggle": qsTr("ページ一覧の表示切替"), "panel.status.toggle": qsTr("ステータスの表示切替"),
"focus.next": qsTr("操作する領域を切替"), "focus.content": qsTr("本文に戻る"),
"zoom.in": qsTr("拡大"), "zoom.out": qsTr("縮小"), "zoom.fit_width": qsTr("幅に合わせる"),
"view.rotate": qsTr("PDFを回転"), "command.open": qsTr("コマンドを入力"),
"config.reload": qsTr("設定を再読込"), "help.toggle": qsTr("操作一覧"),
"nav.back": qsTr("移動履歴を戻る"), "nav.forward": qsTr("移動履歴を進む"),
"search.open": qsTr("本文を検索"), "search.next": qsTr("次の検索結果"),
"search.previous": qsTr("前の検索結果"), "panel.next": qsTr("次の項目を選択"),
"panel.previous": qsTr("前の項目を選択"), "panel.collapse": qsTr("階層を折りたたむ"),
"panel.expand": qsTr("階層を展開"), "panel.first": qsTr("先頭項目を選択"),
"panel.last": qsTr("末尾項目を選択"), "panel.half_down": qsTr("項目を半画面進む"),
"panel.half_up": qsTr("項目を半画面戻る"), "panel.activate": qsTr("選択項目へ移動"),
"link.next": qsTr("次のリンク・PDF注釈を選択"), "link.previous": qsTr("前のリンク・PDF注釈を選択"),
"link.activate": qsTr("選択リンク・PDF注釈を開く"), "unbound": qsTr("割り当てなし")
})
function contextName(value) {
return value === "content" ? qsTr("本文") : value === "toc" ? qsTr("目次")
: value === "pages" ? qsTr("ページ一覧") : qsTr("共通")
}
color: backgroundColor
font.family: "monospace"
font.pixelSize: reader.settings.ui.font_size
function focusContent() {
previousContext = "" // Explicit navigation takes precedence over temporary UI restoration.
reader.context = "content"
if (reader.format === "pdf") pdf.forceActiveFocus()
else if (reader.format !== "") web.focusContent()
else if (recentList.count > 0) recentList.forceActiveFocus()
else content.forceActiveFocus()
}
function beginTemporary() {
if (!previousContext) previousContext = reader.context
}
function endTemporary() {
reader.mode = "normal"
var target = previousContext
previousContext = ""
if (!target) return // A successful action already selected its own focus.
if (target === "toc" && tocVisible && tocList.count > 0) {
reader.context = "toc"; tocList.forceActiveFocus()
} else if (target === "pages" && pagesVisible && pageList.count > 0) {
reader.context = "pages"; pageList.forceActiveFocus()
} else focusContent()
}
function setInputMode(mode) { reader.mode = mode }
function updateToc(resetSelection) {
var previous = resetSelection ? null : tocRows[tocList.currentIndex]
let list = [], skipDepth = -1
for (let i = 0; i < reader.outline.length; ++i) {
let row = reader.outline[i], depth = row.depth || 0
if (skipDepth >= 0 && depth > skipDepth) continue
skipDepth = -1
let copy = Object.assign({}, row)
copy.originalIndex = i
copy.hasChildren = i + 1 < reader.outline.length && (reader.outline[i+1].depth || 0) > depth
list.push(copy)
if (collapsed[i]) skipDepth = depth
}
tocRows = list
var selected = 0
if (previous) {
for (var row = 0; row < list.length; ++row)
if (list[row].originalIndex === previous.originalIndex && list[row].title === previous.title &&
list[row].href === previous.href && list[row].page === previous.page) { selected = row; break }
}
tocList.currentIndex = list.length ? selected : -1
}
function panelCommand(id) {
let list = reader.context === "toc" ? tocList : pageList
if (list.count === 0) return
let next = list.currentIndex
if (id === "panel.next") next++
if (id === "panel.previous") next--
if (id === "panel.first") next = 0
if (id === "panel.last") next = list.count - 1
if (id === "panel.half_down") next += Math.max(1, Math.floor(list.height / 56))
if (id === "panel.half_up") next -= Math.max(1, Math.floor(list.height / 56))
if (id === "panel.activate") { reader.activateItem(list.model[list.currentIndex]); return }
if (reader.context === "toc" && (id === "panel.collapse" || id === "panel.expand")) {
let row = tocRows[list.currentIndex]
let c = Object.assign({}, collapsed)
if (id === "panel.collapse") {
if (row.hasChildren && !c[row.originalIndex]) c[row.originalIndex] = true
else for(let i = list.currentIndex-1; i >= 0; --i) if ((tocRows[i].depth || 0) < (row.depth || 0)) { next = i; break }
} else {
if (c[row.originalIndex]) delete c[row.originalIndex]
else if (row.hasChildren) next++
}
collapsed = c; updateToc()
}
list.currentIndex = Math.max(0, Math.min(list.count-1,next))
list.positionViewAtIndex(list.currentIndex,ListView.Contain)
}
function beginInput(search) {
beginTemporary(); searchInput = search
reader.mode = search ? "search" : "command"
commandField.text = ""; commandField.forceActiveFocus()
}
function recentCommand(id) {
if (!recentList.count || reader.state !== "Empty") return
if (id === "link.next" || id === "link.previous") {
if (clearRecent.activeFocus) recentList.forceActiveFocus()
else clearRecent.forceActiveFocus()
return
}
if (id === "link.activate") {
if (clearRecent.activeFocus) reader.execute("history.clear")
else if (recentList.currentIndex >= 0) reader.openRecent(recentList.model[recentList.currentIndex].documentId)
return
}
let next = recentList.currentIndex
if (id === "scroll.down") next++
if (id === "scroll.up") next--
if (id === "nav.document_start") next = 0
if (id === "nav.document_end") next = recentList.count - 1
if (id === "scroll.half_down" || id === "scroll.page_down") next += Math.max(1, Math.floor(recentList.height / 52))
if (id === "scroll.half_up" || id === "scroll.page_up") next -= Math.max(1, Math.floor(recentList.height / 52))
recentList.currentIndex = Math.max(0, Math.min(recentList.count - 1, next))
recentList.positionViewAtIndex(recentList.currentIndex, ListView.Contain)
recentList.forceActiveFocus()
}
function endInput() { endTemporary() }
Connections {
target: reader
function onNavigationChanged() { window.updateToc() }
function onDocumentChanged() { window.collapsed = ({}); window.updateToc(true) }
function onRecentDocumentsChanged() {
Qt.callLater(function() { if (reader.format === "" && reader.mode === "normal") window.focusContent() })
}
function onUiCommand(id,args) {
if(id === "file.open") { beginTemporary(); reader.mode = "dialog"; fileDialog.open() }
else if(id === "panel.toc.toggle") { tocVisible = !tocVisible; if(!tocVisible && reader.context === "toc") focusContent() }
else if(id === "panel.pages.toggle") { pagesVisible = !pagesVisible; if(!pagesVisible && reader.context === "pages") focusContent() }
else if(id === "panel.status.toggle") statusVisible = !statusVisible
else if(id.startsWith("panel.")) panelCommand(id)
else if(id === "focus.next") {
previousContext = ""; let areas=["content"]; if(tocVisible && reader.outline.length)areas.push("toc");if(pagesVisible && reader.pages.length)areas.push("pages")
reader.context=areas[(areas.indexOf(reader.context)+1)%areas.length]
if(reader.context === "toc")tocList.forceActiveFocus();else if(reader.context === "pages")pageList.forceActiveFocus();else focusContent()
}
else if(id === "focus.content") focusContent()
else if(id === "command.open") beginInput(false)
else if(id === "search.open") beginInput(true)
else if(id === "help.toggle") { beginTemporary(); reader.mode="dialog"; helpDialog.open() }
else if(id === "document.info") { beginTemporary(); infoText.text=args.text; infoDialog.title=args.title || qsTr("文書情報"); reader.mode="dialog"; infoDialog.open() }
else if(id === "document.root.choose") { beginTemporary(); reader.mode="dialog"; folderDialog.open() }
else if(id === "history.clear") { beginTemporary(); reader.mode="dialog"; historyDialog.open() }
else if(id === "recent.navigate") recentCommand(args.command)
}
function onPasswordNeeded(name) { beginTemporary(); reader.mode="dialog"; passwordDialog.title=name; passwordField.text="";passwordDialog.open();passwordField.forceActiveFocus() }
function onEntryNeeded(candidates) { beginTemporary(); candidateList.model=candidates; reader.mode="dialog"; entryDialog.open();candidateList.forceActiveFocus() }
function onExternalLinkRequested(url) { beginTemporary(); externalDialog.destination=url;reader.mode="dialog";externalDialog.open() }
function onSettingsChanged() {
tocVisible=reader.settings.ui.toc_visible;pagesVisible=reader.settings.ui.pages_visible;statusVisible=reader.settings.ui.status_bar
if ((!tocVisible && reader.context === "toc") || (!pagesVisible && reader.context === "pages")) focusContent()
}
}
ColumnLayout {
anchors.fill: parent; spacing: 0
RowLayout {
Layout.fillWidth: true; Layout.fillHeight: true; spacing: 1
ColumnLayout {
objectName: "navigationSidebar"
visible: (tocVisible && (reader.outline.length > 0 || reader.outlineLoading)) || (pagesVisible && reader.pages.length > 0)
readonly property real panelWidth: Math.min(reader.settings.ui.panel_width,window.width*0.4)
Layout.preferredWidth: panelWidth
Layout.minimumWidth: panelWidth
Layout.maximumWidth: panelWidth
Layout.fillWidth: false
Layout.fillHeight: true; spacing: 1
Rectangle {
visible: tocVisible && (reader.outline.length > 0 || reader.outlineLoading)
Layout.fillWidth:true;Layout.fillHeight:true;color:surfaceColor
border.color:reader.context === "toc" ? accentColor : surfaceColor
ColumnLayout {
anchors.fill:parent;anchors.margins:8;spacing:6
Label { objectName:"outlineTitle";text:reader.outlineTitle;color:mutedColor;font.pixelSize:12 }
Label { objectName:"outlineLoading";visible:reader.outlineLoading;text:qsTr("目次を読み込んでいます…");color:mutedColor;wrapMode:Text.Wrap;Layout.fillWidth:true;textFormat:Text.PlainText }
ListView {
id:tocList;objectName:"tocList";Layout.fillWidth:true;Layout.fillHeight:true;clip:true;model:window.tocRows;currentIndex:0
delegate: Rectangle {
required property var modelData;required property int index
objectName:"tocRow";property bool readingCurrent:window.visibleCurrentOutline === index
property bool keyboardSelected:tocList.currentIndex === index
width:ListView.view.width;height:32;color:tocList.currentIndex === index ? (dark ? "#35414b" : "#dce6ee") : "transparent"
border.width:keyboardSelected ? 1 : 0;border.color:mutedColor
Label { anchors.fill:parent;anchors.leftMargin:4+(modelData.depth||0)*14;verticalAlignment:Text.AlignVCenter;color:textColor;elide:Text.ElideRight;text:(readingCurrent ? "> " : " ")+(modelData.hasChildren ? (window.collapsed[modelData.originalIndex] ? "+ " : "− ") : " ")+(modelData.title||qsTr("見出し"));textFormat:Text.PlainText }
MouseArea { anchors.fill:parent;onClicked:{tocList.currentIndex=index;reader.context="toc";reader.activateItem(modelData)} }
}
ScrollBar.vertical:ScrollBar{}
Accessible.name:qsTr("目次一覧")
}
}
}
Rectangle {
visible:pagesVisible && reader.pages.length > 0
Layout.fillWidth:true;Layout.fillHeight:true;color:surfaceColor
border.color:reader.context === "pages" ? accentColor : surfaceColor
ColumnLayout {
anchors.fill:parent;anchors.margins:8;spacing:6
Label{text:reader.listTitle;color:mutedColor;font.pixelSize:12}
ListView {
id:pageList;Layout.fillWidth:true;Layout.fillHeight:true;clip:true;model:reader.pages;currentIndex:0
delegate:Rectangle {
required property var modelData;required property int index
width:ListView.view.width;height:32;color:pageList.currentIndex === index ? (dark ? "#35414b" : "#dce6ee") : "transparent"
Label{anchors.fill:parent;anchors.leftMargin:4;verticalAlignment:Text.AlignVCenter;color:textColor;elide:Text.ElideRight;text:(reader.format === "pdf" && pdf.currentPage === index ? "> " : " ")+(modelData.title||modelData.href||String(index+1));textFormat:Text.PlainText}
MouseArea{anchors.fill:parent;onClicked:{pageList.currentIndex=index;reader.context="pages";reader.activateItem(modelData)}}
}
ScrollBar.vertical:ScrollBar{}
Accessible.name:reader.listTitle
}
}
}
}
Item {
id:content;objectName:"documentContent";Layout.fillWidth:true;Layout.fillHeight:true;focus:true
PdfCanvas { id:pdf;anchors.fill:parent;visible:reader.format === "pdf";backgroundColor:window.backgroundColor;Component.onCompleted:reader.attachCanvas(pdf);Accessible.name:qsTr("PDF本文") }
WebPane { id:web;anchors.fill:parent;visible:reader.format !== "pdf" && reader.format !== "" }
ColumnLayout {
id:initialScreen;objectName:"initialScreen"
readonly property real availableHeight:content.height-(noticeBanner.visible ? noticeBanner.height : 0)
anchors.centerIn:parent;width:Math.min(580,parent.width-48);spacing:8;visible:reader.format === ""
anchors.verticalCenterOffset:noticeBanner.visible ? -noticeBanner.height/2 : 0
Label { text:qsTr("Ctrl+O: 文書を開く");color:textColor;Layout.fillWidth:true }
Label { text:qsTr("?: 操作一覧");color:mutedColor;Layout.fillWidth:true }
Label { visible:recentList.count>0;text:qsTr("最近使った文書");color:textColor;Layout.topMargin:12;Layout.fillWidth:true }
ListView {
id:recentList;objectName:"recentDocumentsList"
readonly property real rowHeight: Math.max(52, window.font.pixelSize+34)
Layout.fillWidth:true;Layout.preferredHeight:Math.min(260,Math.max(rowHeight,initialScreen.availableHeight-4*window.font.pixelSize-154),count*rowHeight)
visible:count>0;enabled:reader.state === "Empty";clip:true
model:reader.recentDocuments;currentIndex:0;keyNavigationEnabled:true
onCountChanged:currentIndex=count>0 ? Math.max(0,Math.min(currentIndex,count-1)) : -1
Keys.onPressed:event=>{
if (event.key === Qt.Key_Home) currentIndex=0
else if (event.key === Qt.Key_End) currentIndex=count-1
else return
positionViewAtIndex(currentIndex,ListView.Contain);event.accepted=true
}
delegate:ItemDelegate {
required property var modelData;required property int index
width:ListView.view.width;height:recentList.rowHeight;padding:6;focusPolicy:Qt.NoFocus
highlighted:recentList.currentIndex===index
background:Rectangle {
color:recentList.currentIndex===index ? (dark ? "#35414b" : "#dce6ee") : surfaceColor
border.color:recentList.activeFocus && recentList.currentIndex===index ? accentColor : "transparent"
}
contentItem:Column {
Label { width:parent.width;text:modelData.title;textFormat:Text.PlainText;color:textColor;elide:Text.ElideRight }
Label { width:parent.width;text:modelData.path;textFormat:Text.PlainText;color:mutedColor;font.pixelSize:12;elide:Text.ElideMiddle }
}
Accessible.name:modelData.title+" — "+modelData.path
onClicked: { recentList.currentIndex=index;recentList.forceActiveFocus();reader.openRecent(modelData.documentId) }
}
ScrollBar.vertical:ScrollBar{}
Accessible.name:qsTr("最近使った文書。上下キーで選択し、Enterで開きます")
}
RowLayout {
visible:recentList.count>0;Layout.fillWidth:true
Label { text:qsTr("↑↓: 選択 Enter: 開く");color:mutedColor;font.pixelSize:12;Layout.fillWidth:true }
Button { id:clearRecent;objectName:"clearRecentDocuments";text:qsTr("履歴を消去…");enabled:reader.state === "Empty";onClicked:reader.execute("history.clear") }
}
}
Rectangle {
id:noticeBanner;objectName:"noticeBanner"
anchors.left:parent.left;anchors.right:parent.right;anchors.bottom:parent.bottom
visible:reader.notice.length > 0;height:noticeLabel.implicitHeight+20;color:surfaceColor
Label { id:noticeLabel;anchors.left:parent.left;anchors.right:parent.right;anchors.margins:12;anchors.verticalCenter:parent.verticalCenter;text:reader.notice;textFormat:Text.PlainText;wrapMode:Text.Wrap;color:textColor;Accessible.role:Accessible.AlertMessage }
}
DropArea { anchors.fill:parent;onDropped:drop=>{if(drop.hasUrls){reader.open(drop.urls[0]);drop.acceptProposedAction()}} }
}
}
Rectangle {
visible:statusVisible;Layout.fillWidth:true;Layout.preferredHeight:28;color:surfaceColor
RowLayout {
anchors.fill:parent;anchors.leftMargin:10;anchors.rightMargin:10
Label { Layout.fillWidth:true;text:reader.title;elide:Text.ElideMiddle;color:mutedColor;textFormat:Text.PlainText }
Label { text:reader.state === "Opening" ? qsTr("読込中") : reader.position;color:textColor }
Label { text:reader.pending;color:accentColor }
Label { text:reader.context === "content" ? qsTr("内容") : reader.context === "toc" ? qsTr("目次") : reader.listTitle;color:mutedColor;leftPadding:18 }
}
}
RowLayout {
visible:reader.mode === "command" || reader.mode === "search"
Layout.fillWidth:true;spacing:0
Label { text:window.searchInput ? "/" : ":";color:textColor;leftPadding:10 }
TextField {
id:commandField;Layout.fillWidth:true;selectByMouse:true;color:textColor;background:Rectangle{color:surfaceColor}Accessible.name:window.searchInput ? qsTr("本文検索") : qsTr("コマンド")
placeholderText:window.searchInput ? (reader.format === "epub" ? qsTr("表示中の章を検索") : reader.format === "pdf" ? qsTr("本文を検索") : qsTr("表示中のHTMLを検索")) : ""
onAccepted:{if(inputMethodComposing)return;if(window.searchInput){reader.search(text);endInput()}else if(reader.command(text)){if(reader.mode === "command")endInput();else text=""}}
Keys.onEscapePressed:event=>{if(!inputMethodComposing){endInput();event.accepted=true}}
}
}
}
FileDialog { id:fileDialog;title:qsTr("文書を開く");nameFilters:[qsTr("文書 (*.pdf *.html *.htm *.xhtml *.zip *.epub)"),qsTr("すべて (*)")];onAccepted:{reader.mode="normal";reader.open(selectedFile);endTemporary()}onRejected:endTemporary() }
FolderDialog { id:folderDialog;title:qsTr("文書フォルダーを指定");onAccepted:{reader.mode="normal";reader.setHtmlRoot(selectedFolder);endTemporary()}onRejected:endTemporary() }
Dialog {
id:passwordDialog;anchors.centerIn:parent;modal:true;standardButtons:Dialog.Ok|Dialog.Cancel
TextField{id:passwordField;width:300;echoMode:TextInput.Password;Accessible.name:qsTr("PDFパスワード");onAccepted:if(!inputMethodComposing)passwordDialog.accept()}
onAccepted:{reader.mode="normal";let value=passwordField.text;passwordField.text="";reader.password(value);endTemporary()}
onRejected:{passwordField.text="";reader.mode="normal";reader.cancel();endTemporary()}
}
Dialog {
id:entryDialog;objectName:"entryDialog";anchors.centerIn:parent;modal:true;title:qsTr("入口HTMLを選択");width:Math.min(600,window.width-48);height:Math.min(440,window.height-60);standardButtons:Dialog.Ok|Dialog.Cancel
ListView{id:candidateList;objectName:"entryCandidates";anchors.fill:parent;clip:true;currentIndex:0;delegate:ItemDelegate{required property string modelData;required property int index;width:ListView.view.width;text:modelData;highlighted:candidateList.currentIndex===index;onClicked:{candidateList.currentIndex=index;entryDialog.accept()}}Keys.onReturnPressed:entryDialog.accept()}
onAccepted:{reader.mode="normal";if(candidateList.currentIndex>=0)reader.chooseEntry(candidateList.model[candidateList.currentIndex]);endTemporary()}
onRejected:{reader.cancel();endTemporary()}
}
Dialog {
id:externalDialog;property url destination;anchors.centerIn:parent;modal:true;title:qsTr("外部ブラウザーで開く");width:Math.min(640,window.width-48);standardButtons:Dialog.Open|Dialog.Cancel
Label{width:parent.width;text:externalDialog.destination.toString();textFormat:Text.PlainText;wrapMode:Text.WrapAnywhere}
onAccepted:{reader.openExternal(destination);endTemporary()}
onRejected:endTemporary()
}
Dialog {
id:infoDialog;objectName:"infoDialog";anchors.centerIn:parent;modal:true;title:qsTr("文書情報")
width:Math.min(640,window.width-48);height:Math.min(implicitHeight,window.height-60)
standardButtons:Dialog.Close
contentItem:ScrollView {
id:infoScroll;objectName:"infoScroll";clip:true
contentWidth:availableWidth;implicitHeight:infoText.implicitHeight
Label{id:infoText;objectName:"infoText";width:infoScroll.availableWidth;wrapMode:Text.WrapAnywhere;textFormat:Text.PlainText}
}
onClosed:endTemporary()
}
Dialog {
id:historyDialog;objectName:"historyDialog";anchors.centerIn:parent;modal:true
title:qsTr("履歴と保存位置を消去");width:Math.min(580,window.width-48);standardButtons:Dialog.Ok|Dialog.Cancel
Label { id:historyConfirmation;objectName:"historyConfirmation";width:parent.width;wrapMode:Text.Wrap }
onAboutToShow:historyConfirmation.text=qsTr("%1 件の履歴と読書位置を消去します。原本は変更されません。").arg(reader.historyCount())
onAccepted:{reader.clearHistory();endTemporary()}
onRejected:endTemporary()
}
Dialog {
id:helpDialog;anchors.centerIn:parent;modal:true;title:qsTr("操作一覧");width:Math.min(760,window.width-48);height:Math.min(620,window.height-60);standardButtons:Dialog.Close
ColumnLayout {
anchors.fill:parent
Label{text:qsTr("Vim風の閲覧操作です。q単押しでは終了しません。:q で終了します。");wrapMode:Text.Wrap;Layout.fillWidth:true}
ListView {
Layout.fillWidth:true;Layout.fillHeight:true;clip:true;model:reader.settings.keybindings
delegate:Label{required property var modelData;width:ListView.view.width;text:modelData.keys.join(" ")+" "+(window.commandNames[modelData.command] || modelData.command)+" ["+window.contextName(modelData.context)+"]";textFormat:Text.PlainText;font.family:"monospace";height:26;elide:Text.ElideRight}
ScrollBar.vertical:ScrollBar{}
}
}
onClosed:endTemporary()
}
Shortcut { sequence:"?";enabled:helpDialog.visible;onActivated:helpDialog.close() }
Component.onCompleted:{updateToc();focusContent()}
}