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

search-filter.lua #846

Closed
wants to merge 11 commits into from
108 changes: 108 additions & 0 deletions search-filter.lua
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
-- Required libraries
local gui = require('gui') -- Importing the 'gui' library
local widgets = require('gui.widgets') -- Importing the 'widgets' library

-- Define SearchEngine class
SearchEngine = defclass(SearchEngine, widgets.Window) -- Defining a new class 'SearchEngine' that inherits from 'widgets.Window'
SearchEngine.ATTRS = {
frame_title='Search Engine for Fort-Controlled Units', -- Title of the frame
frame={w=50, h=45}, -- Dimensions of the frame
resizable=true, -- Frame can be resized
resize_min={w=43, h=20}, -- Minimum dimensions when resizing
}

function SearchEngine:init() -- Initialization function for the SearchEngine class
self:addviews{
widgets.EditField{
view_id='edit',
frame={t=1, l=1}, -- Position of the EditField view
text='', -- Initial text in the EditField view
on_change=self:callback('updateList'), -- Callback function when text in EditField changes
},
widgets.List{
view_id='list',
frame={t=3, b=0},
choices=self:getFortControlledUnits(), -- Choices in the List view are obtained from getFortControlledUnits function
on_select=self:callback('onSelect'), -- Callback function when a unit is selected from the list
}
Comment on lines +16 to +27
Copy link
Member

Choose a reason for hiding this comment

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

This should be a widgets.FilteredList

Copy link
Author

Choose a reason for hiding this comment

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

Neat. Idk how to properly change it on the git hub or the code. I'm ok with the code being taken over by someone else.

}
end

function SearchEngine:getFortControlledUnits()
local fortControlledUnits = {}
for _, unit in ipairs(df.global.world.units.active) do
local unitName = dfhack.TranslateName(dfhack.units.getVisibleName(unit))
if unitName == "" then
-- Use the race name if the unit's name field is empty
unitName = dfhack.units.getRaceName(unit)
end
Comment on lines +34 to +38
Copy link
Member

Choose a reason for hiding this comment

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

dfhack.units.getReadableName(unit) would be a better fit here

Copy link
Author

Choose a reason for hiding this comment

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

I added excessive comments because I'm not a programmer. These comments in the code are most likely wrong, and I'm open to having them all removed.

Regarding the program's functionality, it successfully realized my initial vision, which makes me satisfied. Additionally, I believe it's crucial to maintain the graphical user interface for the search filter program, as it offers an accessibility feature that I would greatly miss.

Copy link
Author

Choose a reason for hiding this comment

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

Also I'm not interested in any recognition/credit so if someone is interested in taking all the code and calling it their own. I'm 100% happy with that. I just want others to enjoy the ability to ctrl+f aka use a search engine.

Copy link
Author

Choose a reason for hiding this comment

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

Also feel free to contact me on discord: unboundlopez

if dfhack.units.isVisible(unit) and dfhack.units.isActive(unit) then
table.insert(fortControlledUnits, {text=unitName, search_normalized=dfhack.toSearchNormalized(unitName), id=unit.id})
Copy link
Member

Choose a reason for hiding this comment

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

set search_key instead of search_normalized, and you don't need to normalize the key yourself with toSearchNormalized. that is handled by widgets.FilteredList

end
end
table.sort(fortControlledUnits, function(a, b) return a.text < b.text end)
return fortControlledUnits
end

function SearchEngine:updateList()
local input = dfhack.toSearchNormalized(self.subviews.edit.text)
local fortControlledUnits = self:getFortControlledUnits()
local filtered_fortControlledUnits = {}

for _, unit in ipairs(fortControlledUnits) do
if string.find(unit.search_normalized, input) then
table.insert(filtered_fortControlledUnits, unit)
end
end

self.subviews.list:setChoices(filtered_fortControlledUnits)
end
Comment on lines +47 to +59
Copy link
Member

Choose a reason for hiding this comment

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

this can be removed once you move from widgets.List to widgets.FilteredList


function SearchEngine:onSelect(index, unit)
Copy link
Member

Choose a reason for hiding this comment

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

please fix the indentation for this function

-- Assign the unit to a specific id
local unit = df.unit.find(unit.id)

-- Get the position of the unit and center the camera on the unit
local x, y, z = dfhack.units.getPosition(unit)
dfhack.gui.revealInDwarfmodeMap(xyz2pos(x, y, z), true)

-- Get the dimensions of the Dwarf Fortress map
local dims = dfhack.gui.getDwarfmodeViewDims()
Copy link
Member

Choose a reason for hiding this comment

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

if you're using the global window_x and window_y fields (which is fine), you don't need this line

-- Calculate zoom factor based on current viewport zoom level
local gpsZoom = df.global.gps.viewport_zoom_factor
-- Set the mouse x and y positions to click on the unit
df.global.gps.precise_mouse_x = (x - df.global.window_x) * gpsZoom // 4 + gpsZoom // 8
df.global.gps.precise_mouse_y = (y - df.global.window_y) * gpsZoom // 4 + gpsZoom // 8
Comment on lines +74 to +75
Copy link
Member

Choose a reason for hiding this comment

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

please factor this out into a function instead of duplicating the logic


-- Enable mouse tracking and set the left mouse button as pressed
df.global.enabler.tracking_on = 1
df.global.enabler.mouse_lbut = 1
df.global.enabler.mouse_lbut_down = 1
Comment on lines +77 to +80
Copy link
Member

Choose a reason for hiding this comment

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

Suggested change
-- Enable mouse tracking and set the left mouse button as pressed
df.global.enabler.tracking_on = 1
df.global.enabler.mouse_lbut = 1
df.global.enabler.mouse_lbut_down = 1

Copy link
Member

Choose a reason for hiding this comment

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

mouse button state is handled by gui.simulateInput()


-- Simulate a left mouse click at the current mouse position
gui.simulateInput(dfhack.gui.getDFViewscreen(), '_MOUSE_R')
Copy link
Member

Choose a reason for hiding this comment

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

why are you simulating a right click here?

Copy link
Author

Choose a reason for hiding this comment

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

The reason I simulate a right click there is sometimes the program clicks on a object like a object or unit that doesn't want to get off the screen. Therefore, a right click somewhere in the screen closes out that pesky character sheet of the object that doesn't want to close.


gui.simulateInput(dfhack.gui.getDFViewscreen(), '_MOUSE_L')
Copy link
Member

Choose a reason for hiding this comment

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

some more logic is needed here to make sure that the unit is the selected focus of the panel. if more than one "thing" is on that tile, it might be an item or a different unit that is selected. You'd need to also click on the "tab" to bring up the appropriate unit:
image

Copy link
Member

Choose a reason for hiding this comment

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

note that you can't just change df.global.game.main_interface.view_sheets.active_id because that won't update the displayed text

Copy link
Author

Choose a reason for hiding this comment

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

It is upsetting having the search filter bring up multiple units/objects when they are on top of each other. However, I personally don't mind clicking on the stacked pile of dwarf's/animals/monsters as I don't know how to bring up the searched name to the forefront when this occurs.


-- Disable mouse tracking and set the left mouse button as not pressed
df.global.enabler.tracking_on = 0
df.global.enabler.mouse_lbut = 0
df.global.enabler.mouse_lbut_down = 0
Comment on lines +87 to +90
Copy link
Member

Choose a reason for hiding this comment

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

Suggested change
-- Disable mouse tracking and set the left mouse button as not pressed
df.global.enabler.tracking_on = 0
df.global.enabler.mouse_lbut = 0
df.global.enabler.mouse_lbut_down = 0

Copy link
Member

Choose a reason for hiding this comment

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

gui.simulateInput() also handles mouse state restoration after sending input to the target screen


end

-- Screen creation
SearchEngineScreen = defclass(SearchEngineScreen, gui.ZScreen)
SearchEngineScreen.ATTRS = {
focus_path='SearchEngine',
Copy link
Member

Choose a reason for hiding this comment

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

focus path should match the script name (search-filter, or whatever we decide to rename this to)

}

function SearchEngineScreen:init()
self:addviews{SearchEngine{}}
end

function SearchEngineScreen:onDismiss()
view = nil
end

view = view and view:raise() or SearchEngineScreen{}:show()
Loading