-
Notifications
You must be signed in to change notification settings - Fork 0
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
Add a filter panel #196
base: master
Are you sure you want to change the base?
Add a filter panel #196
Changes from all commits
0b76808
92daab0
14b4e6a
e657351
99bc3b9
9318fe2
5613fb4
d40b97b
5b78f44
7a5d9fe
6ea64ee
fb590f6
5e0c745
126459d
8ae7c50
7bcda61
1a306fb
de30c33
d7f80bf
f48d099
c412bb0
9da6563
52f5b6a
d0fe77c
bdaf679
9651b95
28e909a
5d5dffa
e18552b
7b08235
0cff312
953a44f
cd0342a
802c07f
5ca3090
0a0bf4d
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,27 +1,45 @@ | ||
package solve | ||
|
||
import de.codecentric.centerdevice.javafxsvg.SvgImageLoaderFactory | ||
import javafx.stage.Stage | ||
import solve.main.MainView | ||
import solve.scene.view.landmarks.AnimationProvider | ||
import solve.scene.view.landmarks.JavaFXAnimationProvider | ||
import solve.styles.ApplicationStylesheet | ||
import solve.utils.SVGImageLoaderDimensionProvider | ||
import solve.utils.ServiceLocator | ||
import tornadofx.App | ||
import tornadofx.FX.Companion.stylesheets | ||
import tornadofx.launch | ||
|
||
class SolveApp : App(MainView::class) { | ||
class SolveApp : App(MainView::class, ApplicationStylesheet::class) { | ||
override fun start(stage: Stage) { | ||
initializeDependencies() | ||
registerServices() | ||
|
||
with(stage) { | ||
width = 1000.0 | ||
height = 600.0 | ||
isMaximized = true | ||
} | ||
registerServices() | ||
super.start(stage) | ||
} | ||
|
||
private fun registerServices() { | ||
ServiceLocator.registerService<AnimationProvider>(JavaFXAnimationProvider()) | ||
} | ||
|
||
private fun initializeDependencies() { | ||
SvgImageLoaderFactory.install(SVGImageLoaderDimensionProvider()) | ||
|
||
initializeStyle() | ||
} | ||
|
||
private fun initializeStyle() { | ||
stylesheets.add("https://fonts.googleapis.com/css2?family=Roboto+Condensed") | ||
stylesheets.add("https://fonts.googleapis.com/css2?family=Roboto+Condensed:wght@700") | ||
stylesheets.add("https://fonts.googleapis.com/css2?family=Roboto") | ||
} | ||
} | ||
|
||
fun main(args: Array<String>) = launch<SolveApp>(args) |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -27,3 +27,9 @@ const val IconsLayers = "/icons/sidepanel/Layers.png" | |
const val IconsLayersFilled = "/icons/sidepanel/LayersFilled.png" | ||
const val IconsGrid = "/icons/sidepanel/Grid.png" | ||
const val IconsGridSelected = "/icons/sidepanel/GridSelected.png" | ||
Comment on lines
27
to
29
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Create an issue to convert all icons to svg There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Done |
||
const val IconsFiltersAddPath = "icons/filters/add.svg" | ||
const val IconsFiltersOrCheckBoxPath = "icons/filters/or_checkbox.svg" | ||
|
||
// Common icons. | ||
const val IconsEditPath = "icons/common/edit.svg" | ||
const val IconsDeletePath = "icons/common/delete.svg" |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,59 @@ | ||
package solve.filters.controller | ||
|
||
import solve.catalogue.controller.CatalogueController | ||
import solve.filters.model.Filter | ||
import solve.filters.model.FilterPanelModel | ||
import solve.project.controller.ProjectController | ||
import solve.project.model.ProjectFrame | ||
import tornadofx.* | ||
|
||
class FilterPanelController : Controller() { | ||
val model = FilterPanelModel() | ||
|
||
private val projectController: ProjectController by inject() | ||
private val catalogueController: CatalogueController by inject() | ||
|
||
init { | ||
addBindings() | ||
} | ||
|
||
fun addFilter(filter: Filter) { | ||
model.addFilter(filter) | ||
} | ||
|
||
fun removeFilter(filter: Filter) { | ||
model.removeFilter(filter) | ||
} | ||
|
||
fun editFilter(editingFilter: Filter, editedFilter: Filter) { | ||
model.replaceFilter(editingFilter, editedFilter) | ||
} | ||
|
||
fun applyFilters() { | ||
val filteredFrames = getFilteredProjectFrames() | ||
catalogueController.setCatalogueFrames(filteredFrames) | ||
} | ||
|
||
private fun addBindings() { | ||
projectController.model.projectProperty.onChange { | ||
applyFilters() | ||
Comment on lines
+38
to
+39
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Is there any case when a lot of updates happen at once? |
||
} | ||
model.filters.onChange { | ||
applyFilters() | ||
} | ||
} | ||
|
||
private fun getFilteredProjectFrames(): List<ProjectFrame> { | ||
val projectFrames = projectController.model.project.frames | ||
|
||
if (model.filters.isEmpty() || model.filters.all { !it.enabled }) { | ||
return projectFrames | ||
} | ||
|
||
val enabledFilters = model.filters.filter { it.enabled } | ||
val notOrderedFilteredFrames = enabledFilters.map { it.apply(projectFrames) }.flatten().distinct() | ||
val projectFrameToIndexMap = projectFrames.indices.associateBy { index -> projectFrames[index] } | ||
|
||
return notOrderedFilteredFrames.sortedBy { frame -> projectFrameToIndexMap[frame] } | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,48 @@ | ||
package solve.filters.model | ||
|
||
import solve.filters.settings.model.FilterSetting | ||
import solve.filters.settings.model.IndicesStepFilterSetting | ||
import solve.filters.settings.model.TimePeriodFilterSetting | ||
import solve.filters.settings.model.UIDFilterSetting | ||
import solve.project.model.ProjectFrame | ||
|
||
class Filter(val settings: List<FilterSetting<out Any>>) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. May be make it mutable to avoid problems with list view? |
||
var enabled: Boolean = true | ||
val preview: String by lazy { createPreviewText() } | ||
|
||
fun apply(frames: List<ProjectFrame>): List<ProjectFrame> { | ||
if (!enabled) { | ||
return frames | ||
} | ||
|
||
var suitableFrames = frames | ||
settings.forEach { suitableFrames = it.apply(suitableFrames) } | ||
|
||
return suitableFrames | ||
} | ||
|
||
private fun createPreviewText(): String { | ||
val indicesStepSetting = settings.firstOrNull { it is IndicesStepFilterSetting } as? IndicesStepFilterSetting | ||
val timePeriodSetting = settings.firstOrNull { it is TimePeriodFilterSetting } as? TimePeriodFilterSetting | ||
val uidSetting = settings.firstOrNull { it is UIDFilterSetting } as? UIDFilterSetting | ||
|
||
val prefix = "Show " | ||
val indicesStepPart = if (indicesStepSetting != null) { | ||
"every ${indicesStepSetting.settingValue} image " | ||
} else { | ||
"images " | ||
} | ||
val timePeriodPart = if (timePeriodSetting != null) { | ||
"from ${timePeriodSetting.settingValue.x} to ${timePeriodSetting.settingValue.y} " | ||
} else { | ||
"" | ||
} | ||
val uidPart = if (uidSetting != null) { | ||
"with landmark ${uidSetting.settingValue}" | ||
} else { | ||
"" | ||
} | ||
|
||
return "$prefix$indicesStepPart$timePeriodPart$uidPart" | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,27 @@ | ||
package solve.filters.model | ||
|
||
import javafx.collections.FXCollections | ||
import javafx.collections.ObservableList | ||
|
||
class FilterPanelModel { | ||
private val _filters = FXCollections.observableArrayList<Filter>() | ||
val filters: ObservableList<Filter> = FXCollections.synchronizedObservableList(_filters) | ||
|
||
fun addFilter(filter: Filter) { | ||
_filters.add(filter) | ||
} | ||
|
||
fun removeFilter(filter: Filter) { | ||
_filters.remove(filter) | ||
} | ||
|
||
fun replaceFilter(oldFilter: Filter, newFilter: Filter) { | ||
if (!_filters.contains(oldFilter)) { | ||
return | ||
} | ||
|
||
val oldFilterIndex = _filters.indexOf(oldFilter) | ||
_filters.remove(oldFilter) | ||
_filters.add(oldFilterIndex, newFilter) | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,22 @@ | ||
package solve.filters.settings.controller | ||
|
||
import solve.filters.controller.FilterPanelController | ||
import solve.filters.model.Filter | ||
import solve.filters.settings.model.FilterSetting | ||
import tornadofx.* | ||
|
||
class FilterSettingsController : Controller() { | ||
private val panelController: FilterPanelController by inject() | ||
|
||
fun createFilter(filterSettings: List<FilterSetting<out Any>>) { | ||
val filter = Filter(filterSettings) | ||
panelController.addFilter(filter) | ||
} | ||
|
||
fun editFilter(editingFilter: Filter, editedFilterSettings: List<FilterSetting<out Any>>) { | ||
val newFilter = Filter(editedFilterSettings) | ||
newFilter.enabled = editingFilter.enabled | ||
|
||
panelController.editFilter(editingFilter, newFilter) | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,20 @@ | ||
package solve.filters.settings.model | ||
|
||
import solve.project.model.ProjectFrame | ||
|
||
abstract class FilterSetting<T>(settingValue: T) { | ||
var enabled: Boolean = true | ||
|
||
var settingValue: T = settingValue | ||
protected set | ||
|
||
fun apply(fields: List<ProjectFrame>) : List<ProjectFrame> { | ||
if (!enabled) | ||
return fields; | ||
|
||
return applySetting(fields) | ||
} | ||
protected abstract fun applySetting(fields: List<ProjectFrame>): List<ProjectFrame> | ||
|
||
abstract fun edit(newValue: T) | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,13 @@ | ||
package solve.filters.settings.model | ||
|
||
import solve.project.model.ProjectFrame | ||
|
||
class IndicesStepFilterSetting(step: Int) : FilterSetting<Int>(step) { | ||
override fun applySetting(fields: List<ProjectFrame>) = fields.slice( | ||
(settingValue - 1)..fields.lastIndex step this.settingValue | ||
) | ||
|
||
override fun edit(newValue: Int) { | ||
settingValue = newValue | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,13 @@ | ||
package solve.filters.settings.model | ||
|
||
import solve.project.model.ProjectFrame | ||
import solve.utils.structures.IntPoint | ||
|
||
class TimePeriodFilterSetting(timePeriod: IntPoint) : FilterSetting<IntPoint>(timePeriod) { | ||
override fun applySetting(fields: List<ProjectFrame>): List<ProjectFrame> = | ||
fields.filter { it.timestamp in settingValue.x..settingValue.y } | ||
|
||
override fun edit(newValue: IntPoint) { | ||
settingValue = newValue | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
package solve.filters.settings.model | ||
|
||
import solve.project.model.ProjectFrame | ||
|
||
class UIDFilterSetting(uid: Long) : FilterSetting<Long>(uid) { | ||
override fun applySetting(fields: List<ProjectFrame>) = fields.filter { it.uids.contains(settingValue) } | ||
|
||
override fun edit(newValue: Long) { | ||
settingValue = newValue | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We already have global application style class Style.kt, can we merge them?