Skip to content

Commit

Permalink
Merge pull request #1243 from wiktor-obrebski/gui/journal-ToC
Browse files Browse the repository at this point in the history
Add table of contents to Gui/journal
  • Loading branch information
myk002 authored Aug 3, 2024
2 parents a6a452a + 61ec67d commit 1b2988c
Show file tree
Hide file tree
Showing 6 changed files with 1,335 additions and 323 deletions.
5 changes: 4 additions & 1 deletion docs/gui/journal.rst
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ Supported Features
------------------

- Cursor Control: Navigate through text using arrow keys (left, right, up, down) for precise cursor placement.
- Fast Rewind: Use :kbd:`Shift` + :kbd:`Left` / :kbd:`Ctrl` + :kbd:`B` and :kbd:`Shift` + :kbd:`Right` / :kbd:`Ctrl` + :kbd:`F` to move the cursor one word back or forward.
- Fast Rewind: Use :kbd:`Ctrl` + :kbd:`Left` / :kbd:`Ctrl` + :kbd:`B` and :kbd:`Ctrl` + :kbd:`Right` / :kbd:`Ctrl` + :kbd:`F` to move the cursor one word back or forward.
- Longest X Position Memory: The cursor remembers the longest x position when moving up or down, making vertical navigation more intuitive.
- Mouse Control: Use the mouse to position the cursor within the text, providing an alternative to keyboard navigation.
- New Lines: Easily insert new lines using the :kbd:`Enter` key, supporting multiline text input.
Expand All @@ -35,6 +35,7 @@ Supported Features
- Jump to Beginning/End: Quickly move the cursor to the beginning or end of the text using :kbd:`Shift` + :kbd:`Up` and :kbd:`Shift` + :kbd:`Down`.
- Select Word/Line: Use double click to select current word, or triple click to select current line
- Select All: Select entire text by :kbd:`Ctrl` + :kbd:`A`
- Undo/Redo: Undo/Redo changes by :kbd:`Ctrl` + :kbd:`Z` / :kbd:`Ctrl` + :kbd:`Y`
- Clipboard Operations: Perform OS clipboard cut, copy, and paste operations on selected text, allowing you to paste the copied content into other applications.
- Copy Text: Use :kbd:`Ctrl` + :kbd:`C` to copy selected text.
- copy selected text, if available
Expand All @@ -46,6 +47,8 @@ Supported Features
- replace selected text, if available
- If no text is selected, paste text in the cursor position
- Scrolling behaviour for long text build-in
- Table of contents (:kbd:`Ctrl` + :kbd:`O`), with headers line prefixed by '#', e.g. '# Fort history', '## Year 1'
- Table of contents navigation: jump to previous/next section by :kbd:`Ctrl` + :kbd:`Up` / :kbd:`Ctrl` + :kbd:`Down`

Usage
-----
Expand Down
261 changes: 229 additions & 32 deletions gui/journal.lua
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ local widgets = require 'gui.widgets'
local utils = require 'utils'
local json = require 'json'
local text_editor = reqscript('internal/journal/text_editor')
local shifter = reqscript('internal/journal/shifter')
local table_of_contents = reqscript('internal/journal/table_of_contents')

local RESIZE_MIN = {w=32, h=10}

Expand All @@ -18,12 +20,98 @@ JournalWindow.ATTRS {
frame_title='DF Journal',
resizable=true,
resize_min=RESIZE_MIN,
frame_inset=0
frame_inset={l=0,r=0,t=0,b=0},
init_text=DEFAULT_NIL,
init_cursor=1,
save_layout=true,

on_text_change=DEFAULT_NIL,
on_cursor_change=DEFAULT_NIL,
on_layout_change=DEFAULT_NIL
}

function JournalWindow:init()
local config_frame = copyall(journal_config.data.frame or {})
self.frame = self:sanitizeFrame(config_frame)
local frame, toc_visible, toc_width = self:loadConfig()

self.frame = frame and self:sanitizeFrame(frame) or self.frame

self:addviews({
table_of_contents.TableOfContents{
view_id='table_of_contents_panel',
frame={l=0, w=toc_width, t=0, b=1},
visible=toc_visible,
frame_inset={l=1, t=0, b=1, r=1},

resize_min={w=20},
resizable=true,
resize_anchors={l=false, t=false, b=true, r=true},

on_resize_begin=self:callback('onPanelResizeBegin'),
on_resize_end=self:callback('onPanelResizeEnd'),

on_submit=self:callback('onTableOfContentsSubmit')
},
shifter.Shifter{
view_id='shifter',
frame={l=0, w=1, t=1, b=2},
collapsed=not toc_visible,
on_changed = function (collapsed)
self.subviews.table_of_contents_panel.visible = not collapsed
self.subviews.table_of_contents_divider.visible = not collapsed

if not colllapsed then
self.subviews.table_of_contents_panel:reload(
self.subviews.journal_editor:getText(),
self.subviews.journal_editor:getCursor()
)
end

self:ensurePanelsRelSize()
self:updateLayout()
end,
},
widgets.Divider{
frame={l=0,r=0,b=2,h=1},
frame_style_l=false,
frame_style_r=false,
interior_l=true,
},
widgets.Divider{
view_id='table_of_contents_divider',

frame={l=30,t=0,b=2,w=1},
visible=toc_visible,

interior_b=true,
frame_style_t=false,
},
text_editor.TextEditor{
view_id='journal_editor',
frame={t=1, b=3, l=25, r=0},
resize_min={w=30, h=10},
frame_inset={l=1,r=0},
init_text=self.init_text,
init_cursor=self.init_cursor,
on_text_change=self:callback('onTextChange'),
on_cursor_change=self:callback('onCursorChange'),
},
widgets.Panel{
frame={l=0,r=0,b=1,h=1},
frame_inset={l=1,r=1,t=0, w=100},
subviews={
widgets.HotkeyLabel{
key='CUSTOM_CTRL_O',
label='Table of Contents',
on_activate=function() self.subviews.shifter:toggle() end
}
}
}
})

self.subviews.table_of_contents_panel:reload(
self.init_text,
self.subviews.journal_editor:getCursor() or self.init_cursor
)
end

function JournalWindow:sanitizeFrame(frame)
Expand All @@ -48,69 +136,178 @@ function JournalWindow:sanitizeFrame(frame)
return frame
end

function JournalWindow:postUpdateLayout()
self:saveConfig()
end

function JournalWindow:saveConfig()
if not self.save_layout then
return
end

local toc = self.subviews.table_of_contents_panel

utils.assign(journal_config.data, {
frame = self.frame
frame = self.frame,
toc = {
width = toc.frame.w,
visible = toc.visible
}
})
journal_config:write()
end

function JournalWindow:loadConfig()
if not self.save_layout then
return nil, false, 25
end

local window_frame = copyall(journal_config.data.frame or {})
window_frame.w = window_frame.w or 80
window_frame.h = window_frame.h or 50

local table_of_contents = copyall(journal_config.data.toc or {})
table_of_contents.width = table_of_contents.width or 20
table_of_contents.visible = table_of_contents.visible or false

return window_frame, table_of_contents.visible or false, table_of_contents.width or 25
end

function JournalWindow:onPanelResizeBegin()
self.resizing_panels = true
end

function JournalWindow:onPanelResizeEnd()
self.resizing_panels = false
self:ensurePanelsRelSize()

self:updateLayout()
end

function JournalWindow:onRenderBody(painter)
if self.resizing_panels then
self:ensurePanelsRelSize()
self:updateLayout()
end

return JournalWindow.super.onRenderBody(self, painter)
end

function JournalWindow:ensurePanelsRelSize()
local toc_panel = self.subviews.table_of_contents_panel
local editor = self.subviews.journal_editor
local divider = self.subviews.table_of_contents_divider

toc_panel.frame.w = math.min(
math.max(toc_panel.frame.w, toc_panel.resize_min.w),
self.frame.w - editor.resize_min.w
)
editor.frame.l = toc_panel.visible and toc_panel.frame.w or 1
divider.frame.l = editor.frame.l - 1
end

function JournalWindow:preUpdateLayout()
self:ensurePanelsRelSize()
end

function JournalWindow:postUpdateLayout()
self:saveConfig()
end

function JournalWindow:onCursorChange(cursor)
self.subviews.table_of_contents_panel:setCursor(cursor)
local section_index = self.subviews.table_of_contents_panel:currentSection()
self.subviews.table_of_contents_panel:setSelectedSection(section_index)

if self.on_cursor_change ~= nil then
self.on_cursor_change(cursor)
end
end

function JournalWindow:onTextChange(text)
self.subviews.table_of_contents_panel:reload(
text,
self.subviews.journal_editor:getCursor()
)

if self.on_text_change ~= nil then
self.on_text_change(text)
end
end

function JournalWindow:onTableOfContentsSubmit(ind, section)
self.subviews.journal_editor:setCursor(section.line_cursor)
self.subviews.journal_editor:scrollToCursor(section.line_cursor)
end

JournalScreen = defclass(JournalScreen, gui.ZScreen)
JournalScreen.ATTRS {
focus_path='journal',
save_on_change=true
save_on_change=true,
save_layout=true,
save_prefix=''
}

function JournalScreen:init(options)
local content = self:loadContextContent()
function JournalScreen:init()
local context = self:loadContext()

self:addviews{
JournalWindow{
view_id='journal_window',
frame_title='DF Journal',
frame={w=65, h=45},
resize_min={w=50, h=20},
resizable=true,
resize_min={w=32, h=10},
frame_inset=0,
subviews={
text_editor.TextEditor{
view_id='journal_editor',
frame={l=1, t=1, b=1, r=0},
text=content,
on_change=function(text) self:saveContextContent(text) end
}
}
}

save_layout=self.save_layout,

init_text=context.text[1],
init_cursor=context.cursor[1],

on_text_change=self:callback('saveContext'),
on_cursor_change=self:callback('saveContext')
},
}
end

function JournalScreen:loadContextContent()
local site_data = dfhack.persistent.getSiteData(JOURNAL_PERSIST_KEY) or {
text = {''}
}
return site_data.text ~= nil and site_data.text[1] or ''
function JournalScreen:loadContext()
local site_data = dfhack.persistent.getSiteData(
self.save_prefix .. JOURNAL_PERSIST_KEY
) or {}
site_data.text = site_data.text or {''}
site_data.cursor = site_data.cursor or {#site_data.text[1] + 1}

return site_data
end

function JournalScreen:saveContextContent(text)
function JournalScreen:onTextChange(text)
self:saveContext(text)
end

function JournalScreen:saveContext()
if self.save_on_change and dfhack.isWorldLoaded() then
dfhack.persistent.saveSiteData(JOURNAL_PERSIST_KEY, {text={text}})
local text = self.subviews.journal_editor:getText()
local cursor = self.subviews.journal_editor:getCursor()

dfhack.persistent.saveSiteData(
self.save_prefix .. JOURNAL_PERSIST_KEY,
{text={text}, cursor={cursor}}
)
end
end

function JournalScreen:onDismiss()
view = nil
end

function main()
function main(options)
if not dfhack.isMapLoaded() or not dfhack.world.isFortressMode() then
qerror('journal requires a fortress map to be loaded')
end

view = view and view:raise() or JournalScreen{}:show()
local save_layout = options and options.save_layout
local save_on_change = options and options.save_on_change

view = view and view:raise() or JournalScreen{
save_prefix=options and options.save_prefix or '',
save_layout=save_layout == nil and true or save_layout,
save_on_change=save_on_change == nil and true or save_on_change,
}:show()
end

if not dfhack_flags.module then
Expand Down
Loading

0 comments on commit 1b2988c

Please sign in to comment.