Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

notes script #1287

Merged
merged 15 commits into from
Sep 6, 2024
Merged
Show file tree
Hide file tree
Changes from 8 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions internal/journal/table_of_contents.lua
Original file line number Diff line number Diff line change
Expand Up @@ -37,12 +37,12 @@ function TableOfContents:init()

local function can_prev()
local toc = self.subviews.table_of_contents
return #toc:getChoices() > 0 and toc:getSelected() > 1
return #toc:getChoices() > 0
end
local function can_next()
local toc = self.subviews.table_of_contents
local num_choices = #toc:getChoices()
return num_choices > 0 and toc:getSelected() < num_choices
return num_choices > 0
end

self:addviews{
Expand Down
10 changes: 8 additions & 2 deletions internal/journal/text_editor.lua
Original file line number Diff line number Diff line change
Expand Up @@ -169,14 +169,14 @@ function TextEditor:setCursor(cursor_offset)
end

function TextEditor:getPreferredFocusState()
return true
return self.parent_view.focus
end

function TextEditor:postUpdateLayout()
self:updateScrollbar(self.render_start_line_y)

if self.subviews.text_area.cursor == nil then
local cursor = self.init_cursor or #self.text + 1
local cursor = self.init_cursor or #self.init_text + 1
self.subviews.text_area:setCursor(cursor)
self:scrollToCursor(cursor)
end
Expand Down Expand Up @@ -234,6 +234,10 @@ function TextEditor:onInput(keys)
return self.subviews.scrollbar:onInput(keys)
end

if keys._MOUSE_L and self:getMousePos() then
self:setFocus(true)
end

return TextEditor.super.onInput(self, keys)
end

Expand Down Expand Up @@ -629,6 +633,8 @@ function TextEditorView:onInput(keys)
self:paste()
self.history:store(HISTORY_ENTRY.OTHER, self.text, self.cursor)
return true
else
return TextEditor.super.onInput(self, keys)
end
end

Expand Down
324 changes: 324 additions & 0 deletions notes.lua
Original file line number Diff line number Diff line change
@@ -0,0 +1,324 @@
--@ module = true

local gui = require('gui')
local widgets = require('gui.widgets')
local textures = require('gui.textures')
local overlay = require('plugins.overlay')
local guidm = require('gui.dwarfmode')
local text_editor = reqscript('internal/journal/text_editor')

local green_pin = dfhack.textures.loadTileset('hack/data/art/note-green-pin.png', 32, 32, true)

NotesOverlay = defclass(NotesOverlay, overlay.OverlayWidget)
NotesOverlay.ATTRS{
desc='Render map notes.',
viewscreens='dwarfmode',
default_enabled=true,
overlay_onupdate_max_freq_seconds=30,
}

local waypoints = df.global.plotinfo.waypoints
local map_points = df.global.plotinfo.waypoints.points

function NotesOverlay:init()
self.notes = {}
self.note_manager = nil
self.last_click_pos = {}
self:reloadVisibleNotes()
end

function NotesOverlay:overlay_onupdate()
self:reloadVisibleNotes()
end

function NotesOverlay:overlay_trigger(args)
return self:showNoteManager()
end

function NotesOverlay:onInput(keys)
if keys._MOUSE_L then
local top_most_screen = dfhack.gui.getDFViewscreen(true)
if dfhack.gui.matchFocusString('dwarfmode/Default', top_most_screen) then
local pos = dfhack.gui.getMousePos()

local note = self:clickedNote(pos)
if note ~= nil then
self:showNoteManager(note)
end
end
end
end

function NotesOverlay:clickedNote(click_pos)
local pos_curr_note = same_xyz(self.last_click_pos, click_pos)
myk002 marked this conversation as resolved.
Show resolved Hide resolved
and self.note_manager
and self.note_manager.note
or nil

self.last_click_pos = click_pos

local last_note_on_pos = nil
local first_note_on_pos = nil
for _, note in ipairs(self.notes) do
myk002 marked this conversation as resolved.
Show resolved Hide resolved
if same_xyz(note.point.pos, click_pos) then
if (last_note_on_pos and pos_curr_note
and last_note_on_pos.point.id == pos_curr_note.point.id
) then
return note
end

first_note_on_pos = first_note_on_pos or note
last_note_on_pos = note
end
end

return first_note_on_pos
end

function NotesOverlay:showNoteManager(note)
if self.note_manager ~= nil then
self.note_manager:dismiss()
end

self.note_manager = NoteManager{
note=note,
on_update=function() self:reloadVisibleNotes() end
}

return self.note_manager:show()
end

function NotesOverlay:viewportChanged()
return self.viewport_pos.x ~= df.global.window_x or
self.viewport_pos.y ~= df.global.window_y or
self.viewport_pos.z ~= df.global.window_z
end

function NotesOverlay:onRenderFrame(dc)
if not df.global.pause_state and not dfhack.screen.inGraphicsMode() then
return
end

if self:viewportChanged() then
self:reloadVisibleNotes()
end

dc:map(true)

local texpos = dfhack.textures.getTexposByHandle(green_pin[1])
dc:pen({fg=COLOR_BLACK, bg=COLOR_LIGHTCYAN, tile=texpos})

for _, note in pairs(self.notes) do
dc
:seek(note.screen_pos.x, note.screen_pos.y)
:char('N')
end

dc:map(false)
end

function NotesOverlay:reloadVisibleNotes()
self.notes = {}

local viewport = guidm.Viewport.get()
self.viewport_pos = {
x=df.global.window_x,
y=df.global.window_y,
z=df.global.window_z
}

for _, map_point in ipairs(map_points) do
if viewport:isVisible(map_point.pos) then
local screen_pos = viewport:tileToScreen(map_point.pos)
table.insert(self.notes, {
point=map_point,
screen_pos=screen_pos
})
end
end
end

NoteManager = defclass(NoteManager, gui.ZScreen)
NoteManager.ATTRS{
focus_path='notes/note-manager',
note=DEFAULT_NIL,
on_update=DEFAULT_NIL,
}

function NoteManager:init()
local edit_mode = self.note ~= nil

self:addviews{
widgets.Window{
frame={w=35,h=20},
frame_inset={t=1},
subviews={
widgets.HotkeyLabel {
myk002 marked this conversation as resolved.
Show resolved Hide resolved
key='CUSTOM_ALT_N',
label='Name',
frame={t=0},
on_activate=function() self.subviews.name:setFocus(true) end,
},
text_editor.TextEditor{
view_id='name',
frame={t=1,h=3},
frame_style=gui.FRAME_INTERIOR,
init_text=self.note and self.note.point.name or '',
init_cursor=1
},
widgets.HotkeyLabel {
myk002 marked this conversation as resolved.
Show resolved Hide resolved
key='CUSTOM_ALT_C',
label='Comment',
frame={t=5},
on_activate=function() self.subviews.comment:setFocus(true) end,
},
text_editor.TextEditor{
view_id='comment',
frame={t=6,b=3},
frame_style=gui.FRAME_INTERIOR,
init_text=self.note and self.note.point.comment or '',
init_cursor=1
},
widgets.Panel{
view_id='buttons',
frame={b=0,h=2},
autoarrange_subviews=true,
subviews={
widgets.HotkeyLabel{
myk002 marked this conversation as resolved.
Show resolved Hide resolved
view_id='Save',
frame={h=1},
label='Save',
key='CUSTOM_ALT_S',
visible=edit_mode,
on_activate=function() self:saveNote() end,
enabled=function() return #self.subviews.name:getText() > 0 end,
},
widgets.HotkeyLabel{
view_id='Create',
frame={h=1},
label='Create',
key='CUSTOM_ALT_S',
visible=not edit_mode,
on_activate=function() self:createNote() end,
enabled=function() return #self.subviews.name:getText() > 0 end,
},
widgets.HotkeyLabel{
view_id='delete',
frame={h=1},
label='Delete',
key='CUSTOM_ALT_D',
visible=edit_mode,
on_activate=function() self:deleteNote() end,
} or nil,
}
}
},
},
}
end

function NoteManager:createNote()
local cursor_pos = guidm.getCursorPos()
if cursor_pos == nil then
print('Enable keyboard cursor to add a note.')
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A mouse-capable way of adding a note is generally expected of current tools. See gui/blueprint for a possible example. You click on a button and then click on the map. while the location is being selected, a marker is rendered under the mouse cursor: https://github.com/DFHack/scripts/blob/master/gui/blueprint.lua#L427

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

seems very good, but for now we do not have any notes interface where such button can be placed.
Do you think I should replace notes add command by mouse cursor selection?
Or this should stay as it is and the new way should appear after there will be a gui/notes for managing notes?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think there should be a gui/notes entrypoint, but I also think that this dialog should have a way to create (and switch to editing) an additional note

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This would be confusing.

  1. You clicked on a note to edit it and in a dialog you have option "New note"? Did you current changes to note saved or not? Do it switch to new note overlay mouse hover mode? etc.

  2. You creating a new note, and got to moment when you see this dialog. Then you switch to editing. What this really means? You are forced to select a note on the screen now? Its sounds very confusing.
    If you want to switch to editing - just select a note on the map. You can do this right now, do not need any additional wat to "switch" to it.
    --
    I never met such features in my web/desktop experience. Edit dialog is for editing, new dialog is for creating.

I believe the needs you speak about will be fully solved by gui/notes tool.
I though I can create it later, but maybe it should be done from the begining to avoid the confusion.

myk002 marked this conversation as resolved.
Show resolved Hide resolved
return
end

local name = self.subviews.name:getText()
local comment = self.subviews.comment:getText()

if #name == 0 then
dfhack.printerr('Note need at least a name')
return
end

map_points:insert("#", {
new=true,

id = waypoints.next_point_id,
tile=88,
fg_color=7,
bg_color=0,
name=name,
comment=comment,
pos=cursor_pos
})
waypoints.next_point_id = waypoints.next_point_id + 1

if self.on_update then
self.on_update()
end

self:dismiss()
end

function NoteManager:saveNote()
if self.note == nil then
return
end

local name = self.subviews.name:getText()
local comment = self.subviews.comment:getText()

if #name == 0 then
dfhack.printerr('Note need at least a name')
return
end

self.note.point.name = name
self.note.point.comment = comment

if self.on_update then
self.on_update()
end

self:dismiss()
end

function NoteManager:deleteNote()
if self.note == nil then
return
end

for ind, map_point in pairs(map_points) do
if map_point.id == self.note.point.id then
map_points:erase(ind)
break
end
end

if self.on_update then
self.on_update()
end

self:dismiss()
end

function NoteManager:onDismiss()
self.note = nil
end

-- register widgets
OVERLAY_WIDGETS = {
map_notes=NotesOverlay
}

local function main(args)
if #args == 0 then
return
end

if args[1] == 'add' then
local cursor_pos = guidm.getCursorPos()
if cursor_pos == nil then
dfhack.printerr('Enable keyboard cursor to add a note.')
return
end

return dfhack.internal.runCommand('overlay trigger notes.map_notes')
end
end

if not dfhack_flags.module then
main({...})
end
Loading