From 0b76808eda8de70decc8cfeb674011510d013d2c Mon Sep 17 00:00:00 2001 From: Stanislav Mishchenko Date: Thu, 4 May 2023 22:55:41 +0300 Subject: [PATCH 01/36] Fix an incorrect initializaion of dividers positions --- src/main/kotlin/solve/main/MainView.kt | 36 +++++++++---------- .../solve/main/splitpane/FixedSplitPane.kt | 14 ++++++-- 2 files changed, 29 insertions(+), 21 deletions(-) diff --git a/src/main/kotlin/solve/main/MainView.kt b/src/main/kotlin/solve/main/MainView.kt index fcd9d0900..1d18535c2 100644 --- a/src/main/kotlin/solve/main/MainView.kt +++ b/src/main/kotlin/solve/main/MainView.kt @@ -39,24 +39,6 @@ import solve.utils.mfxButton import tornadofx.* class MainView : View() { - companion object { - private const val LeftSidePanelAndSceneDividerPosition = 0.25 - private const val RightSidePanelAndSceneDividerPosition = 0.88 - - private const val TabsViewLocationParamName = "location" - private const val TabsViewTabsParamName = "tabs" - private const val TabsViewInitialTabParamName = "initialTab" - - const val ProjectTabName = "Project" - const val LayersTabName = "Layers" - const val GridTabName = "Grid" - - private val importIcon = loadResourcesImage(IconsImportFab) - private val pluginsIcon = loadResourcesImage(IconsPlugins) - private val settingsIcon = loadResourcesImage(IconsSettings) - private val helpIcon = loadResourcesImage(IconsHelp) - } - val importer: ImporterView by inject() private val mainView: MainView by inject() @@ -232,6 +214,24 @@ class MainView : View() { } private data class SidePanelViews(val tabsView: SidePanelTabsView, val contentView: SidePanelContentView) + + companion object { + private const val LeftSidePanelAndSceneDividerPosition = 0.2 + private const val RightSidePanelAndSceneDividerPosition = 0.85 + + private const val TabsViewLocationParamName = "location" + private const val TabsViewTabsParamName = "tabs" + private const val TabsViewInitialTabParamName = "initialTab" + + const val ProjectTabName = "Project" + const val LayersTabName = "Layers" + const val GridTabName = "Grid" + + private val importIcon = loadResourcesImage(IconsImportFab) + private val pluginsIcon = loadResourcesImage(IconsPlugins) + private val settingsIcon = loadResourcesImage(IconsSettings) + private val helpIcon = loadResourcesImage(IconsHelp) + } } class MainSplitPaneStyle : Stylesheet() { diff --git a/src/main/kotlin/solve/main/splitpane/FixedSplitPane.kt b/src/main/kotlin/solve/main/splitpane/FixedSplitPane.kt index 7f47275a5..4b56f9f62 100644 --- a/src/main/kotlin/solve/main/splitpane/FixedSplitPane.kt +++ b/src/main/kotlin/solve/main/splitpane/FixedSplitPane.kt @@ -13,6 +13,8 @@ open class FixedSplitPane( private var isWindowResizing = false protected val dividersInstalledPositions = mutableMapOf() + private var areDividersAbleToChange = false + init { if (dividersInitialPositions.count() != containedNodes.count() - 1) { throw IllegalArgumentException( @@ -32,14 +34,19 @@ open class FixedSplitPane( protected fun initializeDividerPositionControl(divider: Divider, installedPositionIndex: Int) { divider.positionProperty().onChange { - if (!isWindowResizing) { + if (!isWindowResizing && areDividersAbleToChange) { dividersInstalledPositions[installedPositionIndex] = divider.position + } else { + dividersInstalledPositions[installedPositionIndex]?.let { divider.position = it } } - dividersInstalledPositions[installedPositionIndex]?.let { divider.position = it } } Platform.runLater { initializeWindowResizingDetection() + + Platform.runLater { + areDividersAbleToChange = true // Needed for a correct initialization of split pane dividers. + } } } @@ -61,10 +68,11 @@ open class FixedSplitPane( } private fun initializeDividers() { + dividers.forEachIndexed { index, divider -> val initialPosition = dividersInitialPositions[index] - divider.position = initialPosition dividersInstalledPositions[index] = initialPosition + initializeDividerPosition(divider, index) } } From 92daab0b3d889b30bab3e2564bc803810a7bbee6 Mon Sep 17 00:00:00 2001 From: Stanislav Mishchenko Date: Fri, 5 May 2023 22:24:53 +0300 Subject: [PATCH 02/36] Add a filters panel view --- build.gradle.kts | 1 + src/main/kotlin/solve/SolveApp.kt | 13 +++- .../solve/catalogue/view/CatalogueView.kt | 10 ++- .../kotlin/solve/constants/ResourcesPaths.kt | 6 ++ src/main/kotlin/solve/filters/model/Filter.kt | 7 ++ .../filters/view/FilterPanelFieldsView.kt | 59 +++++++++++++++ .../solve/filters/view/FilterPanelView.kt | 57 +++++++++++++++ src/main/kotlin/solve/main/MainView.kt | 4 +- .../solve/main/splitpane/FixedSplitPane.kt | 1 - .../settings/grid/view/GridSettingsView.kt | 4 +- .../view/VisualizationSettingsLayerCell.kt | 12 ++-- .../solve/styles/ApplicationStylesheet.kt | 12 ++++ .../styles/FilterPanelListViewStylesheet.kt | 72 +++++++++++++++++++ .../solve/styles/MFXButtonStyleSheet.kt | 2 +- .../solve/styles/SeparatorStylesheet.kt | 16 +++++ .../kotlin/solve/styles/SidePanelTabsStyle.kt | 6 +- src/main/kotlin/solve/styles/Style.kt | 8 ++- .../solve/styles/TreeTableViewStylesheet.kt | 3 +- src/main/kotlin/solve/utils/MFXUtils.kt | 38 +++++++++- .../kotlin/solve/utils/NodeFactoriesUtils.kt | 6 +- src/main/kotlin/solve/utils/ResourcesUtils.kt | 2 +- .../utils/SVGImageLoaderDimensionProvider.kt | 23 ++++++ .../kotlin/solve/utils/StylesheetUtils.kt | 20 ++++++ src/main/resources/icons/common/delete.svg | 1 + src/main/resources/icons/common/edit.svg | 1 + src/main/resources/icons/filters/add.svg | 1 + .../resources/icons/filters/or_checkbox.svg | 4 ++ 27 files changed, 364 insertions(+), 25 deletions(-) create mode 100644 src/main/kotlin/solve/filters/model/Filter.kt create mode 100644 src/main/kotlin/solve/filters/view/FilterPanelFieldsView.kt create mode 100644 src/main/kotlin/solve/filters/view/FilterPanelView.kt create mode 100644 src/main/kotlin/solve/styles/ApplicationStylesheet.kt create mode 100644 src/main/kotlin/solve/styles/FilterPanelListViewStylesheet.kt create mode 100644 src/main/kotlin/solve/styles/SeparatorStylesheet.kt create mode 100644 src/main/kotlin/solve/utils/SVGImageLoaderDimensionProvider.kt create mode 100644 src/main/resources/icons/common/delete.svg create mode 100644 src/main/resources/icons/common/edit.svg create mode 100644 src/main/resources/icons/filters/add.svg create mode 100644 src/main/resources/icons/filters/or_checkbox.svg diff --git a/build.gradle.kts b/build.gradle.kts index a2d260c3c..e3abd5ae6 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -52,6 +52,7 @@ dependencies { implementation("junit:junit:$junitVersion") implementation("org.junit.jupiter:junit-jupiter:$junitJupiterVersion") implementation("org.controlsfx:controlsfx:$controlsfxVersion") + implementation("de.codecentric.centerdevice:javafxsvg:1.3.0") testImplementation("org.junit.jupiter:junit-jupiter:$junitJupiterVersion") testImplementation("org.junit.jupiter:junit-jupiter-params:$junitJupiterParamsVersion") diff --git a/src/main/kotlin/solve/SolveApp.kt b/src/main/kotlin/solve/SolveApp.kt index d297eacfd..e3d32bb18 100644 --- a/src/main/kotlin/solve/SolveApp.kt +++ b/src/main/kotlin/solve/SolveApp.kt @@ -1,5 +1,6 @@ package solve +import de.codecentric.centerdevice.javafxsvg.SvgImageLoaderFactory import javafx.stage.Stage import solve.main.MainView import solve.scene.view.landmarks.AnimationProvider @@ -7,21 +8,29 @@ import solve.scene.view.landmarks.JavaFXAnimationProvider import solve.utils.ServiceLocator import tornadofx.App import tornadofx.launch +import solve.styles.ApplicationStylesheet +import solve.utils.SVGImageLoaderDimensionProvider -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(JavaFXAnimationProvider()) } + + private fun initializeDependencies() { + SvgImageLoaderFactory.install(SVGImageLoaderDimensionProvider()) + } } fun main(args: Array) = launch(args) diff --git a/src/main/kotlin/solve/catalogue/view/CatalogueView.kt b/src/main/kotlin/solve/catalogue/view/CatalogueView.kt index 5d0be2d7f..a9004c71c 100644 --- a/src/main/kotlin/solve/catalogue/view/CatalogueView.kt +++ b/src/main/kotlin/solve/catalogue/view/CatalogueView.kt @@ -21,6 +21,7 @@ import solve.catalogue.view.fields.CatalogueFieldsView import solve.catalogue.view.fields.CatalogueFileNamesFieldsView import solve.catalogue.view.fields.CataloguePreviewImagesFieldsView import solve.constants.IconsCatalogueApplyPath +import solve.filters.view.FilterPanelView import solve.project.model.ProjectFrame import solve.utils.addSafely import solve.utils.createInsetsWithValue @@ -42,6 +43,7 @@ class CatalogueView : View() { } private val settingsView: CatalogueSettingsView by inject() + private val filterPanelView: FilterPanelView by inject() private val controller: CatalogueController by inject() private val fields = FXCollections.observableArrayList() @@ -91,7 +93,13 @@ class CatalogueView : View() { vgrow = Priority.ALWAYS } - override val root = catalogueNode.also { initializeNodes() } + override val root = vbox { + add(catalogueNode) + initializeNodes() + add(filterPanelView) + + vgrow = Priority.ALWAYS + } init { accelerators[KeyCodeCombination(KeyCode.ENTER)] = { diff --git a/src/main/kotlin/solve/constants/ResourcesPaths.kt b/src/main/kotlin/solve/constants/ResourcesPaths.kt index c964a032a..fe0e2db8b 100644 --- a/src/main/kotlin/solve/constants/ResourcesPaths.kt +++ b/src/main/kotlin/solve/constants/ResourcesPaths.kt @@ -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" +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" diff --git a/src/main/kotlin/solve/filters/model/Filter.kt b/src/main/kotlin/solve/filters/model/Filter.kt new file mode 100644 index 000000000..92aa3476c --- /dev/null +++ b/src/main/kotlin/solve/filters/model/Filter.kt @@ -0,0 +1,7 @@ +package solve.filters.model + +import solve.project.model.ProjectFrame + +interface Filter { + fun apply(frames: List): List +} diff --git a/src/main/kotlin/solve/filters/view/FilterPanelFieldsView.kt b/src/main/kotlin/solve/filters/view/FilterPanelFieldsView.kt new file mode 100644 index 000000000..7d9ccaba8 --- /dev/null +++ b/src/main/kotlin/solve/filters/view/FilterPanelFieldsView.kt @@ -0,0 +1,59 @@ +package solve.filters.view + +import io.github.palexdev.materialfx.controls.cell.MFXCheckListCell +import io.github.palexdev.materialfx.effects.DepthLevel +import javafx.beans.binding.Bindings +import solve.constants.IconsDeletePath +import solve.constants.IconsEditPath +import solve.filters.model.Filter +import solve.styles.FilterPanelListViewStylesheet +import solve.utils.createHGrowHBox +import solve.utils.imageViewIcon +import solve.utils.loadResourcesImage +import solve.utils.mfxCheckListView +import tornadofx.* +import java.util.function.Function + +class FilterPanelFieldsView : View() { + private val editIconImage = loadResourcesImage(IconsEditPath) + private val deleteIconImage = loadResourcesImage(IconsDeletePath) + + val filtersListView = mfxCheckListView() { + addStylesheet(FilterPanelListViewStylesheet::class) + + depthLevel = DepthLevel.LEVEL0 + + cellFactory = Function { + val cell = MFXCheckListCell(this, it) + addButtonsToCell(cell) + + return@Function cell + } + + val itemsNumberProperty = Bindings.size(items) + prefHeightProperty().bind(itemsNumberProperty.multiply(35.0)) + + paddingBottom = 15.0 + paddingLeft = 1.5 + useMaxWidth = true + } + + override val root = filtersListView + + private fun addButtonsToCell(cell: MFXCheckListCell) { + cell.add(createHGrowHBox()) + cell.add( + hbox { + hbox { + imageViewIcon(editIconImage ?: return@hbox, 24.0) { + paddingTop = 3.5 + paddingRight = 7.0 + } + } + imageViewIcon(deleteIconImage ?: return@hbox, 24.0) + + paddingRight = 20.5 + } + ) + } +} diff --git a/src/main/kotlin/solve/filters/view/FilterPanelView.kt b/src/main/kotlin/solve/filters/view/FilterPanelView.kt new file mode 100644 index 000000000..ca05f8bf1 --- /dev/null +++ b/src/main/kotlin/solve/filters/view/FilterPanelView.kt @@ -0,0 +1,57 @@ +package solve.filters.view + +import javafx.scene.paint.Paint +import solve.constants.IconsFiltersAddPath +import solve.styles.SeparatorStylesheet +import solve.styles.Style +import solve.utils.createHGrowHBox +import solve.utils.createPxValue +import solve.utils.imageViewIcon +import solve.utils.loadResourcesImage +import solve.utils.mfxCircleButton +import tornadofx.* + +class FilterPanelView : View() { + private val filterPanelFieldsView: FilterPanelFieldsView by inject() + + override val root = vbox { + separator { + addStylesheet(SeparatorStylesheet::class) + } + hbox { + minHeight = HeaderMinHeight + + label("Filters") { + style { + fontFamily = Style.font + fontSize = createPxValue(HeaderFontSize) + textFill = Paint.valueOf(Style.headerFontColor) + } + + paddingLeft = 15.0 + } + add(createHGrowHBox()) + val addButtonIconImage = loadResourcesImage(IconsFiltersAddPath) + val addButtonGraphic = if (addButtonIconImage != null) { + imageViewIcon(addButtonIconImage, FilterFieldButtonsSize) + } else { + null + } + hbox { + mfxCircleButton(addButtonGraphic) + paddingRight = 14.0 + paddingTop = -6.0 + } + + paddingTop = 10.0 + } + add(filterPanelFieldsView) + } + + companion object { + private const val HeaderMinHeight = 48.0 + private const val HeaderFontSize = 14.0 + + private const val FilterFieldButtonsSize = 24.0 + } +} diff --git a/src/main/kotlin/solve/main/MainView.kt b/src/main/kotlin/solve/main/MainView.kt index 1d18535c2..6a6912137 100644 --- a/src/main/kotlin/solve/main/MainView.kt +++ b/src/main/kotlin/solve/main/MainView.kt @@ -216,8 +216,8 @@ class MainView : View() { private data class SidePanelViews(val tabsView: SidePanelTabsView, val contentView: SidePanelContentView) companion object { - private const val LeftSidePanelAndSceneDividerPosition = 0.2 - private const val RightSidePanelAndSceneDividerPosition = 0.85 + private const val LeftSidePanelAndSceneDividerPosition = 0.20 + private const val RightSidePanelAndSceneDividerPosition = 0.82 private const val TabsViewLocationParamName = "location" private const val TabsViewTabsParamName = "tabs" diff --git a/src/main/kotlin/solve/main/splitpane/FixedSplitPane.kt b/src/main/kotlin/solve/main/splitpane/FixedSplitPane.kt index 4b56f9f62..d8d4d8d3e 100644 --- a/src/main/kotlin/solve/main/splitpane/FixedSplitPane.kt +++ b/src/main/kotlin/solve/main/splitpane/FixedSplitPane.kt @@ -68,7 +68,6 @@ open class FixedSplitPane( } private fun initializeDividers() { - dividers.forEachIndexed { index, divider -> val initialPosition = dividersInitialPositions[index] dividersInstalledPositions[index] = initialPosition diff --git a/src/main/kotlin/solve/settings/grid/view/GridSettingsView.kt b/src/main/kotlin/solve/settings/grid/view/GridSettingsView.kt index 4f23be91a..9a12c277f 100644 --- a/src/main/kotlin/solve/settings/grid/view/GridSettingsView.kt +++ b/src/main/kotlin/solve/settings/grid/view/GridSettingsView.kt @@ -18,8 +18,8 @@ import solve.scene.SceneFacade import solve.scene.controller.SceneController import solve.settings.createSettingsField import solve.settings.grid.controller.GridSettingsController -import solve.utils.createImageViewIcon import solve.utils.createInsetsWithValue +import solve.utils.imageViewIcon import solve.utils.loadResourcesImage import solve.utils.scale import solve.utils.unscale @@ -139,7 +139,7 @@ class GridSettingsView : View() { } iconImage ?: return@button - graphic = createImageViewIcon(iconImage, GridSettingsColumnsNumberButtonsSize) + graphic = imageViewIcon(iconImage, GridSettingsColumnsNumberButtonsSize) updateViewByActivity(isActiveProperty.value) isActiveProperty.onChange { isActive -> diff --git a/src/main/kotlin/solve/settings/visualization/fields/view/VisualizationSettingsLayerCell.kt b/src/main/kotlin/solve/settings/visualization/fields/view/VisualizationSettingsLayerCell.kt index a80aee14a..413863d8c 100644 --- a/src/main/kotlin/solve/settings/visualization/fields/view/VisualizationSettingsLayerCell.kt +++ b/src/main/kotlin/solve/settings/visualization/fields/view/VisualizationSettingsLayerCell.kt @@ -27,10 +27,10 @@ import solve.settings.visualization.popover.LineLayerSettingsPopOverNode import solve.settings.visualization.popover.PointLayerSettingsPopOverNode import solve.utils.TransparentScalingButtonStyle import solve.utils.createHGrowHBox -import solve.utils.createImageViewIcon import solve.utils.createSnapshot import solve.utils.createVGrowBox import solve.utils.getScreenPosition +import solve.utils.imageViewIcon import solve.utils.loadResourcesImage import solve.utils.nodes.listcell.dragdrop.DragAndDropCellItemInfo import solve.utils.nodes.listcell.dragdrop.DragAndDropListCell @@ -111,7 +111,7 @@ class VisualizationSettingsLayerCell( // Needed to set the padding and to center the imageview. return vbox { add(createVGrowBox()) - add(createImageViewIcon(layerIcon, LayerIconWidth)) + imageViewIcon(layerIcon, LayerIconWidth) add(createVGrowBox()) paddingRight = LayerTypeIconPaddingRight } @@ -124,8 +124,7 @@ class VisualizationSettingsLayerCell( private fun createLayerEditButton(layerType: LandmarkType): Node = button { editIconImage ?: return@button - val editImageViewIcon = createImageViewIcon(editIconImage, LayerFieldEditIconSize) - graphic = editImageViewIcon + graphic = imageViewIcon(editIconImage, LayerFieldEditIconSize) isPickOnBounds = false initializeLayerSettingsPopOver(this, this, layerType) @@ -135,9 +134,8 @@ class VisualizationSettingsLayerCell( private fun createLayerVisibilityButton(): Node = hbox { layerVisibleIconImage ?: return@hbox layerInvisibleIconImage ?: return@hbox - val layerVisibleImageViewIcon = createImageViewIcon(layerVisibleIconImage, LayerVisibilityIconSize) - val layerInvisibleImageViewIcon = - createImageViewIcon(layerInvisibleIconImage, LayerVisibilityIconSize) + val layerVisibleImageViewIcon = imageViewIcon(layerVisibleIconImage, LayerVisibilityIconSize) + val layerInvisibleImageViewIcon = imageViewIcon(layerInvisibleIconImage, LayerVisibilityIconSize) fun getCurrentVisibilityImageViewIcon() = if (item.enabled) layerVisibleImageViewIcon else layerInvisibleImageViewIcon diff --git a/src/main/kotlin/solve/styles/ApplicationStylesheet.kt b/src/main/kotlin/solve/styles/ApplicationStylesheet.kt new file mode 100644 index 000000000..261ef6d57 --- /dev/null +++ b/src/main/kotlin/solve/styles/ApplicationStylesheet.kt @@ -0,0 +1,12 @@ +package solve.styles + +import javafx.scene.text.FontSmoothingType +import tornadofx.* + +class ApplicationStylesheet : Stylesheet() { + init { + text { + fontSmoothingType = FontSmoothingType.GRAY + } + } +} diff --git a/src/main/kotlin/solve/styles/FilterPanelListViewStylesheet.kt b/src/main/kotlin/solve/styles/FilterPanelListViewStylesheet.kt new file mode 100644 index 000000000..cbffa26d3 --- /dev/null +++ b/src/main/kotlin/solve/styles/FilterPanelListViewStylesheet.kt @@ -0,0 +1,72 @@ +package solve.styles + +import javafx.scene.paint.Color +import javafx.scene.paint.Paint +import solve.constants.IconsFiltersOrCheckBoxPath +import solve.utils.addBackgroundImage +import solve.utils.createCssBoxWithValue +import solve.utils.createLinearUnitsBoxWithLeft +import solve.utils.createPxBoxWithValue +import solve.utils.createPxValue +import tornadofx.* + +class FilterPanelListViewStylesheet : Stylesheet() { + init { + selected { + mfxCheckbox { + rippleContainer { + box { + borderColor += createCssBoxWithValue(Color.TRANSPARENT) + addBackgroundImage(IconsFiltersOrCheckBoxPath) + } + } + } + } + + mfxCheckbox { + rippleContainer { + box { + borderColor += createCssBoxWithValue(Paint.valueOf(Style.separatorLineColor)) + borderWidth += createPxBoxWithValue(CheckBoxBorderWidth) + + mark { + visibility = FXVisibility.HIDDEN + } + } + } + } + + mfxCheckListView { + maxHeight = createPxValue(ListMaxHeight) + backgroundColor += Color.TRANSPARENT + + virtualFlow { + backgroundColor += Color.TRANSPARENT + + mfxCheckListCell { + backgroundColor += Color.TRANSPARENT + borderColor += createCssBoxWithValue(Color.TRANSPARENT) + padding = createLinearUnitsBoxWithLeft(4.0, Dimension.LinearUnits.px) + + dataLabel { + fontSize = createPxValue(ListFieldFontSize) + fontFamily = Style.font + textFill = Paint.valueOf(Style.listFontColor) + } + } + } + } + } + + companion object { + private val mfxCheckListView by cssclass() + private val mfxCheckListCell by cssclass() + private val dataLabel by cssclass() + private val mfxCheckbox by cssclass() + private val rippleContainer by cssclass() + + private const val ListMaxHeight = 180.0 + private const val CheckBoxBorderWidth = 2.5 + private const val ListFieldFontSize = 14.0 + } +} diff --git a/src/main/kotlin/solve/styles/MFXButtonStyleSheet.kt b/src/main/kotlin/solve/styles/MFXButtonStyleSheet.kt index bdedc3315..ac7a0c505 100644 --- a/src/main/kotlin/solve/styles/MFXButtonStyleSheet.kt +++ b/src/main/kotlin/solve/styles/MFXButtonStyleSheet.kt @@ -9,7 +9,7 @@ class MFXButtonStyleSheet : Stylesheet() { init { mfxButton { and(hover, pressed) { - backgroundColor += Paint.valueOf(Style.backgroundColour) + backgroundColor += Paint.valueOf(Style.backgroundColor) } and(pressed) { backgroundColor += Paint.valueOf(Style.surfaceColor) diff --git a/src/main/kotlin/solve/styles/SeparatorStylesheet.kt b/src/main/kotlin/solve/styles/SeparatorStylesheet.kt new file mode 100644 index 000000000..67b08af2e --- /dev/null +++ b/src/main/kotlin/solve/styles/SeparatorStylesheet.kt @@ -0,0 +1,16 @@ +package solve.styles + +import javafx.scene.paint.Paint +import solve.utils.createPxBoxWithValue +import tornadofx.* + +class SeparatorStylesheet : Stylesheet() { + init { + Companion.separator { + backgroundColor += Paint.valueOf(Style.separatorLineColor) + Companion.line { + borderWidth += createPxBoxWithValue(0.0) + } + } + } +} diff --git a/src/main/kotlin/solve/styles/SidePanelTabsStyle.kt b/src/main/kotlin/solve/styles/SidePanelTabsStyle.kt index c89902064..b255d43c4 100644 --- a/src/main/kotlin/solve/styles/SidePanelTabsStyle.kt +++ b/src/main/kotlin/solve/styles/SidePanelTabsStyle.kt @@ -32,7 +32,7 @@ class SidePanelTabsStyle : Stylesheet() { graphic = URI(IconsProject) and(hover) { - backgroundColor += Paint.valueOf(Style.backgroundColour) + backgroundColor += Paint.valueOf(Style.backgroundColor) focusColor = Color.TRANSPARENT } and(selected) { @@ -45,7 +45,7 @@ class SidePanelTabsStyle : Stylesheet() { graphic = URI(IconsLayers) and(hover) { - backgroundColor += Paint.valueOf(Style.backgroundColour) + backgroundColor += Paint.valueOf(Style.backgroundColor) focusColor = Color.TRANSPARENT } and(selected) { @@ -58,7 +58,7 @@ class SidePanelTabsStyle : Stylesheet() { graphic = URI(IconsGrid) and(hover) { - backgroundColor += Paint.valueOf(Style.backgroundColour) + backgroundColor += Paint.valueOf(Style.backgroundColor) focusColor = Color.TRANSPARENT } and(selected) { diff --git a/src/main/kotlin/solve/styles/Style.kt b/src/main/kotlin/solve/styles/Style.kt index ad78d4395..d23029d93 100644 --- a/src/main/kotlin/solve/styles/Style.kt +++ b/src/main/kotlin/solve/styles/Style.kt @@ -6,7 +6,7 @@ import javafx.scene.shape.Circle import tornadofx.* object Style { - const val backgroundColour = "EFF0F0" + const val backgroundColor = "EFF0F0" const val surfaceColor = "FFFFFF" @@ -20,10 +20,16 @@ object Style { const val secondaryColor = "41497F" + const val separatorLineColor = "9DAEB7" + const val fontCondensed = "'Roboto Condensed'" const val font = "Roboto" + const val listFontColor = "3E4345" + + const val headerFontColor = "1A1A1A" + const val fontWeightBold = "700" const val buttonFontSize = "14px" diff --git a/src/main/kotlin/solve/styles/TreeTableViewStylesheet.kt b/src/main/kotlin/solve/styles/TreeTableViewStylesheet.kt index 4e1d1260d..9646b0723 100644 --- a/src/main/kotlin/solve/styles/TreeTableViewStylesheet.kt +++ b/src/main/kotlin/solve/styles/TreeTableViewStylesheet.kt @@ -13,8 +13,7 @@ import java.net.URI class TreeTableViewStylesheet : Stylesheet() { companion object { val fxTreeTableCellBorderColor by cssproperty>("-fx-table-cell-border-color") - - val backgroundColour: Color = valueOf(Style.backgroundColour) + val backgroundColour: Color = valueOf(Style.backgroundColor) val surfaceColor: Color = valueOf(Style.surfaceColor) val primaryColor: Color = valueOf(Style.primaryColor) } diff --git a/src/main/kotlin/solve/utils/MFXUtils.kt b/src/main/kotlin/solve/utils/MFXUtils.kt index cc340fc00..6a4d3293d 100644 --- a/src/main/kotlin/solve/utils/MFXUtils.kt +++ b/src/main/kotlin/solve/utils/MFXUtils.kt @@ -1,12 +1,18 @@ package solve.utils import io.github.palexdev.materialfx.controls.MFXButton +import io.github.palexdev.materialfx.controls.MFXCheckListView import io.github.palexdev.materialfx.controls.MFXContextMenu import io.github.palexdev.materialfx.controls.MFXContextMenuItem import io.github.palexdev.materialfx.controls.MFXTextField +import javafx.application.Platform +import javafx.collections.ObservableList import javafx.event.EventTarget import javafx.scene.Node -import tornadofx.attachTo +import javafx.scene.paint.Color +import javafx.scene.shape.Circle +import tornadofx.* +import java.util.function.Supplier fun EventTarget.mfxButton( text: String = "", @@ -38,3 +44,33 @@ fun MFXContextMenu.item( fun MFXContextMenu.lineSeparator() = this.addLineSeparator(MFXContextMenu.Builder.getLineSeparator()) fun MFXContextMenuItem.action(op: () -> Unit) = this.setOnAction { op() } + +fun EventTarget.mfxCircleButton( + graphic: Node? = null, + radius: Double = 20.0, + text: String = "", + op: MFXButton.() -> Unit = {} +) = mfxButton(text, graphic) { + this.graphic = graphic + Platform.runLater { + val rippleCenter = boundsInLocal + rippleGenerator.clipSupplier = Supplier { + return@Supplier Circle(rippleCenter.centerX, rippleCenter.centerY, radius) + } + } + style { + backgroundColor += Color.TRANSPARENT + minHeight = Dimension(radius, Dimension.LinearUnits.px) + } + op() +} + +fun EventTarget.mfxCheckListView( + items: ObservableList = observableListOf(), + op: MFXCheckListView.() -> Unit = {} +): MFXCheckListView { + val mfxCheckListView = MFXCheckListView(items) + mfxCheckListView.attachTo(this, op) + + return mfxCheckListView +} diff --git a/src/main/kotlin/solve/utils/NodeFactoriesUtils.kt b/src/main/kotlin/solve/utils/NodeFactoriesUtils.kt index 171bda663..462c239b8 100644 --- a/src/main/kotlin/solve/utils/NodeFactoriesUtils.kt +++ b/src/main/kotlin/solve/utils/NodeFactoriesUtils.kt @@ -1,12 +1,16 @@ package solve.utils +import javafx.event.EventTarget import javafx.scene.image.Image import javafx.scene.image.ImageView +import tornadofx.* -fun createImageViewIcon(iconImage: Image, width: Double): ImageView { +fun EventTarget.imageViewIcon(iconImage: Image, width: Double, op: ImageView.() -> Unit = {}): ImageView { val imageView = ImageView(iconImage) + imageView.fitWidth = width imageView.isPreserveRatio = true + imageView.attachTo(this, op) return imageView } diff --git a/src/main/kotlin/solve/utils/ResourcesUtils.kt b/src/main/kotlin/solve/utils/ResourcesUtils.kt index 6a6c01019..02ba407d2 100644 --- a/src/main/kotlin/solve/utils/ResourcesUtils.kt +++ b/src/main/kotlin/solve/utils/ResourcesUtils.kt @@ -51,4 +51,4 @@ fun loadBufferedImage(filePath: String): BufferedImage? { return image } -private fun getResource(resourcesPath: String) = Any::class::class.java.getResource("/$resourcesPath") +fun getResource(resourcesPath: String) = Any::class::class.java.getResource("/$resourcesPath") diff --git a/src/main/kotlin/solve/utils/SVGImageLoaderDimensionProvider.kt b/src/main/kotlin/solve/utils/SVGImageLoaderDimensionProvider.kt new file mode 100644 index 000000000..e27e818aa --- /dev/null +++ b/src/main/kotlin/solve/utils/SVGImageLoaderDimensionProvider.kt @@ -0,0 +1,23 @@ +package solve.utils + +import de.codecentric.centerdevice.javafxsvg.dimension.Dimension +import de.codecentric.centerdevice.javafxsvg.dimension.DimensionProvider +import de.codecentric.centerdevice.javafxsvg.dimension.PrimitiveDimensionProvider +import org.w3c.dom.Document + +class SVGImageLoaderDimensionProvider : DimensionProvider { + private val primitiveDimensionProvider = PrimitiveDimensionProvider() + + override fun getDimension(document: Document?): Dimension { + val primitiveDimension = primitiveDimensionProvider.getDimension(document) + + return Dimension( + primitiveDimension.width * PrimitiveDimensionScale, + primitiveDimension.height * PrimitiveDimensionScale + ) + } + + companion object { + private const val PrimitiveDimensionScale = 1.5f // Scale value for the primitive calculated dimension. + } +} diff --git a/src/main/kotlin/solve/utils/StylesheetUtils.kt b/src/main/kotlin/solve/utils/StylesheetUtils.kt index f18ab83f1..8f9f481c9 100644 --- a/src/main/kotlin/solve/utils/StylesheetUtils.kt +++ b/src/main/kotlin/solve/utils/StylesheetUtils.kt @@ -1,6 +1,7 @@ package solve.utils import javafx.geometry.Insets +import javafx.scene.layout.BackgroundSize import javafx.scene.layout.HBox import javafx.scene.layout.Priority import javafx.scene.layout.VBox @@ -10,6 +11,8 @@ import kotlin.math.pow val DarkLightGrayColor: Color = Color.web("#AAAAAA") +fun createCssBoxWithValue(value: T) = CssBox(value, value, value, value) + fun createLinearUnitsBox(top: Double, right: Double, bottom: Double, left: Double, unitsType: Dimension.LinearUnits) = CssBox( Dimension(top, unitsType), @@ -18,6 +21,11 @@ fun createLinearUnitsBox(top: Double, right: Double, bottom: Double, left: Doubl Dimension(left, unitsType) ) +fun createLinearUnitsBoxWithLeft(left: Double, unitsType: Dimension.LinearUnits) = + createLinearUnitsBox(0.0, 0.0, 0.0, left, unitsType) + +fun createPxValue(value: Double) = Dimension(value, Dimension.LinearUnits.px) + fun createPxBox(top: Double, right: Double, bottom: Double, left: Double) = createLinearUnitsBox(top, right, bottom, left, Dimension.LinearUnits.px) @@ -55,3 +63,15 @@ fun getBlackOrWhiteContrastingTo(color: Color): Color { } fun Color.withReplacedOpacity(opacity: Double): Color = Color(red, green, blue, opacity) + +fun CssSelectionBlock.addBackgroundImage(path: String) { + backgroundImage += getResource(path)?.toURI() ?: return + backgroundSize += BackgroundSize( + 100.0, + 100.0, + true, + true, + true, + true + ) +} diff --git a/src/main/resources/icons/common/delete.svg b/src/main/resources/icons/common/delete.svg new file mode 100644 index 000000000..4a3ebcbfe --- /dev/null +++ b/src/main/resources/icons/common/delete.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/main/resources/icons/common/edit.svg b/src/main/resources/icons/common/edit.svg new file mode 100644 index 000000000..eda137cc6 --- /dev/null +++ b/src/main/resources/icons/common/edit.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/main/resources/icons/filters/add.svg b/src/main/resources/icons/filters/add.svg new file mode 100644 index 000000000..64ec3211a --- /dev/null +++ b/src/main/resources/icons/filters/add.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/main/resources/icons/filters/or_checkbox.svg b/src/main/resources/icons/filters/or_checkbox.svg new file mode 100644 index 000000000..770a9de21 --- /dev/null +++ b/src/main/resources/icons/filters/or_checkbox.svg @@ -0,0 +1,4 @@ + + + + From 14b4e6ab1f16e6687a4afcd049fdf333929fd5f2 Mon Sep 17 00:00:00 2001 From: Stanislav Mishchenko Date: Mon, 8 May 2023 03:23:15 +0300 Subject: [PATCH 03/36] Styling a controlsfx rangeslider for mfx design --- src/main/kotlin/solve/filters/model/Filter.kt | 6 ++- .../solve/filters/model/IndicesStepFilter.kt | 11 +++++ .../solve/filters/model/TimePeriodFilter.kt | 13 ++++++ .../kotlin/solve/filters/model/UIDFilter.kt | 11 +++++ .../filters/view/FilterPanelFieldsView.kt | 8 ++-- .../solve/filters/view/FilterPanelView.kt | 7 ++- .../solve/filters/view/FilterSettingsView.kt | 20 +++++++++ .../kotlin/solve/importer/view/AlertDialog.kt | 8 ++-- .../solve/importer/view/ControlPanel.kt | 4 +- .../solve/importer/view/DirectoryPathView.kt | 4 +- .../solve/importer/view/LoadingScreen.kt | 2 +- .../kotlin/solve/menubar/view/MenuBarView.kt | 2 +- .../solve/project/model/ProjectFrame.kt | 8 ++-- src/main/kotlin/solve/scene/view/FrameView.kt | 8 ++-- ....kt => FilterPanelFieldsViewStylesheet.kt} | 2 +- src/main/kotlin/solve/styles/Style.kt | 2 + src/main/kotlin/solve/utils/ImporterUtils.kt | 1 + .../solve/utils/{ => materialfx}/MFXUtils.kt | 26 ++++++++++- .../{ => materialfx}/MaterialFXDialog.kt | 2 +- .../stylesheets/MFXRangeSliderStylesheet.kt | 43 +++++++++++++++++++ .../kotlin/solve/utils/structures/IntPoint.kt | 3 ++ 21 files changed, 161 insertions(+), 30 deletions(-) create mode 100644 src/main/kotlin/solve/filters/model/IndicesStepFilter.kt create mode 100644 src/main/kotlin/solve/filters/model/TimePeriodFilter.kt create mode 100644 src/main/kotlin/solve/filters/model/UIDFilter.kt create mode 100644 src/main/kotlin/solve/filters/view/FilterSettingsView.kt rename src/main/kotlin/solve/styles/{FilterPanelListViewStylesheet.kt => FilterPanelFieldsViewStylesheet.kt} (97%) rename src/main/kotlin/solve/utils/{ => materialfx}/MFXUtils.kt (75%) rename src/main/kotlin/solve/utils/{ => materialfx}/MaterialFXDialog.kt (97%) create mode 100644 src/main/kotlin/solve/utils/materialfx/stylesheets/MFXRangeSliderStylesheet.kt create mode 100644 src/main/kotlin/solve/utils/structures/IntPoint.kt diff --git a/src/main/kotlin/solve/filters/model/Filter.kt b/src/main/kotlin/solve/filters/model/Filter.kt index 92aa3476c..fbf7400f4 100644 --- a/src/main/kotlin/solve/filters/model/Filter.kt +++ b/src/main/kotlin/solve/filters/model/Filter.kt @@ -2,6 +2,8 @@ package solve.filters.model import solve.project.model.ProjectFrame -interface Filter { - fun apply(frames: List): List +interface Filter { + fun apply(fields: List): List + + fun edit(newValue: T) } diff --git a/src/main/kotlin/solve/filters/model/IndicesStepFilter.kt b/src/main/kotlin/solve/filters/model/IndicesStepFilter.kt new file mode 100644 index 000000000..900b5c166 --- /dev/null +++ b/src/main/kotlin/solve/filters/model/IndicesStepFilter.kt @@ -0,0 +1,11 @@ +package solve.filters.model + +import solve.project.model.ProjectFrame + +class IndicesStepFilter(private var step: Int) : Filter { + override fun apply(fields: List) = fields.slice(0..fields.lastIndex step this.step) + + override fun edit(newValue: Int) { + step = newValue + } +} diff --git a/src/main/kotlin/solve/filters/model/TimePeriodFilter.kt b/src/main/kotlin/solve/filters/model/TimePeriodFilter.kt new file mode 100644 index 000000000..1e3b12219 --- /dev/null +++ b/src/main/kotlin/solve/filters/model/TimePeriodFilter.kt @@ -0,0 +1,13 @@ +package solve.filters.model + +import solve.project.model.ProjectFrame +import solve.utils.structures.IntPoint + +class TimePeriodFilter(private var timePeriod: IntPoint) : Filter { + override fun apply(fields: List): List = + fields.filter { it.timestamp in timePeriod.x..timePeriod.y } + + override fun edit(newValue: IntPoint) { + timePeriod = newValue + } +} diff --git a/src/main/kotlin/solve/filters/model/UIDFilter.kt b/src/main/kotlin/solve/filters/model/UIDFilter.kt new file mode 100644 index 000000000..017484bdc --- /dev/null +++ b/src/main/kotlin/solve/filters/model/UIDFilter.kt @@ -0,0 +1,11 @@ +package solve.filters.model + +import solve.project.model.ProjectFrame + +class UIDFilter(private var uid: Long) : Filter { + override fun apply(fields: List) = fields.filter { (it.uids as List<*>).contains(uid) } + + override fun edit(newValue: Long) { + uid = newValue + } +} diff --git a/src/main/kotlin/solve/filters/view/FilterPanelFieldsView.kt b/src/main/kotlin/solve/filters/view/FilterPanelFieldsView.kt index 7d9ccaba8..725f3addb 100644 --- a/src/main/kotlin/solve/filters/view/FilterPanelFieldsView.kt +++ b/src/main/kotlin/solve/filters/view/FilterPanelFieldsView.kt @@ -6,11 +6,11 @@ import javafx.beans.binding.Bindings import solve.constants.IconsDeletePath import solve.constants.IconsEditPath import solve.filters.model.Filter -import solve.styles.FilterPanelListViewStylesheet +import solve.styles.FilterPanelFieldsViewStylesheet import solve.utils.createHGrowHBox import solve.utils.imageViewIcon import solve.utils.loadResourcesImage -import solve.utils.mfxCheckListView +import solve.utils.materialfx.mfxCheckListView import tornadofx.* import java.util.function.Function @@ -18,8 +18,8 @@ class FilterPanelFieldsView : View() { private val editIconImage = loadResourcesImage(IconsEditPath) private val deleteIconImage = loadResourcesImage(IconsDeletePath) - val filtersListView = mfxCheckListView() { - addStylesheet(FilterPanelListViewStylesheet::class) + val filtersListView = mfxCheckListView> { + addStylesheet(FilterPanelFieldsViewStylesheet::class) depthLevel = DepthLevel.LEVEL0 diff --git a/src/main/kotlin/solve/filters/view/FilterPanelView.kt b/src/main/kotlin/solve/filters/view/FilterPanelView.kt index ca05f8bf1..162e388a1 100644 --- a/src/main/kotlin/solve/filters/view/FilterPanelView.kt +++ b/src/main/kotlin/solve/filters/view/FilterPanelView.kt @@ -8,13 +8,15 @@ import solve.utils.createHGrowHBox import solve.utils.createPxValue import solve.utils.imageViewIcon import solve.utils.loadResourcesImage -import solve.utils.mfxCircleButton +import solve.utils.materialfx.mfxCircleButton +import solve.utils.materialfx.mfxRangeSlider import tornadofx.* class FilterPanelView : View() { private val filterPanelFieldsView: FilterPanelFieldsView by inject() override val root = vbox { + mfxRangeSlider(1.0, 19.0, 4.0, 8.5) separator { addStylesheet(SeparatorStylesheet::class) } @@ -38,7 +40,7 @@ class FilterPanelView : View() { null } hbox { - mfxCircleButton(addButtonGraphic) + mfxCircleButton(addButtonGraphic, AddFilterButtonSize) paddingRight = 14.0 paddingTop = -6.0 } @@ -53,5 +55,6 @@ class FilterPanelView : View() { private const val HeaderFontSize = 14.0 private const val FilterFieldButtonsSize = 24.0 + private const val AddFilterButtonSize = 15.0 } } diff --git a/src/main/kotlin/solve/filters/view/FilterSettingsView.kt b/src/main/kotlin/solve/filters/view/FilterSettingsView.kt new file mode 100644 index 000000000..a4dc90455 --- /dev/null +++ b/src/main/kotlin/solve/filters/view/FilterSettingsView.kt @@ -0,0 +1,20 @@ +package solve.filters.view + +import javafx.scene.Node +import solve.utils.materialfx.mfxCheckbox +import tornadofx.* + +class FilterSettingsView : View() { + private val filterSettingsContentNode = borderpane { + top = hbox { + buildFilterSettingField("Time period", pane()) + } + } + override val root = filterSettingsContentNode + + private fun buildFilterSettingField(name: String, settingNode: Node) = hbox { + mfxCheckbox() + label(name) + add(settingNode) + } +} diff --git a/src/main/kotlin/solve/importer/view/AlertDialog.kt b/src/main/kotlin/solve/importer/view/AlertDialog.kt index c6cc296e8..2cba741d8 100644 --- a/src/main/kotlin/solve/importer/view/AlertDialog.kt +++ b/src/main/kotlin/solve/importer/view/AlertDialog.kt @@ -10,11 +10,11 @@ import javafx.scene.layout.BorderPane import kotlinx.coroutines.cancel import solve.constants.IconsAlertError import solve.styles.Style -import solve.utils.action -import solve.utils.item import solve.utils.loadResourcesImage -import solve.utils.mfxButton -import solve.utils.mfxContextMenu +import solve.utils.materialfx.action +import solve.utils.materialfx.item +import solve.utils.materialfx.mfxButton +import solve.utils.materialfx.mfxContextMenu import tornadofx.* class AlertDialog(contentText: String) : View() { diff --git a/src/main/kotlin/solve/importer/view/ControlPanel.kt b/src/main/kotlin/solve/importer/view/ControlPanel.kt index 25511fdaf..36ebdb2cf 100644 --- a/src/main/kotlin/solve/importer/view/ControlPanel.kt +++ b/src/main/kotlin/solve/importer/view/ControlPanel.kt @@ -20,10 +20,10 @@ import solve.importer.model.FrameAfterPartialParsing import solve.main.MainController import solve.main.MainView import solve.styles.Style -import solve.utils.MaterialFXDialog import solve.utils.createAlertForError import solve.utils.loadResourcesImage -import solve.utils.mfxButton +import solve.utils.materialfx.MaterialFXDialog +import solve.utils.materialfx.mfxButton import solve.utils.toStringWithoutBrackets import tornadofx.* diff --git a/src/main/kotlin/solve/importer/view/DirectoryPathView.kt b/src/main/kotlin/solve/importer/view/DirectoryPathView.kt index c1e29f3a0..eb6be0679 100644 --- a/src/main/kotlin/solve/importer/view/DirectoryPathView.kt +++ b/src/main/kotlin/solve/importer/view/DirectoryPathView.kt @@ -14,8 +14,8 @@ import javafx.stage.DirectoryChooser import solve.importer.ProjectParser import solve.importer.controller.ImporterController import solve.styles.Style -import solve.utils.mfxButton -import solve.utils.mfxTextField +import solve.utils.materialfx.mfxButton +import solve.utils.materialfx.mfxTextField import tornadofx.* import java.io.File diff --git a/src/main/kotlin/solve/importer/view/LoadingScreen.kt b/src/main/kotlin/solve/importer/view/LoadingScreen.kt index 1b0fbc78c..51b63f156 100644 --- a/src/main/kotlin/solve/importer/view/LoadingScreen.kt +++ b/src/main/kotlin/solve/importer/view/LoadingScreen.kt @@ -8,7 +8,7 @@ import javafx.scene.layout.VBox import javafx.scene.paint.Color import kotlinx.coroutines.cancel import solve.styles.Style -import solve.utils.mfxButton +import solve.utils.materialfx.mfxButton import tornadofx.* class LoadingScreen : View("Loading") { diff --git a/src/main/kotlin/solve/menubar/view/MenuBarView.kt b/src/main/kotlin/solve/menubar/view/MenuBarView.kt index d54862220..9193a6d92 100644 --- a/src/main/kotlin/solve/menubar/view/MenuBarView.kt +++ b/src/main/kotlin/solve/menubar/view/MenuBarView.kt @@ -6,7 +6,7 @@ import javafx.geometry.Insets import solve.importer.controller.ImporterController import solve.importer.view.ImporterView import solve.main.MainView -import solve.utils.MaterialFXDialog +import solve.utils.materialfx.MaterialFXDialog import tornadofx.View import tornadofx.action import tornadofx.button diff --git a/src/main/kotlin/solve/project/model/ProjectFrame.kt b/src/main/kotlin/solve/project/model/ProjectFrame.kt index 28b81bb1a..14013ddcc 100644 --- a/src/main/kotlin/solve/project/model/ProjectFrame.kt +++ b/src/main/kotlin/solve/project/model/ProjectFrame.kt @@ -2,8 +2,6 @@ package solve.project.model import java.nio.file.Path -data class ProjectFrame( - val timestamp: Long, - val imagePath: Path, - val landmarkFiles: List -) +data class ProjectFrame(val timestamp: Long, val imagePath: Path, val landmarkFiles: List) { + val uids: List by lazy { landmarkFiles.map { it.containedLandmarksId }.flatten() } +} diff --git a/src/main/kotlin/solve/scene/view/FrameView.kt b/src/main/kotlin/solve/scene/view/FrameView.kt index faf62235b..fa26cb093 100644 --- a/src/main/kotlin/solve/scene/view/FrameView.kt +++ b/src/main/kotlin/solve/scene/view/FrameView.kt @@ -32,11 +32,11 @@ import solve.scene.view.landmarks.LandmarkView import solve.utils.CacheElement import solve.utils.Storage import solve.utils.Updatable -import solve.utils.action import solve.utils.dialogs.ChooserDialog -import solve.utils.item -import solve.utils.lineSeparator -import solve.utils.mfxContextMenu +import solve.utils.materialfx.action +import solve.utils.materialfx.item +import solve.utils.materialfx.lineSeparator +import solve.utils.materialfx.mfxContextMenu import tornadofx.* import tornadofx.add import solve.utils.structures.Size as DoubleSize diff --git a/src/main/kotlin/solve/styles/FilterPanelListViewStylesheet.kt b/src/main/kotlin/solve/styles/FilterPanelFieldsViewStylesheet.kt similarity index 97% rename from src/main/kotlin/solve/styles/FilterPanelListViewStylesheet.kt rename to src/main/kotlin/solve/styles/FilterPanelFieldsViewStylesheet.kt index cbffa26d3..b5362d134 100644 --- a/src/main/kotlin/solve/styles/FilterPanelListViewStylesheet.kt +++ b/src/main/kotlin/solve/styles/FilterPanelFieldsViewStylesheet.kt @@ -10,7 +10,7 @@ import solve.utils.createPxBoxWithValue import solve.utils.createPxValue import tornadofx.* -class FilterPanelListViewStylesheet : Stylesheet() { +class FilterPanelFieldsViewStylesheet : Stylesheet() { init { selected { mfxCheckbox { diff --git a/src/main/kotlin/solve/styles/Style.kt b/src/main/kotlin/solve/styles/Style.kt index d23029d93..bba90e380 100644 --- a/src/main/kotlin/solve/styles/Style.kt +++ b/src/main/kotlin/solve/styles/Style.kt @@ -22,6 +22,8 @@ object Style { const val separatorLineColor = "9DAEB7" + const val settingLightColor = "EDEEF1" + const val fontCondensed = "'Roboto Condensed'" const val font = "Roboto" diff --git a/src/main/kotlin/solve/utils/ImporterUtils.kt b/src/main/kotlin/solve/utils/ImporterUtils.kt index 8f030edf2..29a3a4975 100644 --- a/src/main/kotlin/solve/utils/ImporterUtils.kt +++ b/src/main/kotlin/solve/utils/ImporterUtils.kt @@ -2,6 +2,7 @@ package solve.utils import solve.importer.view.AlertDialog import solve.importer.view.DirectoryPathView +import solve.utils.materialfx.MaterialFXDialog import tornadofx.FX.Companion.find fun createAlertForError(content: String) { diff --git a/src/main/kotlin/solve/utils/MFXUtils.kt b/src/main/kotlin/solve/utils/materialfx/MFXUtils.kt similarity index 75% rename from src/main/kotlin/solve/utils/MFXUtils.kt rename to src/main/kotlin/solve/utils/materialfx/MFXUtils.kt index 6a4d3293d..96a167f54 100644 --- a/src/main/kotlin/solve/utils/MFXUtils.kt +++ b/src/main/kotlin/solve/utils/materialfx/MFXUtils.kt @@ -1,7 +1,8 @@ -package solve.utils +package solve.utils.materialfx import io.github.palexdev.materialfx.controls.MFXButton import io.github.palexdev.materialfx.controls.MFXCheckListView +import io.github.palexdev.materialfx.controls.MFXCheckbox import io.github.palexdev.materialfx.controls.MFXContextMenu import io.github.palexdev.materialfx.controls.MFXContextMenuItem import io.github.palexdev.materialfx.controls.MFXTextField @@ -11,6 +12,8 @@ import javafx.event.EventTarget import javafx.scene.Node import javafx.scene.paint.Color import javafx.scene.shape.Circle +import org.controlsfx.control.RangeSlider +import solve.utils.materialfx.stylesheets.MFXRangeSliderStylesheet import tornadofx.* import java.util.function.Supplier @@ -65,6 +68,13 @@ fun EventTarget.mfxCircleButton( op() } +fun EventTarget.mfxCheckbox(text: String? = null, op: MFXCheckbox.() -> Unit = {}): MFXCheckbox { + val mfxCheckbox = MFXCheckbox(text) + mfxCheckbox.attachTo(this, op) + + return mfxCheckbox +} + fun EventTarget.mfxCheckListView( items: ObservableList = observableListOf(), op: MFXCheckListView.() -> Unit = {} @@ -74,3 +84,17 @@ fun EventTarget.mfxCheckListView( return mfxCheckListView } + +fun EventTarget.mfxRangeSlider( + min: Double, + max: Double, + lowValue: Double, + highValue: Double, + op: RangeSlider.() -> Unit = {} +): RangeSlider { + val slider = RangeSlider(min, max, lowValue, highValue) + slider.addStylesheet(MFXRangeSliderStylesheet::class) + slider.attachTo(this, op) + + return slider +} diff --git a/src/main/kotlin/solve/utils/MaterialFXDialog.kt b/src/main/kotlin/solve/utils/materialfx/MaterialFXDialog.kt similarity index 97% rename from src/main/kotlin/solve/utils/MaterialFXDialog.kt rename to src/main/kotlin/solve/utils/materialfx/MaterialFXDialog.kt index 2eaddfab1..4d9a534e9 100644 --- a/src/main/kotlin/solve/utils/MaterialFXDialog.kt +++ b/src/main/kotlin/solve/utils/materialfx/MaterialFXDialog.kt @@ -1,4 +1,4 @@ -package solve.utils +package solve.utils.materialfx import io.github.palexdev.materialfx.dialogs.MFXGenericDialog import io.github.palexdev.materialfx.dialogs.MFXGenericDialogBuilder diff --git a/src/main/kotlin/solve/utils/materialfx/stylesheets/MFXRangeSliderStylesheet.kt b/src/main/kotlin/solve/utils/materialfx/stylesheets/MFXRangeSliderStylesheet.kt new file mode 100644 index 000000000..20a5d3ff6 --- /dev/null +++ b/src/main/kotlin/solve/utils/materialfx/stylesheets/MFXRangeSliderStylesheet.kt @@ -0,0 +1,43 @@ +package solve.utils.materialfx.stylesheets + +import javafx.scene.paint.Color +import javafx.scene.paint.Paint +import solve.styles.Style +import solve.utils.createCssBoxWithValue +import solve.utils.scale +import tornadofx.* + +class MFXRangeSliderStylesheet : Stylesheet() { + init { + rangeSlider { + lowThumb { + initThumbStyle() + } + highThumb { + initThumbStyle() + } + + rangeBar { + backgroundColor += Paint.valueOf(Style.primaryColor) + } + track { + backgroundColor += Paint.valueOf(Style.settingLightColor) + } + } + } + + private fun CssSelectionBlock.initThumbStyle() { + borderColor += createCssBoxWithValue(Color.TRANSPARENT) + backgroundColor += Paint.valueOf(Style.primaryColor) + scale(DefaultThumbScale) + } + + companion object { + private val rangeSlider by cssclass() + private val lowThumb by cssclass() + private val highThumb by cssclass() + private val rangeBar by cssclass() + + private const val DefaultThumbScale = 1.3 + } +} diff --git a/src/main/kotlin/solve/utils/structures/IntPoint.kt b/src/main/kotlin/solve/utils/structures/IntPoint.kt new file mode 100644 index 000000000..9855600c3 --- /dev/null +++ b/src/main/kotlin/solve/utils/structures/IntPoint.kt @@ -0,0 +1,3 @@ +package solve.utils.structures + +data class IntPoint(val x: Int, val y: Int) From e6573510e9665be660b5f958bd13f479b16143ed Mon Sep 17 00:00:00 2001 From: Stanislav Mishchenko Date: Tue, 9 May 2023 03:05:19 +0300 Subject: [PATCH 04/36] Create and customize validated integer text field --- .../solve/filters/view/FilterPanelView.kt | 2 +- .../solve/filters/view/FilterSettingsView.kt | 6 ++ src/main/kotlin/solve/styles/Style.kt | 4 ++ .../kotlin/solve/utils/materialfx/MFXUtils.kt | 57 +++++++++++++++++++ .../MFXValidationTextFieldStylesheet.kt | 33 +++++++++++ 5 files changed, 101 insertions(+), 1 deletion(-) create mode 100644 src/main/kotlin/solve/utils/materialfx/stylesheets/MFXValidationTextFieldStylesheet.kt diff --git a/src/main/kotlin/solve/filters/view/FilterPanelView.kt b/src/main/kotlin/solve/filters/view/FilterPanelView.kt index 162e388a1..6db23e7f3 100644 --- a/src/main/kotlin/solve/filters/view/FilterPanelView.kt +++ b/src/main/kotlin/solve/filters/view/FilterPanelView.kt @@ -9,6 +9,7 @@ import solve.utils.createPxValue import solve.utils.imageViewIcon import solve.utils.loadResourcesImage import solve.utils.materialfx.mfxCircleButton +import solve.utils.materialfx.mfxIntegerTextField import solve.utils.materialfx.mfxRangeSlider import tornadofx.* @@ -16,7 +17,6 @@ class FilterPanelView : View() { private val filterPanelFieldsView: FilterPanelFieldsView by inject() override val root = vbox { - mfxRangeSlider(1.0, 19.0, 4.0, 8.5) separator { addStylesheet(SeparatorStylesheet::class) } diff --git a/src/main/kotlin/solve/filters/view/FilterSettingsView.kt b/src/main/kotlin/solve/filters/view/FilterSettingsView.kt index a4dc90455..323b107b7 100644 --- a/src/main/kotlin/solve/filters/view/FilterSettingsView.kt +++ b/src/main/kotlin/solve/filters/view/FilterSettingsView.kt @@ -1,7 +1,9 @@ package solve.filters.view import javafx.scene.Node +import solve.project.model.ProjectFrame import solve.utils.materialfx.mfxCheckbox +import solve.utils.materialfx.mfxRangeSlider import tornadofx.* class FilterSettingsView : View() { @@ -17,4 +19,8 @@ class FilterSettingsView : View() { label(name) add(settingNode) } + + private fun getFramesMinTimestamp(frames: List) = frames.minOf { it.timestamp } + + private fun getFramesMaxTimestamp(frames: List) = frames.maxOf { it.timestamp } } diff --git a/src/main/kotlin/solve/styles/Style.kt b/src/main/kotlin/solve/styles/Style.kt index bba90e380..323ee210d 100644 --- a/src/main/kotlin/solve/styles/Style.kt +++ b/src/main/kotlin/solve/styles/Style.kt @@ -24,6 +24,10 @@ object Style { const val settingLightColor = "EDEEF1" + const val errorColor = "EF6E6B" + + const val activeColor = "41497F" + const val fontCondensed = "'Roboto Condensed'" const val font = "Roboto" diff --git a/src/main/kotlin/solve/utils/materialfx/MFXUtils.kt b/src/main/kotlin/solve/utils/materialfx/MFXUtils.kt index 96a167f54..92677a584 100644 --- a/src/main/kotlin/solve/utils/materialfx/MFXUtils.kt +++ b/src/main/kotlin/solve/utils/materialfx/MFXUtils.kt @@ -6,14 +6,20 @@ import io.github.palexdev.materialfx.controls.MFXCheckbox import io.github.palexdev.materialfx.controls.MFXContextMenu import io.github.palexdev.materialfx.controls.MFXContextMenuItem import io.github.palexdev.materialfx.controls.MFXTextField +import io.github.palexdev.materialfx.validation.Constraint +import io.github.palexdev.materialfx.validation.Severity import javafx.application.Platform import javafx.collections.ObservableList import javafx.event.EventTarget import javafx.scene.Node import javafx.scene.paint.Color +import javafx.scene.paint.Paint import javafx.scene.shape.Circle import org.controlsfx.control.RangeSlider +import solve.styles.Style +import solve.utils.createPxValue import solve.utils.materialfx.stylesheets.MFXRangeSliderStylesheet +import solve.utils.materialfx.stylesheets.MFXValidationTextFieldStylesheet import tornadofx.* import java.util.function.Supplier @@ -98,3 +104,54 @@ fun EventTarget.mfxRangeSlider( return slider } + +val MFXTextField.validationMessage: String? + get() { + val constraints = validator.validate() + if (constraints.isEmpty()) { + return null + } + + return constraints.first().message + } + +fun EventTarget.mfxIntegerTextField( + notIntegerErrorMessage: String, + op: MFXTextField.() -> Unit = {} +) = vbox { + val mfxTextField = mfxTextField { + addStylesheet(MFXValidationTextFieldStylesheet::class) + val areAllDigitsSymbols = textProperty().booleanBinding { text -> text?.all { it.isDigit() } ?: false } + validator.constraint(Constraint(Severity.ERROR, notIntegerErrorMessage, areAllDigitsSymbols)) + + fun enableBorderColorCssString(hexColor: String) = + "${MFXValidationTextFieldStylesheet.mfxMain.name}: #${hexColor};\n" + + val enableErrorBorderColorCss = enableBorderColorCssString(MFXValidationTextFieldStylesheet.ErrorBorderColor) + val enableDefaultBorderColorCss = + enableBorderColorCssString(MFXValidationTextFieldStylesheet.DefaultBorderColor) + textProperty().onChange { + updateInvalid(this, !isValid) + style += if (isValid) { + enableDefaultBorderColorCss + } else { + enableErrorBorderColorCss + } + } + } + label { + style { + textFill = Paint.valueOf(Style.errorColor) + fontFamily = Style.font + fontSize = createPxValue(10.0) + } + visibleProperty().bind(!mfxTextField.validator.validProperty()) + mfxTextField.textProperty().onChange { + text = mfxTextField.validationMessage ?: return@onChange + } + + paddingTop = 4.0 + + } + mfxTextField.op() +} diff --git a/src/main/kotlin/solve/utils/materialfx/stylesheets/MFXValidationTextFieldStylesheet.kt b/src/main/kotlin/solve/utils/materialfx/stylesheets/MFXValidationTextFieldStylesheet.kt new file mode 100644 index 000000000..aade9513f --- /dev/null +++ b/src/main/kotlin/solve/utils/materialfx/stylesheets/MFXValidationTextFieldStylesheet.kt @@ -0,0 +1,33 @@ +package solve.utils.materialfx.stylesheets + +import javafx.scene.paint.Paint +import solve.styles.Style +import solve.utils.createCssBoxWithValue +import tornadofx.* + +class MFXValidationTextFieldStylesheet : Stylesheet() { + init { + invalid { + textFill = Paint.valueOf(ErrorBorderColor) + } + + focusWithin { + } + + mfxTextField { + mfxMain.value += Paint.valueOf(DefaultBorderColor) + borderColor += createCssBoxWithValue(Paint.valueOf(DefaultBorderColor)) + } + } + + companion object { + const val ErrorBorderColor = Style.errorColor + const val DefaultBorderColor = Style.primaryColor + + val mfxTextField by cssclass() + val invalid by csspseudoclass() + val mfxMain by cssproperty>("-mfx-main") + + val focusWithin by csspseudoclass("focus-within") + } +} \ No newline at end of file From 99bc3b9e47a83a08f5da8bb5a9fc5e4bd0ecf9fd Mon Sep 17 00:00:00 2001 From: Stanislav Mishchenko Date: Tue, 9 May 2023 03:05:55 +0300 Subject: [PATCH 05/36] Codestyle --- src/main/kotlin/solve/filters/view/FilterPanelView.kt | 2 -- src/main/kotlin/solve/filters/view/FilterSettingsView.kt | 1 - src/main/kotlin/solve/utils/materialfx/MFXUtils.kt | 3 +-- .../materialfx/stylesheets/MFXValidationTextFieldStylesheet.kt | 2 +- 4 files changed, 2 insertions(+), 6 deletions(-) diff --git a/src/main/kotlin/solve/filters/view/FilterPanelView.kt b/src/main/kotlin/solve/filters/view/FilterPanelView.kt index 6db23e7f3..245956e34 100644 --- a/src/main/kotlin/solve/filters/view/FilterPanelView.kt +++ b/src/main/kotlin/solve/filters/view/FilterPanelView.kt @@ -9,8 +9,6 @@ import solve.utils.createPxValue import solve.utils.imageViewIcon import solve.utils.loadResourcesImage import solve.utils.materialfx.mfxCircleButton -import solve.utils.materialfx.mfxIntegerTextField -import solve.utils.materialfx.mfxRangeSlider import tornadofx.* class FilterPanelView : View() { diff --git a/src/main/kotlin/solve/filters/view/FilterSettingsView.kt b/src/main/kotlin/solve/filters/view/FilterSettingsView.kt index 323b107b7..d1e459283 100644 --- a/src/main/kotlin/solve/filters/view/FilterSettingsView.kt +++ b/src/main/kotlin/solve/filters/view/FilterSettingsView.kt @@ -3,7 +3,6 @@ package solve.filters.view import javafx.scene.Node import solve.project.model.ProjectFrame import solve.utils.materialfx.mfxCheckbox -import solve.utils.materialfx.mfxRangeSlider import tornadofx.* class FilterSettingsView : View() { diff --git a/src/main/kotlin/solve/utils/materialfx/MFXUtils.kt b/src/main/kotlin/solve/utils/materialfx/MFXUtils.kt index 92677a584..2196580b4 100644 --- a/src/main/kotlin/solve/utils/materialfx/MFXUtils.kt +++ b/src/main/kotlin/solve/utils/materialfx/MFXUtils.kt @@ -125,7 +125,7 @@ fun EventTarget.mfxIntegerTextField( validator.constraint(Constraint(Severity.ERROR, notIntegerErrorMessage, areAllDigitsSymbols)) fun enableBorderColorCssString(hexColor: String) = - "${MFXValidationTextFieldStylesheet.mfxMain.name}: #${hexColor};\n" + "${MFXValidationTextFieldStylesheet.mfxMain.name}: #$hexColor;\n" val enableErrorBorderColorCss = enableBorderColorCssString(MFXValidationTextFieldStylesheet.ErrorBorderColor) val enableDefaultBorderColorCss = @@ -151,7 +151,6 @@ fun EventTarget.mfxIntegerTextField( } paddingTop = 4.0 - } mfxTextField.op() } diff --git a/src/main/kotlin/solve/utils/materialfx/stylesheets/MFXValidationTextFieldStylesheet.kt b/src/main/kotlin/solve/utils/materialfx/stylesheets/MFXValidationTextFieldStylesheet.kt index aade9513f..858141a39 100644 --- a/src/main/kotlin/solve/utils/materialfx/stylesheets/MFXValidationTextFieldStylesheet.kt +++ b/src/main/kotlin/solve/utils/materialfx/stylesheets/MFXValidationTextFieldStylesheet.kt @@ -30,4 +30,4 @@ class MFXValidationTextFieldStylesheet : Stylesheet() { val focusWithin by csspseudoclass("focus-within") } -} \ No newline at end of file +} From 9318fe2b9a1712367d6479eb7a08a7ba4dd0dc92 Mon Sep 17 00:00:00 2001 From: Stanislav Mishchenko Date: Wed, 10 May 2023 12:57:54 +0300 Subject: [PATCH 06/36] Update filter settings dialog design and add a logic --- src/main/kotlin/solve/SolveApp.kt | 9 + .../kotlin/solve/catalogue/CatalogueUtils.kt | 2 - .../view/fields/CatalogueFieldsView.kt | 8 +- .../controller/FilterPanelController.kt | 18 ++ src/main/kotlin/solve/filters/model/Filter.kt | 10 +- .../controller/FilterSettingsController.kt | 20 ++ .../filters/settings/model/FilterSetting.kt | 9 + .../model/IndicesStepFilterSetting.kt} | 4 +- .../model/TimePeriodFilterSetting.kt} | 4 +- .../model/UIDFilterSetting.kt} | 6 +- .../settings/view/FilterSettingsView.kt | 201 ++++++++++++++++++ .../view/controls/FilterSettingControl.kt | 8 + .../controls/IndicesStepSettingControl.kt | 18 ++ .../view/controls/TimePeriodSettingControl.kt | 18 ++ .../view/controls/UIDSettingControl.kt | 18 ++ .../filters/view/FilterPanelFieldsView.kt | 4 +- .../solve/filters/view/FilterPanelView.kt | 12 +- .../solve/filters/view/FilterSettingsView.kt | 25 --- .../kotlin/solve/importer/view/AlertDialog.kt | 10 +- .../solve/importer/view/ControlPanel.kt | 39 ++-- .../solve/importer/view/DirectoryPathView.kt | 19 +- .../solve/importer/view/ImporterView.kt | 5 +- .../solve/importer/view/LoadingScreen.kt | 18 +- .../solve/importer/view/ProjectTreeView.kt | 7 +- src/main/kotlin/solve/main/MainView.kt | 14 +- .../view/association/AssociationAdorner.kt | 6 +- .../solve/sidepanel/tabs/SidePanelTabsView.kt | 8 +- .../styles/FilterPanelFieldsViewStylesheet.kt | 6 +- .../solve/styles/MFXButtonStyleSheet.kt | 4 +- .../solve/styles/SeparatorStylesheet.kt | 2 +- .../kotlin/solve/styles/SidePanelTabsStyle.kt | 18 +- src/main/kotlin/solve/styles/Style.kt | 55 ++--- src/main/kotlin/solve/styles/TooltipStyle.kt | 8 +- .../solve/styles/TreeTableViewStylesheet.kt | 6 +- src/main/kotlin/solve/utils/NodesUtils.kt | 5 +- src/main/kotlin/solve/utils/NumericUtlis.kt | 3 + .../utils/materialfx/MFXIntegerTextField.kt | 75 +++++++ .../utils/materialfx/MFXNodeFactoriesUtils.kt | 32 +++ .../{MFXUtils.kt => MFXNodesUtils.kt} | 46 ---- .../stylesheets/MFXRangeSliderStylesheet.kt | 6 +- .../MFXValidationTextFieldStylesheet.kt | 9 +- 41 files changed, 571 insertions(+), 224 deletions(-) create mode 100644 src/main/kotlin/solve/filters/controller/FilterPanelController.kt create mode 100644 src/main/kotlin/solve/filters/settings/controller/FilterSettingsController.kt create mode 100644 src/main/kotlin/solve/filters/settings/model/FilterSetting.kt rename src/main/kotlin/solve/filters/{model/IndicesStepFilter.kt => settings/model/IndicesStepFilterSetting.kt} (65%) rename src/main/kotlin/solve/filters/{model/TimePeriodFilter.kt => settings/model/TimePeriodFilterSetting.kt} (70%) rename src/main/kotlin/solve/filters/{model/UIDFilter.kt => settings/model/UIDFilterSetting.kt} (56%) create mode 100644 src/main/kotlin/solve/filters/settings/view/FilterSettingsView.kt create mode 100644 src/main/kotlin/solve/filters/settings/view/controls/FilterSettingControl.kt create mode 100644 src/main/kotlin/solve/filters/settings/view/controls/IndicesStepSettingControl.kt create mode 100644 src/main/kotlin/solve/filters/settings/view/controls/TimePeriodSettingControl.kt create mode 100644 src/main/kotlin/solve/filters/settings/view/controls/UIDSettingControl.kt delete mode 100644 src/main/kotlin/solve/filters/view/FilterSettingsView.kt create mode 100644 src/main/kotlin/solve/utils/materialfx/MFXIntegerTextField.kt create mode 100644 src/main/kotlin/solve/utils/materialfx/MFXNodeFactoriesUtils.kt rename src/main/kotlin/solve/utils/materialfx/{MFXUtils.kt => MFXNodesUtils.kt} (64%) diff --git a/src/main/kotlin/solve/SolveApp.kt b/src/main/kotlin/solve/SolveApp.kt index e3d32bb18..c61005721 100644 --- a/src/main/kotlin/solve/SolveApp.kt +++ b/src/main/kotlin/solve/SolveApp.kt @@ -10,6 +10,7 @@ import tornadofx.App import tornadofx.launch import solve.styles.ApplicationStylesheet import solve.utils.SVGImageLoaderDimensionProvider +import tornadofx.FX.Companion.stylesheets class SolveApp : App(MainView::class, ApplicationStylesheet::class) { override fun start(stage: Stage) { @@ -30,6 +31,14 @@ class SolveApp : App(MainView::class, ApplicationStylesheet::class) { 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") } } diff --git a/src/main/kotlin/solve/catalogue/CatalogueUtils.kt b/src/main/kotlin/solve/catalogue/CatalogueUtils.kt index 32fccb0a6..1f382f706 100644 --- a/src/main/kotlin/solve/catalogue/CatalogueUtils.kt +++ b/src/main/kotlin/solve/catalogue/CatalogueUtils.kt @@ -9,8 +9,6 @@ import tornadofx.tooltip val ProjectFrame.layers: List get() = landmarkFiles.map { it.projectLayer }.distinct() -fun Double.floor(): Int = kotlin.math.floor(this).toInt() - fun synchronizeListViewsSelections(firstListView: ListView, secondListView: ListView) { secondListView.selectionModel = firstListView.selectionModel } diff --git a/src/main/kotlin/solve/catalogue/view/fields/CatalogueFieldsView.kt b/src/main/kotlin/solve/catalogue/view/fields/CatalogueFieldsView.kt index 5c7907513..81bb9a614 100644 --- a/src/main/kotlin/solve/catalogue/view/fields/CatalogueFieldsView.kt +++ b/src/main/kotlin/solve/catalogue/view/fields/CatalogueFieldsView.kt @@ -13,11 +13,11 @@ import javafx.scene.input.TransferMode import javafx.scene.layout.Priority import javafx.scene.paint.Color import solve.catalogue.controller.CatalogueController -import solve.catalogue.floor import solve.catalogue.model.CatalogueField import solve.project.model.ProjectFrame import solve.scene.view.SceneView import solve.utils.deselectAllItems +import solve.utils.floorToInt import solve.utils.onSelectionChanged import solve.utils.selectAllItems import solve.utils.selectedItems @@ -115,7 +115,7 @@ abstract class CatalogueFieldsView : View() { private fun createFileNameFieldsSnapshot(fields: List): Image { val snapshotFields = fields.take(dragViewMaxFieldsNumber) - val prefSnapshotHeight = (snapshotFields.count() * listViewCellHeight).floor() + val prefSnapshotHeight = (snapshotFields.count() * listViewCellHeight).floorToInt() val fieldsSnapshotNode = createFieldsSnapshotNode(snapshotFields) fieldsListView.getChildList()?.remove(fieldsSnapshotNode) @@ -123,8 +123,8 @@ abstract class CatalogueFieldsView : View() { val nodeSnapshot = fieldsSnapshotNode.snapshot(null, null) return WritableImage( nodeSnapshot.pixelReader, - nodeSnapshot.width.floor(), - min(nodeSnapshot.height.floor(), prefSnapshotHeight) + nodeSnapshot.width.floorToInt(), + min(nodeSnapshot.height.floorToInt(), prefSnapshotHeight) ) } } diff --git a/src/main/kotlin/solve/filters/controller/FilterPanelController.kt b/src/main/kotlin/solve/filters/controller/FilterPanelController.kt new file mode 100644 index 000000000..eda03fb84 --- /dev/null +++ b/src/main/kotlin/solve/filters/controller/FilterPanelController.kt @@ -0,0 +1,18 @@ +package solve.filters.controller + +import solve.filters.model.Filter +import tornadofx.* + +class FilterPanelController : Controller() { + fun addFilter(filter: Filter) { + + } + + fun removeFilter(filter: Filter) { + + } + + fun editFilter(oldFilter: Filter, editedFilter: Filter) { + + } +} \ No newline at end of file diff --git a/src/main/kotlin/solve/filters/model/Filter.kt b/src/main/kotlin/solve/filters/model/Filter.kt index fbf7400f4..b26e4e654 100644 --- a/src/main/kotlin/solve/filters/model/Filter.kt +++ b/src/main/kotlin/solve/filters/model/Filter.kt @@ -1,9 +1,13 @@ package solve.filters.model +import solve.filters.settings.model.FilterSetting import solve.project.model.ProjectFrame -interface Filter { - fun apply(fields: List): List +data class Filter(val settings: List>) { + fun apply(frames: List): List { + var suitableFrames = frames + settings.forEach { suitableFrames = it.apply(suitableFrames) } - fun edit(newValue: T) + return suitableFrames + } } diff --git a/src/main/kotlin/solve/filters/settings/controller/FilterSettingsController.kt b/src/main/kotlin/solve/filters/settings/controller/FilterSettingsController.kt new file mode 100644 index 000000000..4874ad4a9 --- /dev/null +++ b/src/main/kotlin/solve/filters/settings/controller/FilterSettingsController.kt @@ -0,0 +1,20 @@ +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() { + val panelController: FilterPanelController by inject() + + fun createFilter(filterSettings: List>) { + val filter = Filter(filterSettings) + panelController.addFilter(filter) + } + + fun createEditedFilter(oldFilter: Filter, editedFilterSettings: List>) { + val editedFilter = Filter(editedFilterSettings) + panelController.editFilter(oldFilter, editedFilter) + } +} \ No newline at end of file diff --git a/src/main/kotlin/solve/filters/settings/model/FilterSetting.kt b/src/main/kotlin/solve/filters/settings/model/FilterSetting.kt new file mode 100644 index 000000000..a13fa1f28 --- /dev/null +++ b/src/main/kotlin/solve/filters/settings/model/FilterSetting.kt @@ -0,0 +1,9 @@ +package solve.filters.settings.model + +import solve.project.model.ProjectFrame + +interface FilterSetting { + fun apply(fields: List): List + + fun edit(newValue: T) +} diff --git a/src/main/kotlin/solve/filters/model/IndicesStepFilter.kt b/src/main/kotlin/solve/filters/settings/model/IndicesStepFilterSetting.kt similarity index 65% rename from src/main/kotlin/solve/filters/model/IndicesStepFilter.kt rename to src/main/kotlin/solve/filters/settings/model/IndicesStepFilterSetting.kt index 900b5c166..a963822db 100644 --- a/src/main/kotlin/solve/filters/model/IndicesStepFilter.kt +++ b/src/main/kotlin/solve/filters/settings/model/IndicesStepFilterSetting.kt @@ -1,8 +1,8 @@ -package solve.filters.model +package solve.filters.settings.model import solve.project.model.ProjectFrame -class IndicesStepFilter(private var step: Int) : Filter { +class IndicesStepFilterSetting(private var step: Int) : FilterSetting { override fun apply(fields: List) = fields.slice(0..fields.lastIndex step this.step) override fun edit(newValue: Int) { diff --git a/src/main/kotlin/solve/filters/model/TimePeriodFilter.kt b/src/main/kotlin/solve/filters/settings/model/TimePeriodFilterSetting.kt similarity index 70% rename from src/main/kotlin/solve/filters/model/TimePeriodFilter.kt rename to src/main/kotlin/solve/filters/settings/model/TimePeriodFilterSetting.kt index 1e3b12219..04e6b5cd3 100644 --- a/src/main/kotlin/solve/filters/model/TimePeriodFilter.kt +++ b/src/main/kotlin/solve/filters/settings/model/TimePeriodFilterSetting.kt @@ -1,9 +1,9 @@ -package solve.filters.model +package solve.filters.settings.model import solve.project.model.ProjectFrame import solve.utils.structures.IntPoint -class TimePeriodFilter(private var timePeriod: IntPoint) : Filter { +class TimePeriodFilterSetting(private var timePeriod: IntPoint) : FilterSetting { override fun apply(fields: List): List = fields.filter { it.timestamp in timePeriod.x..timePeriod.y } diff --git a/src/main/kotlin/solve/filters/model/UIDFilter.kt b/src/main/kotlin/solve/filters/settings/model/UIDFilterSetting.kt similarity index 56% rename from src/main/kotlin/solve/filters/model/UIDFilter.kt rename to src/main/kotlin/solve/filters/settings/model/UIDFilterSetting.kt index 017484bdc..cde70fc9c 100644 --- a/src/main/kotlin/solve/filters/model/UIDFilter.kt +++ b/src/main/kotlin/solve/filters/settings/model/UIDFilterSetting.kt @@ -1,9 +1,9 @@ -package solve.filters.model +package solve.filters.settings.model import solve.project.model.ProjectFrame -class UIDFilter(private var uid: Long) : Filter { - override fun apply(fields: List) = fields.filter { (it.uids as List<*>).contains(uid) } +class UIDFilterSetting(private var uid: Long) : FilterSetting { + override fun apply(fields: List) = fields.filter { it.uids.contains(uid) } override fun edit(newValue: Long) { uid = newValue diff --git a/src/main/kotlin/solve/filters/settings/view/FilterSettingsView.kt b/src/main/kotlin/solve/filters/settings/view/FilterSettingsView.kt new file mode 100644 index 000000000..6b5e1832e --- /dev/null +++ b/src/main/kotlin/solve/filters/settings/view/FilterSettingsView.kt @@ -0,0 +1,201 @@ +package solve.filters.settings.view + +import io.github.palexdev.materialfx.enums.FloatMode +import javafx.event.EventTarget +import javafx.geometry.Insets +import javafx.geometry.Pos +import javafx.scene.Node +import javafx.scene.control.CheckBox +import javafx.scene.layout.BorderPane +import javafx.scene.layout.HBox +import javafx.scene.layout.Pane +import javafx.scene.text.Font +import solve.filters.settings.controller.FilterSettingsController +import solve.filters.settings.model.FilterSetting +import solve.filters.settings.view.controls.FilterSettingControl +import solve.filters.settings.view.controls.IndicesStepSettingControl +import solve.filters.settings.view.controls.TimePeriodSettingControl +import solve.filters.settings.view.controls.UIDSettingControl +import solve.project.model.ProjectFrame +import solve.styles.Style +import solve.styles.Style.headerPadding +import solve.utils.materialfx.MFXIntegerTextField +import solve.utils.materialfx.MFXIntegerTextField.Companion.mfxIntegerTextField +import solve.utils.materialfx.MaterialFXDialog +import solve.utils.materialfx.controlButton +import solve.utils.materialfx.dialogHeaderLabel +import solve.utils.materialfx.mfxCheckbox +import solve.utils.materialfx.mfxRangeSlider +import tornadofx.* + +class FilterSettingsView : View() { + private val controller: FilterSettingsController by inject() + + private lateinit var stepNumberIntegerTextField: MFXIntegerTextField + private lateinit var uidIntegerTextField: MFXIntegerTextField + + private lateinit var timePeriodCheckbox: CheckBox + private lateinit var stepNumberCheckBox: CheckBox + private lateinit var uidIntegerCheckBox: CheckBox + private val checkboxes: List by lazy { + listOf(timePeriodCheckbox, stepNumberCheckBox, uidIntegerCheckBox) + } + + private val checkboxToSettingNodeMap = mutableMapOf() + private val settingControlsMap = mutableMapOf>() + + private val areAllIntegerTextFieldsValid: Boolean + get() = stepNumberIntegerTextField.isValid && uidIntegerTextField.isValid + private val selectedSettingCheckboxes = observableListOf() + + private val filterSettingsContentNode = borderpane { + top = vbox { + dialogHeaderLabel("Create new filter") { + padding = headerPadding + } + vbox(10) { + timePeriodSettingField() + indicesStepSettingField() + uidSettingField() + + paddingTop = 10.0 + paddingLeft = 30.0 + } + + paddingRight = 20.0 + } + bottom = hbox(Style.ControlButtonsSpacing) { + controlButton("CANCEL") { + action { + this@FilterSettingsView.close() + } + } + controlButton("CREATE") { + isDisable = selectedSettingCheckboxes.isEmpty() + selectedSettingCheckboxes.onChange { + isDisable = selectedSettingCheckboxes.isEmpty() + } + + action { + val filterSettings = getFilterSettings() + println(filterSettings) + } + } + + alignment = Pos.CENTER_RIGHT + BorderPane.setMargin(this, Insets(20.0, 0.0, 0.0, 0.0)) + } + } + override val root = filterSettingsContentNode + + fun showDialog(parent: View) { + val content = MaterialFXDialog.createGenericDialog(root) + val dialog = MaterialFXDialog.createStageDialog(content, parent.currentStage, parent.root as Pane) + dialog.isDraggable = false + + dialog.show() + dialog.centerOnScreen() + } + + private fun EventTarget.integerTextField( + notIntegerErrorMessage: String, + width: Double, + maxWidth: Double + ) = mfxIntegerTextField(notIntegerErrorMessage) { + style { + fontFamily = Style.Font + } + minWidth = width + prefWidth = width + this.maxWidth = maxWidth + + textLimit = IntegerTextFieldSymbolsLimit + floatMode = FloatMode.ABOVE + + attachTo(this@FilterSettingsView) + } + + private fun EventTarget.filterSettingField(name: String, settingNode: Node, op: HBox.() -> Unit = {}) = hbox(10) { + val checkBox = mfxCheckbox { + paddingTop = -7.0 + + selectedProperty().onChange { selected -> + if (selected) { + selectedSettingCheckboxes.add(this) + } else { + selectedSettingCheckboxes.remove(this) + } + } + } + checkboxToSettingNodeMap[checkBox] = settingNode + + hbox { + add(checkBox) + label(name) { + font = Font.font(Style.Font, FilterSettingNameFontSize) + } + + paddingTop = FilterSettingNonTextFieldPaddingTop + } + add(settingNode) + attachTo(this@filterSettingField, op) + } + + private fun EventTarget.timePeriodSettingField(): HBox { + val timePeriodRangeSlider = mfxRangeSlider(0.0, 10.0, 1.0, 9.0) { + prefWidth = TimeLimitRangeSliderWidth + paddingTop = FilterSettingNonTextFieldPaddingTop + 2.0 + } + settingControlsMap[timePeriodRangeSlider] = TimePeriodSettingControl(timePeriodRangeSlider) + + return filterSettingField( + "Time period", + timePeriodRangeSlider + ) + } + + private fun EventTarget.indicesStepSettingField(): HBox { + stepNumberIntegerTextField = integerTextField("Step must be an integer number", 60.0, 145.0) + settingControlsMap[stepNumberIntegerTextField] = IndicesStepSettingControl(stepNumberIntegerTextField) + + return filterSettingField( + "Show every", + stepNumberIntegerTextField + ) { + label("image") { + font = Font.font(Style.Font, FilterSettingNameFontSize) + + paddingTop = FilterSettingNonTextFieldPaddingTop + } + } + } + + private fun EventTarget.uidSettingField(): HBox { + uidIntegerTextField = integerTextField("UID must be an integer number", 155.0, 155.0) + settingControlsMap[uidIntegerTextField] = UIDSettingControl(uidIntegerTextField) + + return filterSettingField( + "Show images with landmark:", + uidIntegerTextField + ) + } + + private fun getFilterSettings(): List> { + val enabledCheckboxesSettingNodes = selectedSettingCheckboxes.map { checkboxToSettingNodeMap[it] } + val enabledSettingsControls = enabledCheckboxesSettingNodes.map { settingControlsMap[it] } + + return enabledSettingsControls.mapNotNull { it?.extrudeFilterSettings() } + } + + private fun getFramesMinTimestamp(frames: List) = frames.minOf { it.timestamp } + + private fun getFramesMaxTimestamp(frames: List) = frames.maxOf { it.timestamp } + + companion object { + private const val IntegerTextFieldSymbolsLimit = Long.MIN_VALUE.toString().length + + private const val FilterSettingNameFontSize = 14.0 + private const val FilterSettingNonTextFieldPaddingTop = 8.0 + private const val TimeLimitRangeSliderWidth = 300.0 + } +} diff --git a/src/main/kotlin/solve/filters/settings/view/controls/FilterSettingControl.kt b/src/main/kotlin/solve/filters/settings/view/controls/FilterSettingControl.kt new file mode 100644 index 000000000..6c350d43f --- /dev/null +++ b/src/main/kotlin/solve/filters/settings/view/controls/FilterSettingControl.kt @@ -0,0 +1,8 @@ +package solve.filters.settings.view.controls + +import javafx.scene.Node +import solve.filters.settings.model.FilterSetting + +abstract class FilterSettingControl >(protected val controlNode: T) { + abstract fun extrudeFilterSettings() : FilterSetting<*>? +} diff --git a/src/main/kotlin/solve/filters/settings/view/controls/IndicesStepSettingControl.kt b/src/main/kotlin/solve/filters/settings/view/controls/IndicesStepSettingControl.kt new file mode 100644 index 000000000..12a298a8d --- /dev/null +++ b/src/main/kotlin/solve/filters/settings/view/controls/IndicesStepSettingControl.kt @@ -0,0 +1,18 @@ +package solve.filters.settings.view.controls + +import solve.filters.settings.model.IndicesStepFilterSetting +import solve.utils.materialfx.MFXIntegerTextField + +class IndicesStepSettingControl( + controlNode: MFXIntegerTextField +) : FilterSettingControl(controlNode) { + override fun extrudeFilterSettings(): IndicesStepFilterSetting? { + if (!controlNode.isValid || controlNode.text.isEmpty()) { + return null + } + + val stepNumber = controlNode.text.toInt() + + return IndicesStepFilterSetting(stepNumber) + } +} diff --git a/src/main/kotlin/solve/filters/settings/view/controls/TimePeriodSettingControl.kt b/src/main/kotlin/solve/filters/settings/view/controls/TimePeriodSettingControl.kt new file mode 100644 index 000000000..508c5c037 --- /dev/null +++ b/src/main/kotlin/solve/filters/settings/view/controls/TimePeriodSettingControl.kt @@ -0,0 +1,18 @@ +package solve.filters.settings.view.controls + +import org.controlsfx.control.RangeSlider +import solve.filters.settings.model.TimePeriodFilterSetting +import solve.utils.ceilToInt +import solve.utils.floorToInt +import solve.utils.structures.IntPoint + +class TimePeriodSettingControl( + controlNode: RangeSlider +) : FilterSettingControl(controlNode) { + override fun extrudeFilterSettings(): TimePeriodFilterSetting { + val fromTimePeriod = controlNode.lowValue.floorToInt() + val toTimePeriod = controlNode.highValue.ceilToInt() + + return TimePeriodFilterSetting(IntPoint(fromTimePeriod, toTimePeriod)) + } +} diff --git a/src/main/kotlin/solve/filters/settings/view/controls/UIDSettingControl.kt b/src/main/kotlin/solve/filters/settings/view/controls/UIDSettingControl.kt new file mode 100644 index 000000000..a07b534ee --- /dev/null +++ b/src/main/kotlin/solve/filters/settings/view/controls/UIDSettingControl.kt @@ -0,0 +1,18 @@ +package solve.filters.settings.view.controls + +import solve.filters.settings.model.UIDFilterSetting +import solve.utils.materialfx.MFXIntegerTextField + +class UIDSettingControl( + controlNode: MFXIntegerTextField +) : FilterSettingControl(controlNode) { + override fun extrudeFilterSettings(): UIDFilterSetting? { + if (!controlNode.isValid || controlNode.text.isEmpty()) { + return null + } + + val uid = controlNode.text.toLong() + + return UIDFilterSetting(uid) + } +} diff --git a/src/main/kotlin/solve/filters/view/FilterPanelFieldsView.kt b/src/main/kotlin/solve/filters/view/FilterPanelFieldsView.kt index 725f3addb..3fa8d09b0 100644 --- a/src/main/kotlin/solve/filters/view/FilterPanelFieldsView.kt +++ b/src/main/kotlin/solve/filters/view/FilterPanelFieldsView.kt @@ -5,7 +5,7 @@ import io.github.palexdev.materialfx.effects.DepthLevel import javafx.beans.binding.Bindings import solve.constants.IconsDeletePath import solve.constants.IconsEditPath -import solve.filters.model.Filter +import solve.filters.settings.model.FilterSetting import solve.styles.FilterPanelFieldsViewStylesheet import solve.utils.createHGrowHBox import solve.utils.imageViewIcon @@ -18,7 +18,7 @@ class FilterPanelFieldsView : View() { private val editIconImage = loadResourcesImage(IconsEditPath) private val deleteIconImage = loadResourcesImage(IconsDeletePath) - val filtersListView = mfxCheckListView> { + val filtersListView = mfxCheckListView> { addStylesheet(FilterPanelFieldsViewStylesheet::class) depthLevel = DepthLevel.LEVEL0 diff --git a/src/main/kotlin/solve/filters/view/FilterPanelView.kt b/src/main/kotlin/solve/filters/view/FilterPanelView.kt index 245956e34..e29185817 100644 --- a/src/main/kotlin/solve/filters/view/FilterPanelView.kt +++ b/src/main/kotlin/solve/filters/view/FilterPanelView.kt @@ -2,6 +2,7 @@ package solve.filters.view import javafx.scene.paint.Paint import solve.constants.IconsFiltersAddPath +import solve.filters.settings.view.FilterSettingsView import solve.styles.SeparatorStylesheet import solve.styles.Style import solve.utils.createHGrowHBox @@ -13,6 +14,7 @@ import tornadofx.* class FilterPanelView : View() { private val filterPanelFieldsView: FilterPanelFieldsView by inject() + private val filterSettingsView: FilterSettingsView by inject() override val root = vbox { separator { @@ -23,9 +25,9 @@ class FilterPanelView : View() { label("Filters") { style { - fontFamily = Style.font + fontFamily = Style.Font fontSize = createPxValue(HeaderFontSize) - textFill = Paint.valueOf(Style.headerFontColor) + textFill = Paint.valueOf(Style.HeaderFontColor) } paddingLeft = 15.0 @@ -38,7 +40,11 @@ class FilterPanelView : View() { null } hbox { - mfxCircleButton(addButtonGraphic, AddFilterButtonSize) + mfxCircleButton(addButtonGraphic, AddFilterButtonSize) { + action { + filterSettingsView.showDialog(this@FilterPanelView) + } + } paddingRight = 14.0 paddingTop = -6.0 } diff --git a/src/main/kotlin/solve/filters/view/FilterSettingsView.kt b/src/main/kotlin/solve/filters/view/FilterSettingsView.kt deleted file mode 100644 index d1e459283..000000000 --- a/src/main/kotlin/solve/filters/view/FilterSettingsView.kt +++ /dev/null @@ -1,25 +0,0 @@ -package solve.filters.view - -import javafx.scene.Node -import solve.project.model.ProjectFrame -import solve.utils.materialfx.mfxCheckbox -import tornadofx.* - -class FilterSettingsView : View() { - private val filterSettingsContentNode = borderpane { - top = hbox { - buildFilterSettingField("Time period", pane()) - } - } - override val root = filterSettingsContentNode - - private fun buildFilterSettingField(name: String, settingNode: Node) = hbox { - mfxCheckbox() - label(name) - add(settingNode) - } - - private fun getFramesMinTimestamp(frames: List) = frames.minOf { it.timestamp } - - private fun getFramesMaxTimestamp(frames: List) = frames.maxOf { it.timestamp } -} diff --git a/src/main/kotlin/solve/importer/view/AlertDialog.kt b/src/main/kotlin/solve/importer/view/AlertDialog.kt index 2cba741d8..1ea81c1ca 100644 --- a/src/main/kotlin/solve/importer/view/AlertDialog.kt +++ b/src/main/kotlin/solve/importer/view/AlertDialog.kt @@ -28,19 +28,19 @@ class AlertDialog(contentText: String) : View() { graphic = ImageView(errorIcon) BorderPane.setMargin(this, Insets(0.0, 0.0, 0.0, 14.0)) prefHeight = 0.0 - style = "-fx-font-family: ${Style.fontCondensed}; -fx-font-size: ${Style.headerFontSize};" + style = "-fx-font-family: ${Style.FontCondensed}; -fx-font-size: ${Style.HeaderFontSize};" } } center { label(contentText) { mfxContextMenu { - style = "-fx-font-family: ${Style.fontCondensed}; -fx-font-size: ${Style.mainFontSize};" + style = "-fx-font-family: ${Style.FontCondensed}; -fx-font-size: ${Style.MainFontSize};" copyError(text) } BorderPane.setMargin(this, Insets(0.0, 14.0, 0.0, 14.0)) style = - "-fx-font-family: ${Style.font}; -fx-font-size: ${Style.mainFontSize}; -fx-text-fill: #000000;" + "-fx-font-family: ${Style.Font}; -fx-font-size: ${Style.MainFontSize}; -fx-text-fill: #000000;" BorderPane.setAlignment(this, Pos.CENTER_LEFT) isWrapText = true prefWidth = 300.0 @@ -53,8 +53,8 @@ class AlertDialog(contentText: String) : View() { maxWidth = 40.0 prefHeight = 23.0 style = - "-fx-font-family: ${Style.fontCondensed}; -fx-font-size: ${Style.buttonFontSize}; " + - "-fx-font-weight: ${Style.fontWeightBold}; -fx-text-fill: #78909C;" + "-fx-font-family: ${Style.FontCondensed}; -fx-font-size: ${Style.ButtonFontSize}; " + + "-fx-font-weight: ${Style.FontWeightBold}; -fx-text-fill: #78909C;" BorderPane.setAlignment(this, Pos.TOP_RIGHT) prefWidth = 180.0 action { diff --git a/src/main/kotlin/solve/importer/view/ControlPanel.kt b/src/main/kotlin/solve/importer/view/ControlPanel.kt index 36ebdb2cf..4a7866eeb 100644 --- a/src/main/kotlin/solve/importer/view/ControlPanel.kt +++ b/src/main/kotlin/solve/importer/view/ControlPanel.kt @@ -5,7 +5,6 @@ import javafx.geometry.Insets import javafx.scene.image.ImageView import javafx.scene.input.KeyCode import javafx.scene.input.KeyCodeCombination -import javafx.scene.paint.Paint import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.launch @@ -20,10 +19,12 @@ import solve.importer.model.FrameAfterPartialParsing import solve.main.MainController import solve.main.MainView import solve.styles.Style +import solve.styles.Style.ControlButtonsSpacing import solve.utils.createAlertForError import solve.utils.loadResourcesImage +import solve.utils.materialfx.ControlButtonWidth import solve.utils.materialfx.MaterialFXDialog -import solve.utils.materialfx.mfxButton +import solve.utils.materialfx.controlButton import solve.utils.toStringWithoutBrackets import tornadofx.* @@ -40,8 +41,6 @@ class ControlPanel : View() { private val loading: LoadingScreen by inject() - private val buttonStyle = Style.buttonStyle - private val importButtonModel = ButtonModel() private val filesCountIcon = loadResourcesImage(IconsImporterCheckCirclePath) @@ -52,8 +51,7 @@ class ControlPanel : View() { private val algorithmsLabel = label { val listOfKind = mutableListOf() visibleWhen { controller.projectAfterPartialParsing.isNotNull } - - style = "-fx-font-family: ${Style.fontCondensed}; -fx-font-size: ${Style.mainFontSize};" + style = "-fx-font-family: ${Style.FontCondensed}; -fx-font-size: ${Style.MainFontSize};" controller.projectAfterPartialParsing.onChange { it?.let { @@ -61,36 +59,31 @@ class ControlPanel : View() { getListOfAlgorithms(frame, listOfKind) } this.text = "Algorithms: " + listOfKind.toStringWithoutBrackets() + tooltip(listOfKind.toStringWithoutBrackets()) } } } - private val importButton = mfxButton("IMPORT") { + private val importButton = controlButton("IMPORT") { visibleWhen { controller.projectAfterPartialParsing.isNotNull } - maxWidth = 75.0 - prefHeight = 23.0 - style = buttonStyle buttonController.changeDisable(importButtonModel) isDisable = importButtonModel.disabled.value isFocusTraversable = false - prefWidth = ButtonWidth + prefWidth = ControlButtonWidth + importButtonModel.disabled.onChange { disableValue -> disableValue?.also { isDisable = it } } action { - importAction(this@mfxButton) + importAction(this) } } - private val cancelButton = mfxButton("CANCEL") { - maxWidth = 75.0 - prefHeight = 23.0 - style = buttonStyle - textFill = Paint.valueOf(Style.primaryColor) + private val cancelButton = controlButton("CANCEL") { isFocusTraversable = false - prefWidth = ButtonWidth + action { cancelAction() } @@ -111,7 +104,7 @@ class ControlPanel : View() { private val countImagesLabel = label { visibleWhen { controller.projectAfterPartialParsing.isNotNull } - style = "-fx-font-family: ${Style.fontCondensed}; -fx-font-size: ${Style.mainFontSize};" + style = "-fx-font-family: ${Style.FontCondensed}; -fx-font-size: ${Style.MainFontSize};" controller.projectAfterPartialParsing.onChange { val countFiles = it?.projectFrames?.count() this.text = "$countFiles images found" @@ -122,7 +115,7 @@ class ControlPanel : View() { private val countErrorsLabel = label { visibleWhen { controller.projectAfterPartialParsing.isNotNull } - style = "-fx-font-family: ${Style.fontCondensed}; -fx-font-size: ${Style.mainFontSize};" + style = "-fx-font-family: ${Style.FontCondensed}; -fx-font-size: ${Style.MainFontSize};" controller.projectAfterPartialParsing.onChange { var countErrors = 0 it?.projectFrames?.forEach { frame -> @@ -148,7 +141,7 @@ class ControlPanel : View() { } } right { - hbox(10) { + hbox(ControlButtonsSpacing) { add(cancelButton) controller.projectAfterPartialParsing.onChange { this.clear() @@ -199,8 +192,4 @@ class ControlPanel : View() { } } } - - companion object { - private const val ButtonWidth = 180.0 - } } diff --git a/src/main/kotlin/solve/importer/view/DirectoryPathView.kt b/src/main/kotlin/solve/importer/view/DirectoryPathView.kt index eb6be0679..5409458a1 100644 --- a/src/main/kotlin/solve/importer/view/DirectoryPathView.kt +++ b/src/main/kotlin/solve/importer/view/DirectoryPathView.kt @@ -30,15 +30,15 @@ class DirectoryPathView : View() { isAllowEdit = false isDisable = true - textFill = Color.valueOf(Style.onBackgroundColor) + textFill = Color.valueOf(Style.OnBackgroundColor) prefHeight = 48.0 prefWidth = 280.0 BorderPane.setAlignment(this, Pos.CENTER) BorderPane.setMargin(this, Insets(16.0, 0.0, 0.0, 0.0)) - style = "-fx-border-color: #${Style.primaryColorLight}; -fx-font-size: ${Style.mainFontSize}; " + - "-fx-font-family: ${Style.fontCondensed}" + style = "-fx-border-color: #${Style.PrimaryColorLight}; -fx-font-size: ${Style.MainFontSize}; " + + "-fx-font-family: ${Style.FontCondensed}" floatMode = FloatMode.BORDER floatingText = "Project directory" @@ -60,17 +60,18 @@ class DirectoryPathView : View() { prefHeight = 0.0 prefWidth = 200.0 - style = "-fx-font-family: ${Style.fontCondensed}; -fx-font-size: ${Style.headerFontSize}; " + style = "-fx-font-family: ${Style.FontCondensed}; -fx-font-size: ${Style.HeaderFontSize}; " + VBox.setMargin(this, Insets(0.0, 0.0, 0.0, 0.0)) } private val selectButton = mfxButton("SELECT") { - style = "-fx-border-color: #${Style.primaryColor}; -fx-font-size: ${Style.buttonFontSize}; " + - "-fx-font-family: ${Style.fontCondensed}; -fx-font-weight:700; -fx-border-radius: 4px; " + - "-fx-text-fill: #${Style.primaryColor};" + style = "-fx-border-color: #${Style.PrimaryColor}; -fx-font-size: ${Style.ButtonFontSize}; " + + "-fx-font-family: ${Style.FontCondensed}; -fx-font-weight:700; -fx-border-radius: 4px; " + + "-fx-text-fill: #${Style.PrimaryColor};" BorderPane.setAlignment(this, Pos.CENTER) BorderPane.setMargin(this, Insets(15.0, 0.0, 0.0, 0.0)) - textFill = Color.valueOf(Style.primaryColor) + textFill = Color.valueOf(Style.PrimaryColor) alignment = Pos.CENTER depthLevel = DepthLevel.LEVEL1 isFocusTraversable = false @@ -103,7 +104,7 @@ class DirectoryPathView : View() { } } } - style = "-fx-background-color: #${Style.surfaceColor}" + style = "-fx-background-color: #${Style.SurfaceColor}" BorderPane.setAlignment(this, Pos.CENTER) add(labelName) borderpane { diff --git a/src/main/kotlin/solve/importer/view/ImporterView.kt b/src/main/kotlin/solve/importer/view/ImporterView.kt index 4563d3f4d..b2c995a25 100644 --- a/src/main/kotlin/solve/importer/view/ImporterView.kt +++ b/src/main/kotlin/solve/importer/view/ImporterView.kt @@ -6,12 +6,9 @@ import tornadofx.* class ImporterView : View() { override val root = borderpane { - 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") addStylesheet(TooltipStyle::class) - style = "-fx-background-color: #${Style.surfaceColor};" + style = "-fx-background-color: #${Style.SurfaceColor};" prefHeight = 555.0 prefWidth = 453.0 diff --git a/src/main/kotlin/solve/importer/view/LoadingScreen.kt b/src/main/kotlin/solve/importer/view/LoadingScreen.kt index 51b63f156..caf7c601c 100644 --- a/src/main/kotlin/solve/importer/view/LoadingScreen.kt +++ b/src/main/kotlin/solve/importer/view/LoadingScreen.kt @@ -3,16 +3,17 @@ package solve.importer.view import io.github.palexdev.materialfx.controls.MFXProgressSpinner import javafx.geometry.Insets import javafx.scene.layout.BorderPane -import javafx.scene.layout.Priority import javafx.scene.layout.VBox import javafx.scene.paint.Color import kotlinx.coroutines.cancel import solve.styles.Style +import solve.styles.Style.headerPadding +import solve.utils.materialfx.dialogHeaderLabel import solve.utils.materialfx.mfxButton import tornadofx.* class LoadingScreen : View("Loading") { - private val primaryColor: Color = Color.valueOf(Style.primaryColor) + private val primaryColor: Color = Color.valueOf(Style.PrimaryColor) private val controlPanel: ControlPanel by inject() @@ -29,16 +30,11 @@ class LoadingScreen : View("Loading") { prefWidth = 453.0 style = "-fx-background-color: #ffffff" top { - label("Import a directory") { - hgrow = Priority.ALWAYS - prefHeight = 0.0 - prefWidth = 200.0 - style = "-fx-font-family: ${Style.fontCondensed}; -fx-font-size: ${Style.headerFontSize}" - BorderPane.setMargin(this, Insets(0.0, 0.0, 0.0, 24.0)) + dialogHeaderLabel("Import a directory") { + padding = headerPadding } } center { - vbox(15) { BorderPane.setMargin(this, Insets(200.0, 0.0, 0.0, 170.0)) add( @@ -47,7 +43,7 @@ class LoadingScreen : View("Loading") { } ) label("Please, wait...") { - style = "-fx-font-size: ${Style.headerFontSize}; -fx-font-family: ${Style.fontCondensed}" + style = "-fx-font-size: ${Style.HeaderFontSize}; -fx-font-family: ${Style.FontCondensed}" } } } @@ -58,7 +54,7 @@ class LoadingScreen : View("Loading") { BorderPane.setMargin(this, Insets(0.0, 24.0, 24.0, 0.0)) maxWidth = 75.0 prefHeight = 23.0 - style = Style.buttonStyle + style = Style.ButtonStyle action { controlPanel.coroutineScope.cancel() close() diff --git a/src/main/kotlin/solve/importer/view/ProjectTreeView.kt b/src/main/kotlin/solve/importer/view/ProjectTreeView.kt index 209746c38..4bbf23c2a 100644 --- a/src/main/kotlin/solve/importer/view/ProjectTreeView.kt +++ b/src/main/kotlin/solve/importer/view/ProjectTreeView.kt @@ -40,9 +40,8 @@ open class ProjectTreeView : View() { override val root = treetableview(rootTree) { addStylesheet(TreeTableViewStylesheet::class) - style = - "-fx-font-family: ${Style.font}; -fx-text-fill: #${Style.primaryColor}; " + - "-fx-font-size: ${Style.buttonFontSize};" + style = "-fx-font-family: ${Style.Font}; -fx-text-fill: #${Style.PrimaryColor}; " + + "-fx-font-size: ${Style.ButtonFontSize};" BorderPane.setMargin(this, Insets(0.0, 0.0, 2.0, 15.0)) visibleWhen { controller.projectAfterPartialParsing.isNotNull } @@ -60,7 +59,7 @@ open class ProjectTreeView : View() { filesColumn.cellValueFactory = TreeItemPropertyValueFactory("file") errorsColumn.cellValueFactory = TreeItemPropertyValueFactory("file") - filesColumn.setCellFactory { _ -> + filesColumn.setCellFactory { object : TreeTableCell() { private val imageIcon = loadResourcesImage(IconsImporterPhotoPath) private val fileIcon = loadResourcesImage(IconsImporterDescriptionPath) diff --git a/src/main/kotlin/solve/main/MainView.kt b/src/main/kotlin/solve/main/MainView.kt index 6a6912137..54dec7568 100644 --- a/src/main/kotlin/solve/main/MainView.kt +++ b/src/main/kotlin/solve/main/MainView.kt @@ -32,10 +32,10 @@ import solve.sidepanel.tabs.SidePanelTabsView import solve.styles.MFXButtonStyleSheet import solve.styles.Style import solve.styles.TooltipStyle -import solve.utils.MaterialFXDialog import solve.utils.createPxBox import solve.utils.loadResourcesImage -import solve.utils.mfxButton +import solve.utils.materialfx.MaterialFXDialog +import solve.utils.materialfx.mfxButton import tornadofx.* class MainView : View() { @@ -85,7 +85,7 @@ class MainView : View() { clip = circle graphic = ImageView(importIcon) setPrefSize(56.0, 56.0) - style = "-fx-background-color: #${Style.secondaryColor}; -fx-background-radius: 28;" + style = "-fx-background-color: #${Style.SecondaryColor}; -fx-background-radius: 28;" isFocusTraversable = false tooltip("Ctrl+I") action { @@ -100,13 +100,13 @@ class MainView : View() { private val helpButton = createTabButton("Help", helpIcon) private val nameApp = label("SOLVE") { - style = "-fx-font-family: ${Style.font}; -fx-font-weight:700; -fx-font-size: 18px" + style = "-fx-font-family: ${Style.Font}; -fx-font-weight:700; -fx-font-size: 18px" VBox.setMargin(this, Insets(0.0, 6.0, 0.0, 6.0)) } private val leftPanel = vbox(7) { addStylesheet(MFXButtonStyleSheet::class) - style = "-fx-background-color: #${Style.surfaceColor}" + style = "-fx-background-color: #${Style.SurfaceColor}" add(nameApp) add(importFab) add(leftSidePanelViews.tabsView.root) @@ -114,7 +114,7 @@ class MainView : View() { private val rightPanel = vbox(7) { addStylesheet(MFXButtonStyleSheet::class) - style = "-fx-background-color: #${Style.surfaceColor}" + style = "-fx-background-color: #${Style.SurfaceColor}" add(rightSidePanelViews.tabsView.root) } @@ -164,7 +164,7 @@ class MainView : View() { return mfxButton(text) { clip = Style.circleForRipple(this) styleClass.add("mfxButton") - setPrefSize(Style.navigationRailTabSize, Style.navigationRailTabSize) + setPrefSize(Style.NavigationRailTabSize, Style.NavigationRailTabSize) paddingAll = 0.0 contentDisplay = ContentDisplay.TOP graphic = ImageView(icon) diff --git a/src/main/kotlin/solve/scene/view/association/AssociationAdorner.kt b/src/main/kotlin/solve/scene/view/association/AssociationAdorner.kt index 5555f6607..b284aac33 100644 --- a/src/main/kotlin/solve/scene/view/association/AssociationAdorner.kt +++ b/src/main/kotlin/solve/scene/view/association/AssociationAdorner.kt @@ -50,12 +50,12 @@ class AssociationAdorner( rectangle = Rectangle() rectangle.width = width * scale.value rectangle.height = height * scale.value - rectangle.fill = Paint.valueOf(Style.primaryColor) + rectangle.fill = Paint.valueOf(Style.PrimaryColor) rectangle.opacity = RectangleOpacity val label = Label("Select second frame").also { - it.textFill = Paint.valueOf(Style.surfaceColor) - it.font = Font.font(Style.fontCondensed, FontWeight.BOLD, 18.0) + it.textFill = Paint.valueOf(Style.SurfaceColor) + it.font = Font.font(Style.FontCondensed, FontWeight.BOLD, 18.0) } pane.add(rectangle) diff --git a/src/main/kotlin/solve/sidepanel/tabs/SidePanelTabsView.kt b/src/main/kotlin/solve/sidepanel/tabs/SidePanelTabsView.kt index d5f6df59d..b5ed266f9 100644 --- a/src/main/kotlin/solve/sidepanel/tabs/SidePanelTabsView.kt +++ b/src/main/kotlin/solve/sidepanel/tabs/SidePanelTabsView.kt @@ -27,7 +27,7 @@ open class SidePanelTabsView : View() { override val root = tabsVBox.also { it.addStylesheet(SidePanelTabsStyle::class) - it.style = "-fx-background-color: #${Style.surfaceColor}" + it.style = "-fx-background-color: #${Style.SurfaceColor}" } init { @@ -43,13 +43,13 @@ open class SidePanelTabsView : View() { private fun addTab(tab: SidePanelTab) { val tabButton = togglebutton(tab.name, tabsToggleGroup) { - setPrefSize(Style.navigationRailTabSize, Style.navigationRailTabSize) + setPrefSize(Style.NavigationRailTabSize, Style.NavigationRailTabSize) clip = Style.circleForRipple(this) styleClass.add(tab.name.lowercase()) contentDisplay = ContentDisplay.TOP style = - "-fx-font-family: ${Style.font}; -fx-font-weight:700;" + - " -fx-font-size: ${Style.buttonFontSize}; -fx-background-radius: 36" + "-fx-font-family: ${Style.Font}; -fx-font-weight:700;" + + " -fx-font-size: ${Style.ButtonFontSize}; -fx-background-radius: 36" isFocusTraversable = false tooltip(tab.tooltip) diff --git a/src/main/kotlin/solve/styles/FilterPanelFieldsViewStylesheet.kt b/src/main/kotlin/solve/styles/FilterPanelFieldsViewStylesheet.kt index b5362d134..eea646c08 100644 --- a/src/main/kotlin/solve/styles/FilterPanelFieldsViewStylesheet.kt +++ b/src/main/kotlin/solve/styles/FilterPanelFieldsViewStylesheet.kt @@ -26,7 +26,7 @@ class FilterPanelFieldsViewStylesheet : Stylesheet() { mfxCheckbox { rippleContainer { box { - borderColor += createCssBoxWithValue(Paint.valueOf(Style.separatorLineColor)) + borderColor += createCssBoxWithValue(Paint.valueOf(Style.SeparatorLineColor)) borderWidth += createPxBoxWithValue(CheckBoxBorderWidth) mark { @@ -50,8 +50,8 @@ class FilterPanelFieldsViewStylesheet : Stylesheet() { dataLabel { fontSize = createPxValue(ListFieldFontSize) - fontFamily = Style.font - textFill = Paint.valueOf(Style.listFontColor) + fontFamily = Style.Font + textFill = Paint.valueOf(Style.ListFontColor) } } } diff --git a/src/main/kotlin/solve/styles/MFXButtonStyleSheet.kt b/src/main/kotlin/solve/styles/MFXButtonStyleSheet.kt index ac7a0c505..8bb6e2d7a 100644 --- a/src/main/kotlin/solve/styles/MFXButtonStyleSheet.kt +++ b/src/main/kotlin/solve/styles/MFXButtonStyleSheet.kt @@ -9,10 +9,10 @@ class MFXButtonStyleSheet : Stylesheet() { init { mfxButton { and(hover, pressed) { - backgroundColor += Paint.valueOf(Style.backgroundColor) + backgroundColor += Paint.valueOf(Style.BackgroundColor) } and(pressed) { - backgroundColor += Paint.valueOf(Style.surfaceColor) + backgroundColor += Paint.valueOf(Style.SurfaceColor) } } } diff --git a/src/main/kotlin/solve/styles/SeparatorStylesheet.kt b/src/main/kotlin/solve/styles/SeparatorStylesheet.kt index 67b08af2e..08a08203d 100644 --- a/src/main/kotlin/solve/styles/SeparatorStylesheet.kt +++ b/src/main/kotlin/solve/styles/SeparatorStylesheet.kt @@ -7,7 +7,7 @@ import tornadofx.* class SeparatorStylesheet : Stylesheet() { init { Companion.separator { - backgroundColor += Paint.valueOf(Style.separatorLineColor) + backgroundColor += Paint.valueOf(Style.SeparatorLineColor) Companion.line { borderWidth += createPxBoxWithValue(0.0) } diff --git a/src/main/kotlin/solve/styles/SidePanelTabsStyle.kt b/src/main/kotlin/solve/styles/SidePanelTabsStyle.kt index b255d43c4..a74b4b158 100644 --- a/src/main/kotlin/solve/styles/SidePanelTabsStyle.kt +++ b/src/main/kotlin/solve/styles/SidePanelTabsStyle.kt @@ -20,23 +20,23 @@ class SidePanelTabsStyle : Stylesheet() { init { toggleButton { - backgroundColor += Paint.valueOf(Style.surfaceColor) + backgroundColor += Paint.valueOf(Style.SurfaceColor) backgroundInsets += createPxBoxWithValue(0.0) backgroundRadius += createPxBoxWithValue(0.0) - textFill = Paint.valueOf(Style.primaryColorLight) + textFill = Paint.valueOf(Style.PrimaryColorLight) and(selected) { - textFill = Paint.valueOf(Style.primaryColor) + textFill = Paint.valueOf(Style.PrimaryColor) } } project { graphic = URI(IconsProject) and(hover) { - backgroundColor += Paint.valueOf(Style.backgroundColor) + backgroundColor += Paint.valueOf(Style.BackgroundColor) focusColor = Color.TRANSPARENT } and(selected) { - backgroundColor += Paint.valueOf(Style.surfaceColor) + backgroundColor += Paint.valueOf(Style.SurfaceColor) graphic = URI(IconsProjectFilled) } } @@ -45,11 +45,11 @@ class SidePanelTabsStyle : Stylesheet() { graphic = URI(IconsLayers) and(hover) { - backgroundColor += Paint.valueOf(Style.backgroundColor) + backgroundColor += Paint.valueOf(Style.BackgroundColor) focusColor = Color.TRANSPARENT } and(selected) { - backgroundColor += Paint.valueOf(Style.surfaceColor) + backgroundColor += Paint.valueOf(Style.SurfaceColor) graphic = URI(IconsLayersFilled) } } @@ -58,11 +58,11 @@ class SidePanelTabsStyle : Stylesheet() { graphic = URI(IconsGrid) and(hover) { - backgroundColor += Paint.valueOf(Style.backgroundColor) + backgroundColor += Paint.valueOf(Style.BackgroundColor) focusColor = Color.TRANSPARENT } and(selected) { - backgroundColor += Paint.valueOf(Style.surfaceColor) + backgroundColor += Paint.valueOf(Style.SurfaceColor) graphic = URI(IconsGridSelected) } } diff --git a/src/main/kotlin/solve/styles/Style.kt b/src/main/kotlin/solve/styles/Style.kt index 323ee210d..49cb7bc26 100644 --- a/src/main/kotlin/solve/styles/Style.kt +++ b/src/main/kotlin/solve/styles/Style.kt @@ -4,61 +4,66 @@ import io.github.palexdev.materialfx.controls.MFXButton import javafx.scene.control.ToggleButton import javafx.scene.shape.Circle import tornadofx.* +import javafx.geometry.Insets object Style { - const val backgroundColor = "EFF0F0" + const val BackgroundColor = "EFF0F0" - const val surfaceColor = "FFFFFF" + const val SurfaceColor = "FFFFFF" - const val tooltipColor = "707070" + const val TooltipColor = "707070" - const val primaryColor = "78909C" + const val PrimaryColor = "78909C" - const val onBackgroundColor = "000000" + const val OnBackgroundColor = "000000" - const val primaryColorLight = "B0BEC5" + const val PrimaryColorLight = "B0BEC5" - const val secondaryColor = "41497F" + const val SecondaryColor = "41497F" - const val separatorLineColor = "9DAEB7" + const val SeparatorLineColor = "9DAEB7" - const val settingLightColor = "EDEEF1" + const val SettingLightColor = "EDEEF1" - const val errorColor = "EF6E6B" + const val ErrorColor = "EF6E6B" - const val activeColor = "41497F" + const val ActiveColor = "41497F" - const val fontCondensed = "'Roboto Condensed'" + const val FontCondensed = "'Roboto Condensed'" - const val font = "Roboto" + const val Font = "Roboto" - const val listFontColor = "3E4345" + const val ListFontColor = "3E4345" - const val headerFontColor = "1A1A1A" + const val HeaderFontColor = "1A1A1A" - const val fontWeightBold = "700" + val headerPadding = Insets(0.0, 0.0, 0.0, 24.0) - const val buttonFontSize = "14px" + const val FontWeightBold = "700" - const val mainFontSize = "15px" + const val ButtonFontSize = "14px" - const val headerFontSize = "20px" + const val MainFontSize = "15px" - val tooltipFontSize = Dimension(12.0, Dimension.LinearUnits.px) + val TooltipFontSize = Dimension(12.0, Dimension.LinearUnits.px) - const val buttonStyle = - "-fx-font-family: 'Roboto Condensed'; -fx-font-size: $buttonFontSize;" + + const val ButtonStyle = + "-fx-font-family: 'Roboto Condensed'; -fx-font-size: $ButtonFontSize;" + " -fx-font-weight: 700; -fx-text-fill: #78909C;" - const val navigationRailTabSize = 70.0 + const val NavigationRailTabSize = 70.0 const val FabRadius = 28.0 const val tabStyle = - "-fx-font-family: $font; -fx-font-weight:700; -fx-font-size: $buttonFontSize; " + - "-fx-text-fill: $primaryColorLight; -fx-background-radius: 36" + "-fx-font-family: $Font; -fx-font-weight:700; -fx-font-size: $ButtonFontSize; " + + "-fx-text-fill: $PrimaryColorLight; -fx-background-radius: 36" fun circleForRipple(button: MFXButton) = Circle(button.layoutX + 36.0, button.layoutY + 36.0, 35.0) fun circleForRipple(button: ToggleButton) = Circle(button.layoutX + 36.0, button.layoutY + 36.0, 35.0) + + const val HeaderFontSize = "20px" + + const val ControlButtonsSpacing = 10.0 } diff --git a/src/main/kotlin/solve/styles/TooltipStyle.kt b/src/main/kotlin/solve/styles/TooltipStyle.kt index 70b7db775..add65ec4f 100644 --- a/src/main/kotlin/solve/styles/TooltipStyle.kt +++ b/src/main/kotlin/solve/styles/TooltipStyle.kt @@ -6,10 +6,10 @@ import tornadofx.c class TooltipStyle : Stylesheet() { init { Companion.tooltip { - fontFamily = Style.fontCondensed - fontSize = Style.tooltipFontSize - backgroundColor += c(Style.surfaceColor) - textFill = c(Style.tooltipColor) + fontFamily = Style.FontCondensed + fontSize = Style.TooltipFontSize + backgroundColor += c(Style.SurfaceColor) + textFill = c(Style.TooltipColor) } } } diff --git a/src/main/kotlin/solve/styles/TreeTableViewStylesheet.kt b/src/main/kotlin/solve/styles/TreeTableViewStylesheet.kt index 9646b0723..4ed784c5a 100644 --- a/src/main/kotlin/solve/styles/TreeTableViewStylesheet.kt +++ b/src/main/kotlin/solve/styles/TreeTableViewStylesheet.kt @@ -13,9 +13,9 @@ import java.net.URI class TreeTableViewStylesheet : Stylesheet() { companion object { val fxTreeTableCellBorderColor by cssproperty>("-fx-table-cell-border-color") - val backgroundColour: Color = valueOf(Style.backgroundColor) - val surfaceColor: Color = valueOf(Style.surfaceColor) - val primaryColor: Color = valueOf(Style.primaryColor) + val backgroundColour: Color = valueOf(Style.BackgroundColor) + val surfaceColor: Color = valueOf(Style.SurfaceColor) + val primaryColor: Color = valueOf(Style.PrimaryColor) } init { diff --git a/src/main/kotlin/solve/utils/NodesUtils.kt b/src/main/kotlin/solve/utils/NodesUtils.kt index 78c6a3103..01abba0ed 100644 --- a/src/main/kotlin/solve/utils/NodesUtils.kt +++ b/src/main/kotlin/solve/utils/NodesUtils.kt @@ -5,7 +5,6 @@ import javafx.geometry.Bounds import javafx.scene.Node import javafx.scene.image.Image import javafx.scene.image.WritableImage -import solve.catalogue.floor import solve.utils.structures.DoublePoint import tornadofx.* @@ -39,8 +38,8 @@ fun Node.createSnapshot(): Image { val nodeSnapshot = snapshot(null, null) return WritableImage( nodeSnapshot.pixelReader, - nodeSnapshot.width.floor(), - nodeSnapshot.height.floor() + nodeSnapshot.width.floorToInt(), + nodeSnapshot.height.floorToInt() ) } diff --git a/src/main/kotlin/solve/utils/NumericUtlis.kt b/src/main/kotlin/solve/utils/NumericUtlis.kt index 4fbd70712..8e6667324 100644 --- a/src/main/kotlin/solve/utils/NumericUtlis.kt +++ b/src/main/kotlin/solve/utils/NumericUtlis.kt @@ -3,3 +3,6 @@ package solve.utils import kotlin.math.ceil fun Double.ceilToInt() = ceil(this).toInt() + +fun Double.floorToInt(): Int = kotlin.math.floor(this).toInt() + diff --git a/src/main/kotlin/solve/utils/materialfx/MFXIntegerTextField.kt b/src/main/kotlin/solve/utils/materialfx/MFXIntegerTextField.kt new file mode 100644 index 000000000..87bc1bad1 --- /dev/null +++ b/src/main/kotlin/solve/utils/materialfx/MFXIntegerTextField.kt @@ -0,0 +1,75 @@ +package solve.utils.materialfx + +import io.github.palexdev.materialfx.controls.MFXTextField +import io.github.palexdev.materialfx.validation.Constraint +import io.github.palexdev.materialfx.validation.Severity +import javafx.event.EventTarget +import javafx.scene.Node +import javafx.scene.control.Label +import javafx.scene.paint.Paint +import solve.styles.Style +import solve.utils.createPxValue +import solve.utils.materialfx.stylesheets.MFXValidationTextFieldStylesheet +import tornadofx.* + +class MFXIntegerTextField(private val invalidErrorMessage: String? = null) : MFXTextField() { + private lateinit var errorMessageLabel: Label + + val root: Node + get() = vbox { + add(this) + add(errorMessageLabel) + } + + init { + initializeIntegerTextField() + } + + private fun enableBorderColorCssString(hexColor: String) = + "${MFXValidationTextFieldStylesheet.mfxMain.name}: #$hexColor;\n" + + "-fx-border-color: #${hexColor};\n" + + private fun initializeIntegerTextField() { + addStylesheet(MFXValidationTextFieldStylesheet::class) + val areAllDigitsSymbols = textProperty().booleanBinding { text -> text?.all { it.isDigit() } ?: false } + validator.constraint(Constraint(Severity.ERROR, invalidErrorMessage, areAllDigitsSymbols)) + + val enableErrorBorderColorCss = enableBorderColorCssString(MFXValidationTextFieldStylesheet.ErrorBorderColor) + val enableDefaultBorderColorCss = + enableBorderColorCssString(MFXValidationTextFieldStylesheet.DefaultBorderColor) + textProperty().onChange { + updateInvalid(this, !isValid) + style += if (isValid) { + enableDefaultBorderColorCss + } else { + enableErrorBorderColorCss + } + } + + errorMessageLabel = label { + style { + textFill = Paint.valueOf(Style.ErrorColor) + fontFamily = Style.Font + fontSize = createPxValue(10.0) + } + visibleProperty().bind(!validator.validProperty()) + textProperty().onChange { + text = validationMessage ?: return@onChange + } + + paddingTop = 4.0 + } + } + + companion object { + fun EventTarget.mfxIntegerTextField( + notIntegerErrorMessage: String? = null, + op: MFXTextField.() -> Unit = {} + ) : MFXIntegerTextField { + val mfxIntegerTextField = MFXIntegerTextField(notIntegerErrorMessage) + mfxIntegerTextField.attachTo(this, op) + + return mfxIntegerTextField + } + } +} \ No newline at end of file diff --git a/src/main/kotlin/solve/utils/materialfx/MFXNodeFactoriesUtils.kt b/src/main/kotlin/solve/utils/materialfx/MFXNodeFactoriesUtils.kt new file mode 100644 index 000000000..db31ff486 --- /dev/null +++ b/src/main/kotlin/solve/utils/materialfx/MFXNodeFactoriesUtils.kt @@ -0,0 +1,32 @@ +package solve.utils.materialfx + +import io.github.palexdev.materialfx.controls.MFXButton +import javafx.event.EventTarget +import javafx.scene.control.Label +import javafx.scene.layout.Priority +import solve.styles.Style +import solve.styles.Style.ButtonStyle +import tornadofx.* + +const val ControlButtonWidth = 75.0 +const val ControlButtonHeight = 23.0 + +fun EventTarget.controlButton( + text: String, + width: Double = ControlButtonWidth, + height: Double = ControlButtonHeight, + op: MFXButton.() -> Unit = {} +) = mfxButton(text) { + prefWidth = width + prefHeight = height + style = ButtonStyle + + attachTo(this@controlButton, op) +} + +fun EventTarget.dialogHeaderLabel(text: String, op: Label.() -> Unit = {}) = label(text) { + hgrow = Priority.ALWAYS + style = "-fx-font-family: ${Style.FontCondensed}; -fx-font-size: ${Style.HeaderFontSize}" + + attachTo(this@dialogHeaderLabel, op) +} diff --git a/src/main/kotlin/solve/utils/materialfx/MFXUtils.kt b/src/main/kotlin/solve/utils/materialfx/MFXNodesUtils.kt similarity index 64% rename from src/main/kotlin/solve/utils/materialfx/MFXUtils.kt rename to src/main/kotlin/solve/utils/materialfx/MFXNodesUtils.kt index 2196580b4..4abebee45 100644 --- a/src/main/kotlin/solve/utils/materialfx/MFXUtils.kt +++ b/src/main/kotlin/solve/utils/materialfx/MFXNodesUtils.kt @@ -6,20 +6,14 @@ import io.github.palexdev.materialfx.controls.MFXCheckbox import io.github.palexdev.materialfx.controls.MFXContextMenu import io.github.palexdev.materialfx.controls.MFXContextMenuItem import io.github.palexdev.materialfx.controls.MFXTextField -import io.github.palexdev.materialfx.validation.Constraint -import io.github.palexdev.materialfx.validation.Severity import javafx.application.Platform import javafx.collections.ObservableList import javafx.event.EventTarget import javafx.scene.Node import javafx.scene.paint.Color -import javafx.scene.paint.Paint import javafx.scene.shape.Circle import org.controlsfx.control.RangeSlider -import solve.styles.Style -import solve.utils.createPxValue import solve.utils.materialfx.stylesheets.MFXRangeSliderStylesheet -import solve.utils.materialfx.stylesheets.MFXValidationTextFieldStylesheet import tornadofx.* import java.util.function.Supplier @@ -114,43 +108,3 @@ val MFXTextField.validationMessage: String? return constraints.first().message } - -fun EventTarget.mfxIntegerTextField( - notIntegerErrorMessage: String, - op: MFXTextField.() -> Unit = {} -) = vbox { - val mfxTextField = mfxTextField { - addStylesheet(MFXValidationTextFieldStylesheet::class) - val areAllDigitsSymbols = textProperty().booleanBinding { text -> text?.all { it.isDigit() } ?: false } - validator.constraint(Constraint(Severity.ERROR, notIntegerErrorMessage, areAllDigitsSymbols)) - - fun enableBorderColorCssString(hexColor: String) = - "${MFXValidationTextFieldStylesheet.mfxMain.name}: #$hexColor;\n" - - val enableErrorBorderColorCss = enableBorderColorCssString(MFXValidationTextFieldStylesheet.ErrorBorderColor) - val enableDefaultBorderColorCss = - enableBorderColorCssString(MFXValidationTextFieldStylesheet.DefaultBorderColor) - textProperty().onChange { - updateInvalid(this, !isValid) - style += if (isValid) { - enableDefaultBorderColorCss - } else { - enableErrorBorderColorCss - } - } - } - label { - style { - textFill = Paint.valueOf(Style.errorColor) - fontFamily = Style.font - fontSize = createPxValue(10.0) - } - visibleProperty().bind(!mfxTextField.validator.validProperty()) - mfxTextField.textProperty().onChange { - text = mfxTextField.validationMessage ?: return@onChange - } - - paddingTop = 4.0 - } - mfxTextField.op() -} diff --git a/src/main/kotlin/solve/utils/materialfx/stylesheets/MFXRangeSliderStylesheet.kt b/src/main/kotlin/solve/utils/materialfx/stylesheets/MFXRangeSliderStylesheet.kt index 20a5d3ff6..36929a199 100644 --- a/src/main/kotlin/solve/utils/materialfx/stylesheets/MFXRangeSliderStylesheet.kt +++ b/src/main/kotlin/solve/utils/materialfx/stylesheets/MFXRangeSliderStylesheet.kt @@ -18,17 +18,17 @@ class MFXRangeSliderStylesheet : Stylesheet() { } rangeBar { - backgroundColor += Paint.valueOf(Style.primaryColor) + backgroundColor += Paint.valueOf(Style.PrimaryColor) } track { - backgroundColor += Paint.valueOf(Style.settingLightColor) + backgroundColor += Paint.valueOf(Style.SettingLightColor) } } } private fun CssSelectionBlock.initThumbStyle() { borderColor += createCssBoxWithValue(Color.TRANSPARENT) - backgroundColor += Paint.valueOf(Style.primaryColor) + backgroundColor += Paint.valueOf(Style.PrimaryColor) scale(DefaultThumbScale) } diff --git a/src/main/kotlin/solve/utils/materialfx/stylesheets/MFXValidationTextFieldStylesheet.kt b/src/main/kotlin/solve/utils/materialfx/stylesheets/MFXValidationTextFieldStylesheet.kt index 858141a39..9dcee6888 100644 --- a/src/main/kotlin/solve/utils/materialfx/stylesheets/MFXValidationTextFieldStylesheet.kt +++ b/src/main/kotlin/solve/utils/materialfx/stylesheets/MFXValidationTextFieldStylesheet.kt @@ -11,9 +11,6 @@ class MFXValidationTextFieldStylesheet : Stylesheet() { textFill = Paint.valueOf(ErrorBorderColor) } - focusWithin { - } - mfxTextField { mfxMain.value += Paint.valueOf(DefaultBorderColor) borderColor += createCssBoxWithValue(Paint.valueOf(DefaultBorderColor)) @@ -21,13 +18,11 @@ class MFXValidationTextFieldStylesheet : Stylesheet() { } companion object { - const val ErrorBorderColor = Style.errorColor - const val DefaultBorderColor = Style.primaryColor + const val ErrorBorderColor = Style.ErrorColor + const val DefaultBorderColor = Style.PrimaryColor val mfxTextField by cssclass() val invalid by csspseudoclass() val mfxMain by cssproperty>("-mfx-main") - - val focusWithin by csspseudoclass("focus-within") } } From 5613fb462e5da8741876336196eb32f2b0d87aed Mon Sep 17 00:00:00 2001 From: Stanislav Mishchenko Date: Wed, 10 May 2023 12:58:21 +0300 Subject: [PATCH 07/36] Codestyle --- .../solve/filters/controller/FilterPanelController.kt | 5 +---- .../filters/settings/controller/FilterSettingsController.kt | 2 +- .../filters/settings/view/controls/FilterSettingControl.kt | 4 ++-- src/main/kotlin/solve/utils/NumericUtlis.kt | 1 - .../kotlin/solve/utils/materialfx/MFXIntegerTextField.kt | 6 +++--- 5 files changed, 7 insertions(+), 11 deletions(-) diff --git a/src/main/kotlin/solve/filters/controller/FilterPanelController.kt b/src/main/kotlin/solve/filters/controller/FilterPanelController.kt index eda03fb84..97ae03f32 100644 --- a/src/main/kotlin/solve/filters/controller/FilterPanelController.kt +++ b/src/main/kotlin/solve/filters/controller/FilterPanelController.kt @@ -5,14 +5,11 @@ import tornadofx.* class FilterPanelController : Controller() { fun addFilter(filter: Filter) { - } fun removeFilter(filter: Filter) { - } fun editFilter(oldFilter: Filter, editedFilter: Filter) { - } -} \ No newline at end of file +} diff --git a/src/main/kotlin/solve/filters/settings/controller/FilterSettingsController.kt b/src/main/kotlin/solve/filters/settings/controller/FilterSettingsController.kt index 4874ad4a9..36ceed3b1 100644 --- a/src/main/kotlin/solve/filters/settings/controller/FilterSettingsController.kt +++ b/src/main/kotlin/solve/filters/settings/controller/FilterSettingsController.kt @@ -17,4 +17,4 @@ class FilterSettingsController : Controller() { val editedFilter = Filter(editedFilterSettings) panelController.editFilter(oldFilter, editedFilter) } -} \ No newline at end of file +} diff --git a/src/main/kotlin/solve/filters/settings/view/controls/FilterSettingControl.kt b/src/main/kotlin/solve/filters/settings/view/controls/FilterSettingControl.kt index 6c350d43f..fe18cd5e9 100644 --- a/src/main/kotlin/solve/filters/settings/view/controls/FilterSettingControl.kt +++ b/src/main/kotlin/solve/filters/settings/view/controls/FilterSettingControl.kt @@ -3,6 +3,6 @@ package solve.filters.settings.view.controls import javafx.scene.Node import solve.filters.settings.model.FilterSetting -abstract class FilterSettingControl >(protected val controlNode: T) { - abstract fun extrudeFilterSettings() : FilterSetting<*>? +abstract class FilterSettingControl>(protected val controlNode: T) { + abstract fun extrudeFilterSettings(): FilterSetting<*>? } diff --git a/src/main/kotlin/solve/utils/NumericUtlis.kt b/src/main/kotlin/solve/utils/NumericUtlis.kt index 8e6667324..34f516b02 100644 --- a/src/main/kotlin/solve/utils/NumericUtlis.kt +++ b/src/main/kotlin/solve/utils/NumericUtlis.kt @@ -5,4 +5,3 @@ import kotlin.math.ceil fun Double.ceilToInt() = ceil(this).toInt() fun Double.floorToInt(): Int = kotlin.math.floor(this).toInt() - diff --git a/src/main/kotlin/solve/utils/materialfx/MFXIntegerTextField.kt b/src/main/kotlin/solve/utils/materialfx/MFXIntegerTextField.kt index 87bc1bad1..6db5e41a4 100644 --- a/src/main/kotlin/solve/utils/materialfx/MFXIntegerTextField.kt +++ b/src/main/kotlin/solve/utils/materialfx/MFXIntegerTextField.kt @@ -27,7 +27,7 @@ class MFXIntegerTextField(private val invalidErrorMessage: String? = null) : MFX private fun enableBorderColorCssString(hexColor: String) = "${MFXValidationTextFieldStylesheet.mfxMain.name}: #$hexColor;\n" + - "-fx-border-color: #${hexColor};\n" + "-fx-border-color: #$hexColor;\n" private fun initializeIntegerTextField() { addStylesheet(MFXValidationTextFieldStylesheet::class) @@ -65,11 +65,11 @@ class MFXIntegerTextField(private val invalidErrorMessage: String? = null) : MFX fun EventTarget.mfxIntegerTextField( notIntegerErrorMessage: String? = null, op: MFXTextField.() -> Unit = {} - ) : MFXIntegerTextField { + ): MFXIntegerTextField { val mfxIntegerTextField = MFXIntegerTextField(notIntegerErrorMessage) mfxIntegerTextField.attachTo(this, op) return mfxIntegerTextField } } -} \ No newline at end of file +} From d40b97baee651e8847a1cbf672f344cffae3e59c Mon Sep 17 00:00:00 2001 From: Stanislav Mishchenko Date: Wed, 10 May 2023 14:53:05 +0300 Subject: [PATCH 08/36] Codestyle --- src/main/kotlin/solve/SolveApp.kt | 6 +++--- src/main/kotlin/solve/styles/Style.kt | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/main/kotlin/solve/SolveApp.kt b/src/main/kotlin/solve/SolveApp.kt index c61005721..bf2de486f 100644 --- a/src/main/kotlin/solve/SolveApp.kt +++ b/src/main/kotlin/solve/SolveApp.kt @@ -5,12 +5,12 @@ import javafx.stage.Stage import solve.main.MainView import solve.scene.view.landmarks.AnimationProvider import solve.scene.view.landmarks.JavaFXAnimationProvider -import solve.utils.ServiceLocator -import tornadofx.App -import tornadofx.launch 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, ApplicationStylesheet::class) { override fun start(stage: Stage) { diff --git a/src/main/kotlin/solve/styles/Style.kt b/src/main/kotlin/solve/styles/Style.kt index 49cb7bc26..5398663df 100644 --- a/src/main/kotlin/solve/styles/Style.kt +++ b/src/main/kotlin/solve/styles/Style.kt @@ -1,10 +1,10 @@ package solve.styles import io.github.palexdev.materialfx.controls.MFXButton +import javafx.geometry.Insets import javafx.scene.control.ToggleButton import javafx.scene.shape.Circle import tornadofx.* -import javafx.geometry.Insets object Style { const val BackgroundColor = "EFF0F0" From 5b78f4436bbdb136b8b2f16bc0e4a5960c3f4a63 Mon Sep 17 00:00:00 2001 From: Stanislav Mishchenko Date: Thu, 11 May 2023 22:30:16 +0300 Subject: [PATCH 09/36] Fix validation bugs --- .../controller/FilterPanelController.kt | 6 + src/main/kotlin/solve/filters/model/Filter.kt | 3 +- .../solve/filters/model/FilterPanelModel.kt | 27 +++ .../controller/FilterSettingsController.kt | 2 +- .../model/IndicesStepFilterSetting.kt | 5 +- .../settings/model/TimePeriodFilterSetting.kt | 5 +- .../settings/model/UIDFilterSetting.kt | 5 +- .../settings/view/FilterSettingsView.kt | 219 +++++++++++++----- .../view/controls/FilterSettingControl.kt | 8 - .../controls/FilterSettingNodeConnector.kt | 48 ++++ .../controls/IndicesStepSettingControl.kt | 18 -- .../IndicesStepSettingNodeConnector.kt | 30 +++ .../view/controls/TimePeriodSettingControl.kt | 18 -- .../TimePeriodSettingNodeConnector.kt | 31 +++ .../view/controls/UIDSettingControl.kt | 18 -- .../view/controls/UIDSettingNodeConnector.kt | 27 +++ .../filters/view/FilterPanelFieldsView.kt | 35 ++- .../solve/filters/view/FilterPanelView.kt | 2 +- .../solve/project/model/ProjectFrames.kt | 5 + .../kotlin/solve/utils/StructuresUtils.kt | 3 + .../utils/materialfx/MFXIntegerTextField.kt | 10 +- 21 files changed, 386 insertions(+), 139 deletions(-) create mode 100644 src/main/kotlin/solve/filters/model/FilterPanelModel.kt delete mode 100644 src/main/kotlin/solve/filters/settings/view/controls/FilterSettingControl.kt create mode 100644 src/main/kotlin/solve/filters/settings/view/controls/FilterSettingNodeConnector.kt delete mode 100644 src/main/kotlin/solve/filters/settings/view/controls/IndicesStepSettingControl.kt create mode 100644 src/main/kotlin/solve/filters/settings/view/controls/IndicesStepSettingNodeConnector.kt delete mode 100644 src/main/kotlin/solve/filters/settings/view/controls/TimePeriodSettingControl.kt create mode 100644 src/main/kotlin/solve/filters/settings/view/controls/TimePeriodSettingNodeConnector.kt delete mode 100644 src/main/kotlin/solve/filters/settings/view/controls/UIDSettingControl.kt create mode 100644 src/main/kotlin/solve/filters/settings/view/controls/UIDSettingNodeConnector.kt create mode 100644 src/main/kotlin/solve/project/model/ProjectFrames.kt create mode 100644 src/main/kotlin/solve/utils/StructuresUtils.kt diff --git a/src/main/kotlin/solve/filters/controller/FilterPanelController.kt b/src/main/kotlin/solve/filters/controller/FilterPanelController.kt index 97ae03f32..305cca4cc 100644 --- a/src/main/kotlin/solve/filters/controller/FilterPanelController.kt +++ b/src/main/kotlin/solve/filters/controller/FilterPanelController.kt @@ -1,15 +1,21 @@ package solve.filters.controller import solve.filters.model.Filter +import solve.filters.model.FilterPanelModel import tornadofx.* class FilterPanelController : Controller() { + val model = FilterPanelModel() + fun addFilter(filter: Filter) { + model.addFilter(filter) } fun removeFilter(filter: Filter) { + model.removeFilter(filter) } fun editFilter(oldFilter: Filter, editedFilter: Filter) { + model.replaceFilter(oldFilter, editedFilter) } } diff --git a/src/main/kotlin/solve/filters/model/Filter.kt b/src/main/kotlin/solve/filters/model/Filter.kt index b26e4e654..138b8f4e4 100644 --- a/src/main/kotlin/solve/filters/model/Filter.kt +++ b/src/main/kotlin/solve/filters/model/Filter.kt @@ -3,7 +3,8 @@ package solve.filters.model import solve.filters.settings.model.FilterSetting import solve.project.model.ProjectFrame -data class Filter(val settings: List>) { +data class Filter(val settings: List>) { + val preview: String by lazy { "filter" } fun apply(frames: List): List { var suitableFrames = frames settings.forEach { suitableFrames = it.apply(suitableFrames) } diff --git a/src/main/kotlin/solve/filters/model/FilterPanelModel.kt b/src/main/kotlin/solve/filters/model/FilterPanelModel.kt new file mode 100644 index 000000000..a430fc952 --- /dev/null +++ b/src/main/kotlin/solve/filters/model/FilterPanelModel.kt @@ -0,0 +1,27 @@ +package solve.filters.model + +import javafx.collections.FXCollections +import javafx.collections.ObservableList + +class FilterPanelModel { + private val _filters = FXCollections.observableArrayList() + val filters: ObservableList = FXCollections.unmodifiableObservableList(_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) + } +} \ No newline at end of file diff --git a/src/main/kotlin/solve/filters/settings/controller/FilterSettingsController.kt b/src/main/kotlin/solve/filters/settings/controller/FilterSettingsController.kt index 36ceed3b1..37923f5c4 100644 --- a/src/main/kotlin/solve/filters/settings/controller/FilterSettingsController.kt +++ b/src/main/kotlin/solve/filters/settings/controller/FilterSettingsController.kt @@ -8,7 +8,7 @@ import tornadofx.* class FilterSettingsController : Controller() { val panelController: FilterPanelController by inject() - fun createFilter(filterSettings: List>) { + fun createFilter(filterSettings: List>) { val filter = Filter(filterSettings) panelController.addFilter(filter) } diff --git a/src/main/kotlin/solve/filters/settings/model/IndicesStepFilterSetting.kt b/src/main/kotlin/solve/filters/settings/model/IndicesStepFilterSetting.kt index a963822db..bf6dc23a0 100644 --- a/src/main/kotlin/solve/filters/settings/model/IndicesStepFilterSetting.kt +++ b/src/main/kotlin/solve/filters/settings/model/IndicesStepFilterSetting.kt @@ -2,7 +2,10 @@ package solve.filters.settings.model import solve.project.model.ProjectFrame -class IndicesStepFilterSetting(private var step: Int) : FilterSetting { +class IndicesStepFilterSetting(step: Int) : FilterSetting { + var step: Int = step + private set + override fun apply(fields: List) = fields.slice(0..fields.lastIndex step this.step) override fun edit(newValue: Int) { diff --git a/src/main/kotlin/solve/filters/settings/model/TimePeriodFilterSetting.kt b/src/main/kotlin/solve/filters/settings/model/TimePeriodFilterSetting.kt index 04e6b5cd3..b560b334b 100644 --- a/src/main/kotlin/solve/filters/settings/model/TimePeriodFilterSetting.kt +++ b/src/main/kotlin/solve/filters/settings/model/TimePeriodFilterSetting.kt @@ -3,7 +3,10 @@ package solve.filters.settings.model import solve.project.model.ProjectFrame import solve.utils.structures.IntPoint -class TimePeriodFilterSetting(private var timePeriod: IntPoint) : FilterSetting { +class TimePeriodFilterSetting(timePeriod: IntPoint) : FilterSetting { + var timePeriod: IntPoint = timePeriod + private set + override fun apply(fields: List): List = fields.filter { it.timestamp in timePeriod.x..timePeriod.y } diff --git a/src/main/kotlin/solve/filters/settings/model/UIDFilterSetting.kt b/src/main/kotlin/solve/filters/settings/model/UIDFilterSetting.kt index cde70fc9c..99e09cf42 100644 --- a/src/main/kotlin/solve/filters/settings/model/UIDFilterSetting.kt +++ b/src/main/kotlin/solve/filters/settings/model/UIDFilterSetting.kt @@ -2,7 +2,10 @@ package solve.filters.settings.model import solve.project.model.ProjectFrame -class UIDFilterSetting(private var uid: Long) : FilterSetting { +class UIDFilterSetting(uid: Long) : FilterSetting { + var uid: Long = uid + private set + override fun apply(fields: List) = fields.filter { it.uids.contains(uid) } override fun edit(newValue: Long) { diff --git a/src/main/kotlin/solve/filters/settings/view/FilterSettingsView.kt b/src/main/kotlin/solve/filters/settings/view/FilterSettingsView.kt index 6b5e1832e..cbb666ea7 100644 --- a/src/main/kotlin/solve/filters/settings/view/FilterSettingsView.kt +++ b/src/main/kotlin/solve/filters/settings/view/FilterSettingsView.kt @@ -1,24 +1,29 @@ package solve.filters.settings.view import io.github.palexdev.materialfx.enums.FloatMode +import javafx.beans.InvalidationListener import javafx.event.EventTarget import javafx.geometry.Insets import javafx.geometry.Pos import javafx.scene.Node import javafx.scene.control.CheckBox +import javafx.scene.control.Label +import javafx.scene.control.TextField import javafx.scene.layout.BorderPane import javafx.scene.layout.HBox import javafx.scene.layout.Pane import javafx.scene.text.Font +import solve.filters.model.Filter import solve.filters.settings.controller.FilterSettingsController import solve.filters.settings.model.FilterSetting -import solve.filters.settings.view.controls.FilterSettingControl -import solve.filters.settings.view.controls.IndicesStepSettingControl -import solve.filters.settings.view.controls.TimePeriodSettingControl -import solve.filters.settings.view.controls.UIDSettingControl +import solve.filters.settings.view.controls.FilterSettingNodeConnector +import solve.filters.settings.view.controls.IndicesStepSettingNodeConnector +import solve.filters.settings.view.controls.TimePeriodSettingNodeConnector +import solve.filters.settings.view.controls.UIDSettingNodeConnector import solve.project.model.ProjectFrame import solve.styles.Style import solve.styles.Style.headerPadding +import solve.utils.getKeys import solve.utils.materialfx.MFXIntegerTextField import solve.utils.materialfx.MFXIntegerTextField.Companion.mfxIntegerTextField import solve.utils.materialfx.MaterialFXDialog @@ -34,19 +39,25 @@ class FilterSettingsView : View() { private lateinit var stepNumberIntegerTextField: MFXIntegerTextField private lateinit var uidIntegerTextField: MFXIntegerTextField - private lateinit var timePeriodCheckbox: CheckBox - private lateinit var stepNumberCheckBox: CheckBox - private lateinit var uidIntegerCheckBox: CheckBox - private val checkboxes: List by lazy { - listOf(timePeriodCheckbox, stepNumberCheckBox, uidIntegerCheckBox) - } - private val checkboxToSettingNodeMap = mutableMapOf() - private val settingControlsMap = mutableMapOf>() + private val nodeToNodeConnectorMap = + mutableMapOf>>() + + private val validTextFields = observableSetOf() + private val enabledTextFields = observableSetOf() + private val notEmptyTextFields = observableSetOf() - private val areAllIntegerTextFieldsValid: Boolean - get() = stepNumberIntegerTextField.isValid && uidIntegerTextField.isValid - private val selectedSettingCheckboxes = observableListOf() + private val settingCheckboxes = mutableListOf() + private val selectedSettingCheckboxes = observableSetOf() + + private val areAllEnabledTextFieldsValid: Boolean + get() = validTextFields.toSet() == enabledTextFields.toSet().also { println(123) } + private val haveSelectedCheckboxes: Boolean + get() = selectedSettingCheckboxes.isNotEmpty() + private val areAllEnabledTextFieldsNotEmpty: Boolean + get() = validTextFields.toSet() == notEmptyTextFields.toSet() + private val canCreateFilter: Boolean + get() = haveSelectedCheckboxes && areAllEnabledTextFieldsValid && areAllEnabledTextFieldsNotEmpty private val filterSettingsContentNode = borderpane { top = vbox { @@ -54,9 +65,9 @@ class FilterSettingsView : View() { padding = headerPadding } vbox(10) { - timePeriodSettingField() - indicesStepSettingField() - uidSettingField() + add(buildTimePeriodSettingNode()) + add(buildIndicesStepSettingNode()) + add(buildUIDSettingField()) paddingTop = 10.0 paddingLeft = 30.0 @@ -70,15 +81,22 @@ class FilterSettingsView : View() { this@FilterSettingsView.close() } } - controlButton("CREATE") { - isDisable = selectedSettingCheckboxes.isEmpty() - selectedSettingCheckboxes.onChange { - isDisable = selectedSettingCheckboxes.isEmpty() + val createButton = controlButton("CREATE") { + fun updateDisableProperty() { + isDisable = !canCreateFilter } + updateDisableProperty() + val updateDisablePropertyChangeListener = InvalidationListener { updateDisableProperty() } + validTextFields.addListener(updateDisablePropertyChangeListener) + enabledTextFields.addListener(updateDisablePropertyChangeListener) + selectedSettingCheckboxes.addListener(updateDisablePropertyChangeListener) + notEmptyTextFields.addListener(updateDisablePropertyChangeListener) + action { val filterSettings = getFilterSettings() - println(filterSettings) + controller.createFilter(filterSettings) + this@FilterSettingsView.close() } } @@ -88,7 +106,9 @@ class FilterSettingsView : View() { } override val root = filterSettingsContentNode - fun showDialog(parent: View) { + fun showCreationDialog(parent: View) { + setDialogInitialState() + val content = MaterialFXDialog.createGenericDialog(root) val dialog = MaterialFXDialog.createStageDialog(content, parent.currentStage, parent.root as Pane) dialog.isDraggable = false @@ -97,7 +117,39 @@ class FilterSettingsView : View() { dialog.centerOnScreen() } - private fun EventTarget.integerTextField( + fun showEditingDialog(parent: View, oldFilter: Filter) { + + } + + private fun setDialogInitialState() { + settingCheckboxes.forEach { it.isSelected = false } + nodeToNodeConnectorMap.forEach { (node, connector) -> + connector.setDefaultSettingNodeState(node) + } + } + + private fun getNodeByFilterSetting(setting: FilterSetting): Node { + val correspondingSettingControl = nodeToNodeConnectorMap.values.first { + it.filterSettingKCLass == setting::class + } + + return nodeToNodeConnectorMap.getKeys(correspondingSettingControl).first() + } + + private fun setControlNodesSettingsFromFilter(filter: Filter) { + filter.settings.forEach { setting -> + val correspondingNode = getNodeByFilterSetting(setting) + val correspondingControl = nodeToNodeConnectorMap[correspondingNode] + + correspondingControl?.updateSettingNodeWithSettings(correspondingNode, setting) + } + } + + private fun Node.setEnabledByCheckboxSelection(checkBox: CheckBox) { + enableWhen(checkBox.selectedProperty()) + } + + private fun buildIntegerTextField( notIntegerErrorMessage: String, width: Double, maxWidth: Double @@ -112,10 +164,47 @@ class FilterSettingsView : View() { textLimit = IntegerTextFieldSymbolsLimit floatMode = FloatMode.ABOVE - attachTo(this@FilterSettingsView) + disableProperty().onChange { isDisable -> + if (isDisable) { + text = "" + isAllowEdit = false + enabledTextFields.remove(this) + } else { + isAllowEdit = true + enabledTextFields.add(this) + } + } + textProperty().onChange { + if (text.isEmpty()) { + notEmptyTextFields.remove(this) + } else { + notEmptyTextFields.add(this) + } + + if (!isValid) { + validTextFields.remove(this) + } else { + validTextFields.add(this) + } + } } - private fun EventTarget.filterSettingField(name: String, settingNode: Node, op: HBox.() -> Unit = {}) = hbox(10) { + private fun EventTarget.settingLabel( + text: String, + settingCheckBox: CheckBox, + op: Label.() -> Unit = {} + ) = label(text) { + font = Font.font(Style.Font, FilterSettingNameFontSize) + setEnabledByCheckboxSelection(settingCheckBox) + + attachTo(this@settingLabel, op) + } + + private fun createSettingField( + name: String, + settingNode: Node, + activeNode: Node = settingNode + ): SettingFieldData { val checkBox = mfxCheckbox { paddingTop = -7.0 @@ -127,64 +216,64 @@ class FilterSettingsView : View() { } } } - checkboxToSettingNodeMap[checkBox] = settingNode + settingCheckboxes.add(checkBox) - hbox { - add(checkBox) - label(name) { - font = Font.font(Style.Font, FilterSettingNameFontSize) - } + activeNode.setEnabledByCheckboxSelection(checkBox) + val settingFieldNode = hbox(10) { + checkboxToSettingNodeMap[checkBox] = settingNode + hbox { + add(checkBox) + settingLabel(name, checkBox) - paddingTop = FilterSettingNonTextFieldPaddingTop + paddingTop = FilterSettingNonTextFieldPaddingTop + } + add(settingNode) } - add(settingNode) - attachTo(this@filterSettingField, op) + + return SettingFieldData(settingFieldNode, checkBox) } - private fun EventTarget.timePeriodSettingField(): HBox { + private fun buildTimePeriodSettingNode(): HBox { val timePeriodRangeSlider = mfxRangeSlider(0.0, 10.0, 1.0, 9.0) { prefWidth = TimeLimitRangeSliderWidth paddingTop = FilterSettingNonTextFieldPaddingTop + 2.0 } - settingControlsMap[timePeriodRangeSlider] = TimePeriodSettingControl(timePeriodRangeSlider) + nodeToNodeConnectorMap[timePeriodRangeSlider] = TimePeriodSettingNodeConnector - return filterSettingField( - "Time period", - timePeriodRangeSlider - ) + return createSettingField("Time period:", timePeriodRangeSlider).node } - private fun EventTarget.indicesStepSettingField(): HBox { - stepNumberIntegerTextField = integerTextField("Step must be an integer number", 60.0, 145.0) - settingControlsMap[stepNumberIntegerTextField] = IndicesStepSettingControl(stepNumberIntegerTextField) - - return filterSettingField( - "Show every", - stepNumberIntegerTextField - ) { - label("image") { - font = Font.font(Style.Font, FilterSettingNameFontSize) + private fun buildIndicesStepSettingNode(): HBox { + stepNumberIntegerTextField = buildIntegerTextField("Step must be an integer number", 60.0, 145.0) + nodeToNodeConnectorMap[stepNumberIntegerTextField] = IndicesStepSettingNodeConnector + val fieldData = createSettingField("Show every", stepNumberIntegerTextField.root, stepNumberIntegerTextField) + fieldData.node.add( + settingLabel("image", fieldData.checkBox) { paddingTop = FilterSettingNonTextFieldPaddingTop } - } + ) + + return fieldData.node } - private fun EventTarget.uidSettingField(): HBox { - uidIntegerTextField = integerTextField("UID must be an integer number", 155.0, 155.0) - settingControlsMap[uidIntegerTextField] = UIDSettingControl(uidIntegerTextField) + private fun buildUIDSettingField(): HBox { + uidIntegerTextField = buildIntegerTextField("UID must be an integer number", 155.0, 155.0) + nodeToNodeConnectorMap[uidIntegerTextField] = UIDSettingNodeConnector - return filterSettingField( - "Show images with landmark:", - uidIntegerTextField - ) + return createSettingField("Show images with landmark:", uidIntegerTextField.root, uidIntegerTextField).node } - private fun getFilterSettings(): List> { - val enabledCheckboxesSettingNodes = selectedSettingCheckboxes.map { checkboxToSettingNodeMap[it] } - val enabledSettingsControls = enabledCheckboxesSettingNodes.map { settingControlsMap[it] } + private fun getFilterSettings(): List> { + val enabledSettingNodes = selectedSettingCheckboxes.map { checkboxToSettingNodeMap[it] } + + return enabledSettingNodes.mapNotNull { settingNode -> + settingNode ?: return@mapNotNull null + + val correspondingControl = nodeToNodeConnectorMap[settingNode] - return enabledSettingsControls.mapNotNull { it?.extrudeFilterSettings() } + return@mapNotNull correspondingControl?.extractFilterSettings(settingNode) + } } private fun getFramesMinTimestamp(frames: List) = frames.minOf { it.timestamp } @@ -199,3 +288,5 @@ class FilterSettingsView : View() { private const val TimeLimitRangeSliderWidth = 300.0 } } + +private data class SettingFieldData(val node: HBox, val checkBox: CheckBox) diff --git a/src/main/kotlin/solve/filters/settings/view/controls/FilterSettingControl.kt b/src/main/kotlin/solve/filters/settings/view/controls/FilterSettingControl.kt deleted file mode 100644 index fe18cd5e9..000000000 --- a/src/main/kotlin/solve/filters/settings/view/controls/FilterSettingControl.kt +++ /dev/null @@ -1,8 +0,0 @@ -package solve.filters.settings.view.controls - -import javafx.scene.Node -import solve.filters.settings.model.FilterSetting - -abstract class FilterSettingControl>(protected val controlNode: T) { - abstract fun extrudeFilterSettings(): FilterSetting<*>? -} diff --git a/src/main/kotlin/solve/filters/settings/view/controls/FilterSettingNodeConnector.kt b/src/main/kotlin/solve/filters/settings/view/controls/FilterSettingNodeConnector.kt new file mode 100644 index 000000000..dba1f39e7 --- /dev/null +++ b/src/main/kotlin/solve/filters/settings/view/controls/FilterSettingNodeConnector.kt @@ -0,0 +1,48 @@ +package solve.filters.settings.view.controls + +import javafx.scene.Node +import solve.filters.settings.model.FilterSetting +import kotlin.reflect.KClass +import kotlin.reflect.cast + +abstract class FilterSettingNodeConnector > ( + private val nodeKClass : KClass, + val filterSettingKCLass : KClass +) { + private fun isExpectedNodeType(settingNode: Node) = nodeKClass.isInstance(settingNode) + + private fun castNodeToExpectedType(settingNode: Node) = nodeKClass.cast(settingNode) + + fun extractFilterSettings(settingNode: Node): K? { + if (!isExpectedNodeType(settingNode)) { + return null + } + + return extractFilterSettingsFromTypedSettingNode(castNodeToExpectedType(settingNode)) + } + + protected abstract fun extractFilterSettingsFromTypedSettingNode(settingNode: T): K? + + fun updateSettingNodeWithSettings(settingNode: Node, setting: FilterSetting) { + if (!isExpectedNodeType(settingNode) || !filterSettingKCLass.isInstance(setting)) { + return + } + + return updateTypedSettingNodeWithSettings( + castNodeToExpectedType(settingNode), + filterSettingKCLass.cast(setting) + ) + } + + protected abstract fun updateTypedSettingNodeWithSettings(settingNode: T, setting: K) + + fun setDefaultSettingNodeState(settingNode: Node) { + if (!isExpectedNodeType(settingNode)) { + return + } + + return setDefaultTypedSettingNodeState(castNodeToExpectedType(settingNode)) + } + + protected abstract fun setDefaultTypedSettingNodeState(settingNode: T) +} diff --git a/src/main/kotlin/solve/filters/settings/view/controls/IndicesStepSettingControl.kt b/src/main/kotlin/solve/filters/settings/view/controls/IndicesStepSettingControl.kt deleted file mode 100644 index 12a298a8d..000000000 --- a/src/main/kotlin/solve/filters/settings/view/controls/IndicesStepSettingControl.kt +++ /dev/null @@ -1,18 +0,0 @@ -package solve.filters.settings.view.controls - -import solve.filters.settings.model.IndicesStepFilterSetting -import solve.utils.materialfx.MFXIntegerTextField - -class IndicesStepSettingControl( - controlNode: MFXIntegerTextField -) : FilterSettingControl(controlNode) { - override fun extrudeFilterSettings(): IndicesStepFilterSetting? { - if (!controlNode.isValid || controlNode.text.isEmpty()) { - return null - } - - val stepNumber = controlNode.text.toInt() - - return IndicesStepFilterSetting(stepNumber) - } -} diff --git a/src/main/kotlin/solve/filters/settings/view/controls/IndicesStepSettingNodeConnector.kt b/src/main/kotlin/solve/filters/settings/view/controls/IndicesStepSettingNodeConnector.kt new file mode 100644 index 000000000..803466406 --- /dev/null +++ b/src/main/kotlin/solve/filters/settings/view/controls/IndicesStepSettingNodeConnector.kt @@ -0,0 +1,30 @@ +package solve.filters.settings.view.controls + +import solve.filters.settings.model.IndicesStepFilterSetting +import solve.utils.materialfx.MFXIntegerTextField + +object IndicesStepSettingNodeConnector : FilterSettingNodeConnector( + MFXIntegerTextField::class, + IndicesStepFilterSetting::class +) { + override fun extractFilterSettingsFromTypedSettingNode(settingNode: MFXIntegerTextField): IndicesStepFilterSetting? { + if (!settingNode.isValid || settingNode.text.isEmpty()) { + return null + } + + val stepNumber = settingNode.text.toInt() + + return IndicesStepFilterSetting(stepNumber) + } + + override fun updateTypedSettingNodeWithSettings( + settingNode: MFXIntegerTextField, + setting: IndicesStepFilterSetting + ) { + settingNode.text = setting.step.toString() + } + + override fun setDefaultTypedSettingNodeState(settingNode: MFXIntegerTextField) { + settingNode.text = "" + } +} diff --git a/src/main/kotlin/solve/filters/settings/view/controls/TimePeriodSettingControl.kt b/src/main/kotlin/solve/filters/settings/view/controls/TimePeriodSettingControl.kt deleted file mode 100644 index 508c5c037..000000000 --- a/src/main/kotlin/solve/filters/settings/view/controls/TimePeriodSettingControl.kt +++ /dev/null @@ -1,18 +0,0 @@ -package solve.filters.settings.view.controls - -import org.controlsfx.control.RangeSlider -import solve.filters.settings.model.TimePeriodFilterSetting -import solve.utils.ceilToInt -import solve.utils.floorToInt -import solve.utils.structures.IntPoint - -class TimePeriodSettingControl( - controlNode: RangeSlider -) : FilterSettingControl(controlNode) { - override fun extrudeFilterSettings(): TimePeriodFilterSetting { - val fromTimePeriod = controlNode.lowValue.floorToInt() - val toTimePeriod = controlNode.highValue.ceilToInt() - - return TimePeriodFilterSetting(IntPoint(fromTimePeriod, toTimePeriod)) - } -} diff --git a/src/main/kotlin/solve/filters/settings/view/controls/TimePeriodSettingNodeConnector.kt b/src/main/kotlin/solve/filters/settings/view/controls/TimePeriodSettingNodeConnector.kt new file mode 100644 index 000000000..9abbe7e9f --- /dev/null +++ b/src/main/kotlin/solve/filters/settings/view/controls/TimePeriodSettingNodeConnector.kt @@ -0,0 +1,31 @@ +package solve.filters.settings.view.controls + +import org.controlsfx.control.RangeSlider +import solve.filters.settings.model.TimePeriodFilterSetting +import solve.utils.ceilToInt +import solve.utils.floorToInt +import solve.utils.structures.IntPoint + +object TimePeriodSettingNodeConnector : FilterSettingNodeConnector( + RangeSlider::class, + TimePeriodFilterSetting::class +) { + override fun extractFilterSettingsFromTypedSettingNode(settingNode: RangeSlider): TimePeriodFilterSetting? { + val fromTimePeriod = settingNode.lowValue.floorToInt() + val toTimePeriod = settingNode.highValue.ceilToInt() + + return TimePeriodFilterSetting(IntPoint(fromTimePeriod, toTimePeriod)) + } + + override fun updateTypedSettingNodeWithSettings(settingNode: RangeSlider, setting: TimePeriodFilterSetting) { + settingNode.lowValue = setting.timePeriod.x.toDouble() + settingNode.highValue = setting.timePeriod.y.toDouble() + } + + override fun setDefaultTypedSettingNodeState(settingNode: RangeSlider) { + settingNode.min = 0.0 + settingNode.max = 1.0 + settingNode.lowValue = settingNode.min + settingNode.highValue = settingNode.max + } +} diff --git a/src/main/kotlin/solve/filters/settings/view/controls/UIDSettingControl.kt b/src/main/kotlin/solve/filters/settings/view/controls/UIDSettingControl.kt deleted file mode 100644 index a07b534ee..000000000 --- a/src/main/kotlin/solve/filters/settings/view/controls/UIDSettingControl.kt +++ /dev/null @@ -1,18 +0,0 @@ -package solve.filters.settings.view.controls - -import solve.filters.settings.model.UIDFilterSetting -import solve.utils.materialfx.MFXIntegerTextField - -class UIDSettingControl( - controlNode: MFXIntegerTextField -) : FilterSettingControl(controlNode) { - override fun extrudeFilterSettings(): UIDFilterSetting? { - if (!controlNode.isValid || controlNode.text.isEmpty()) { - return null - } - - val uid = controlNode.text.toLong() - - return UIDFilterSetting(uid) - } -} diff --git a/src/main/kotlin/solve/filters/settings/view/controls/UIDSettingNodeConnector.kt b/src/main/kotlin/solve/filters/settings/view/controls/UIDSettingNodeConnector.kt new file mode 100644 index 000000000..7b517cc21 --- /dev/null +++ b/src/main/kotlin/solve/filters/settings/view/controls/UIDSettingNodeConnector.kt @@ -0,0 +1,27 @@ +package solve.filters.settings.view.controls + +import solve.filters.settings.model.UIDFilterSetting +import solve.utils.materialfx.MFXIntegerTextField + +object UIDSettingNodeConnector : FilterSettingNodeConnector( + MFXIntegerTextField::class, + UIDFilterSetting::class +) { + override fun extractFilterSettingsFromTypedSettingNode(settingNode: MFXIntegerTextField): UIDFilterSetting? { + if (!settingNode.isValid || settingNode.text.isEmpty()) { + return null + } + + val uid = settingNode.text.toLong() + + return UIDFilterSetting(uid) + } + + override fun updateTypedSettingNodeWithSettings(settingNode: MFXIntegerTextField, setting: UIDFilterSetting) { + settingNode.text = setting.uid.toString() + } + + override fun setDefaultTypedSettingNodeState(settingNode: MFXIntegerTextField) { + settingNode.text = "" + } +} diff --git a/src/main/kotlin/solve/filters/view/FilterPanelFieldsView.kt b/src/main/kotlin/solve/filters/view/FilterPanelFieldsView.kt index 3fa8d09b0..20b4d51e6 100644 --- a/src/main/kotlin/solve/filters/view/FilterPanelFieldsView.kt +++ b/src/main/kotlin/solve/filters/view/FilterPanelFieldsView.kt @@ -2,10 +2,17 @@ package solve.filters.view import io.github.palexdev.materialfx.controls.cell.MFXCheckListCell import io.github.palexdev.materialfx.effects.DepthLevel +import io.github.palexdev.mfxcore.utils.converters.FunctionalStringConverter +import javafx.application.Platform import javafx.beans.binding.Bindings +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.delay +import kotlinx.coroutines.javafx.JavaFx +import kotlinx.coroutines.launch import solve.constants.IconsDeletePath import solve.constants.IconsEditPath -import solve.filters.settings.model.FilterSetting +import solve.filters.controller.FilterPanelController import solve.styles.FilterPanelFieldsViewStylesheet import solve.utils.createHGrowHBox import solve.utils.imageViewIcon @@ -15,12 +22,16 @@ import tornadofx.* import java.util.function.Function class FilterPanelFieldsView : View() { + private val filterPanelController: FilterPanelController by inject() + private val editIconImage = loadResourcesImage(IconsEditPath) private val deleteIconImage = loadResourcesImage(IconsDeletePath) - val filtersListView = mfxCheckListView> { + val filtersListView = mfxCheckListView(filterPanelController.model.filters) { addStylesheet(FilterPanelFieldsViewStylesheet::class) + converter = FunctionalStringConverter.to { it.preview } + depthLevel = DepthLevel.LEVEL0 cellFactory = Function { @@ -33,11 +44,25 @@ class FilterPanelFieldsView : View() { val itemsNumberProperty = Bindings.size(items) prefHeightProperty().bind(itemsNumberProperty.multiply(35.0)) - paddingBottom = 15.0 + items.onChange { forceCheckboxesVisualization() } + Platform.runLater { currentWindow?.widthProperty()?.onChange { forceCheckboxesVisualization() } } + paddingLeft = 1.5 useMaxWidth = true } + // Needed because mfx checkboxes are invisible until the first click and before a window hiding (mfx problem). + private fun forceCheckboxesVisualization() { + CoroutineScope(Dispatchers.JavaFx).launch { + delay(ListFieldSpawnTimeMillis) + + val visualizationForceNode = pane() + visualizationForceNode.isManaged = false + add(visualizationForceNode) + getChildList()?.remove(visualizationForceNode) + } + } + override val root = filtersListView private fun addButtonsToCell(cell: MFXCheckListCell) { @@ -56,4 +81,8 @@ class FilterPanelFieldsView : View() { } ) } + + companion object { + private const val ListFieldSpawnTimeMillis = 20L + } } diff --git a/src/main/kotlin/solve/filters/view/FilterPanelView.kt b/src/main/kotlin/solve/filters/view/FilterPanelView.kt index e29185817..ecb1171cb 100644 --- a/src/main/kotlin/solve/filters/view/FilterPanelView.kt +++ b/src/main/kotlin/solve/filters/view/FilterPanelView.kt @@ -42,7 +42,7 @@ class FilterPanelView : View() { hbox { mfxCircleButton(addButtonGraphic, AddFilterButtonSize) { action { - filterSettingsView.showDialog(this@FilterPanelView) + filterSettingsView.showCreationDialog(this@FilterPanelView) } } paddingRight = 14.0 diff --git a/src/main/kotlin/solve/project/model/ProjectFrames.kt b/src/main/kotlin/solve/project/model/ProjectFrames.kt new file mode 100644 index 000000000..9f7cc37b4 --- /dev/null +++ b/src/main/kotlin/solve/project/model/ProjectFrames.kt @@ -0,0 +1,5 @@ +package solve.project.model + +class ProjectFrames { + +} \ No newline at end of file diff --git a/src/main/kotlin/solve/utils/StructuresUtils.kt b/src/main/kotlin/solve/utils/StructuresUtils.kt new file mode 100644 index 000000000..3d4f45a5c --- /dev/null +++ b/src/main/kotlin/solve/utils/StructuresUtils.kt @@ -0,0 +1,3 @@ +package solve.utils + +fun Map.getKeys(value: V) = keys.filter { this[it] == value } diff --git a/src/main/kotlin/solve/utils/materialfx/MFXIntegerTextField.kt b/src/main/kotlin/solve/utils/materialfx/MFXIntegerTextField.kt index 6db5e41a4..a67479c41 100644 --- a/src/main/kotlin/solve/utils/materialfx/MFXIntegerTextField.kt +++ b/src/main/kotlin/solve/utils/materialfx/MFXIntegerTextField.kt @@ -17,7 +17,9 @@ class MFXIntegerTextField(private val invalidErrorMessage: String? = null) : MFX val root: Node get() = vbox { - add(this) + removeFromParent() + + add(this@MFXIntegerTextField) add(errorMessageLabel) } @@ -31,8 +33,8 @@ class MFXIntegerTextField(private val invalidErrorMessage: String? = null) : MFX private fun initializeIntegerTextField() { addStylesheet(MFXValidationTextFieldStylesheet::class) - val areAllDigitsSymbols = textProperty().booleanBinding { text -> text?.all { it.isDigit() } ?: false } - validator.constraint(Constraint(Severity.ERROR, invalidErrorMessage, areAllDigitsSymbols)) + val areAllDigitSymbols = textProperty().booleanBinding { text -> text?.all { it.isDigit() } ?: false } + validator.constraint(Constraint(Severity.ERROR, invalidErrorMessage, areAllDigitSymbols)) val enableErrorBorderColorCss = enableBorderColorCssString(MFXValidationTextFieldStylesheet.ErrorBorderColor) val enableDefaultBorderColorCss = @@ -53,7 +55,7 @@ class MFXIntegerTextField(private val invalidErrorMessage: String? = null) : MFX fontSize = createPxValue(10.0) } visibleProperty().bind(!validator.validProperty()) - textProperty().onChange { + this@MFXIntegerTextField.textProperty().onChange { text = validationMessage ?: return@onChange } From 7a5d9fe2dd9b742d3b942a7fe84b8d6ed9a109e2 Mon Sep 17 00:00:00 2001 From: Stanislav Mishchenko Date: Fri, 12 May 2023 00:24:10 +0300 Subject: [PATCH 10/36] Add a filter applying logic --- .../controller/FilterPanelController.kt | 22 ++++++++++++++ src/main/kotlin/solve/filters/model/Filter.kt | 8 ++++- .../settings/view/FilterSettingsView.kt | 6 ++-- .../filters/view/FilterPanelFieldsView.kt | 30 +++++++++++++++---- .../solve/importer/view/ControlPanel.kt | 6 ++-- src/main/kotlin/solve/main/MainController.kt | 7 +++-- .../project/controller/ProjectController.kt | 30 +++++++++++++++++++ .../solve/project/model/ProjectFrames.kt | 5 ---- .../solve/project/model/ProjectModel.kt | 15 ++++++++++ 9 files changed, 110 insertions(+), 19 deletions(-) create mode 100644 src/main/kotlin/solve/project/controller/ProjectController.kt delete mode 100644 src/main/kotlin/solve/project/model/ProjectFrames.kt create mode 100644 src/main/kotlin/solve/project/model/ProjectModel.kt diff --git a/src/main/kotlin/solve/filters/controller/FilterPanelController.kt b/src/main/kotlin/solve/filters/controller/FilterPanelController.kt index 305cca4cc..9af40a2ad 100644 --- a/src/main/kotlin/solve/filters/controller/FilterPanelController.kt +++ b/src/main/kotlin/solve/filters/controller/FilterPanelController.kt @@ -1,12 +1,18 @@ 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() + fun addFilter(filter: Filter) { model.addFilter(filter) } @@ -18,4 +24,20 @@ class FilterPanelController : Controller() { fun editFilter(oldFilter: Filter, editedFilter: Filter) { model.replaceFilter(oldFilter, editedFilter) } + + fun applyFilters() { + val filteredFrames = getFilteredProjectFrames() + catalogueController.setCatalogueFrames(filteredFrames) + } + + private fun getFilteredProjectFrames(): List { + val projectFrames = projectController.model.project.frames + + val enabledFilters = model.filters.filter { it.enabled } + if (enabledFilters.isEmpty()) { + return projectFrames + } + + return enabledFilters.map { it.apply(projectFrames) }.flatten().distinct() + } } diff --git a/src/main/kotlin/solve/filters/model/Filter.kt b/src/main/kotlin/solve/filters/model/Filter.kt index 138b8f4e4..b4069709d 100644 --- a/src/main/kotlin/solve/filters/model/Filter.kt +++ b/src/main/kotlin/solve/filters/model/Filter.kt @@ -3,9 +3,15 @@ package solve.filters.model import solve.filters.settings.model.FilterSetting import solve.project.model.ProjectFrame -data class Filter(val settings: List>) { +class Filter(val settings: List>) { + var enabled: Boolean = true val preview: String by lazy { "filter" } + fun apply(frames: List): List { + if (!enabled) { + return frames + } + var suitableFrames = frames settings.forEach { suitableFrames = it.apply(suitableFrames) } diff --git a/src/main/kotlin/solve/filters/settings/view/FilterSettingsView.kt b/src/main/kotlin/solve/filters/settings/view/FilterSettingsView.kt index cbb666ea7..275f2abe5 100644 --- a/src/main/kotlin/solve/filters/settings/view/FilterSettingsView.kt +++ b/src/main/kotlin/solve/filters/settings/view/FilterSettingsView.kt @@ -51,7 +51,7 @@ class FilterSettingsView : View() { private val selectedSettingCheckboxes = observableSetOf() private val areAllEnabledTextFieldsValid: Boolean - get() = validTextFields.toSet() == enabledTextFields.toSet().also { println(123) } + get() = validTextFields.toSet() == enabledTextFields.toSet() private val haveSelectedCheckboxes: Boolean get() = selectedSettingCheckboxes.isNotEmpty() private val areAllEnabledTextFieldsNotEmpty: Boolean @@ -81,7 +81,7 @@ class FilterSettingsView : View() { this@FilterSettingsView.close() } } - val createButton = controlButton("CREATE") { + controlButton("CREATE") { fun updateDisableProperty() { isDisable = !canCreateFilter } @@ -220,7 +220,7 @@ class FilterSettingsView : View() { activeNode.setEnabledByCheckboxSelection(checkBox) val settingFieldNode = hbox(10) { - checkboxToSettingNodeMap[checkBox] = settingNode + checkboxToSettingNodeMap[checkBox] = activeNode hbox { add(checkBox) settingLabel(name, checkBox) diff --git a/src/main/kotlin/solve/filters/view/FilterPanelFieldsView.kt b/src/main/kotlin/solve/filters/view/FilterPanelFieldsView.kt index 20b4d51e6..afc9cd08f 100644 --- a/src/main/kotlin/solve/filters/view/FilterPanelFieldsView.kt +++ b/src/main/kotlin/solve/filters/view/FilterPanelFieldsView.kt @@ -1,10 +1,12 @@ package solve.filters.view +import io.github.palexdev.materialfx.controls.MFXCheckListView import io.github.palexdev.materialfx.controls.cell.MFXCheckListCell import io.github.palexdev.materialfx.effects.DepthLevel import io.github.palexdev.mfxcore.utils.converters.FunctionalStringConverter import javafx.application.Platform import javafx.beans.binding.Bindings +import javafx.collections.ListChangeListener import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.delay @@ -13,6 +15,7 @@ import kotlinx.coroutines.launch import solve.constants.IconsDeletePath import solve.constants.IconsEditPath import solve.filters.controller.FilterPanelController +import solve.filters.model.Filter import solve.styles.FilterPanelFieldsViewStylesheet import solve.utils.createHGrowHBox import solve.utils.imageViewIcon @@ -41,11 +44,7 @@ class FilterPanelFieldsView : View() { return@Function cell } - val itemsNumberProperty = Bindings.size(items) - prefHeightProperty().bind(itemsNumberProperty.multiply(35.0)) - - items.onChange { forceCheckboxesVisualization() } - Platform.runLater { currentWindow?.widthProperty()?.onChange { forceCheckboxesVisualization() } } + addFilterListViewBindings() paddingLeft = 1.5 useMaxWidth = true @@ -82,6 +81,27 @@ class FilterPanelFieldsView : View() { ) } + private fun MFXCheckListView.addFilterListViewBindings() { + val itemsNumberProperty = Bindings.size(items) + prefHeightProperty().bind(itemsNumberProperty.multiply(35.0)) + + items.onChange { forceCheckboxesVisualization() } + Platform.runLater { currentWindow?.widthProperty()?.onChange { forceCheckboxesVisualization() } } + + selectionModel.selection.onChange { change -> + val isFilterEnabled = change.wasAdded() + val filter = change.valueAdded ?: change.valueRemoved + filter.enabled = isFilterEnabled + + filterPanelController.applyFilters() + } + + items.onChange { + selectionModel.selection + println(123) + } + } + companion object { private const val ListFieldSpawnTimeMillis = 20L } diff --git a/src/main/kotlin/solve/importer/view/ControlPanel.kt b/src/main/kotlin/solve/importer/view/ControlPanel.kt index 4a7866eeb..13ccecc9c 100644 --- a/src/main/kotlin/solve/importer/view/ControlPanel.kt +++ b/src/main/kotlin/solve/importer/view/ControlPanel.kt @@ -18,6 +18,7 @@ import solve.importer.model.ButtonModel import solve.importer.model.FrameAfterPartialParsing import solve.main.MainController import solve.main.MainView +import solve.project.controller.ProjectController import solve.styles.Style import solve.styles.Style.ControlButtonsSpacing import solve.utils.createAlertForError @@ -35,6 +36,8 @@ class ControlPanel : View() { private val mainController: MainController by inject() + private val projectController: ProjectController by inject() + private val importer: DirectoryPathView by inject() private val mainView: MainView by inject() @@ -172,8 +175,7 @@ class ControlPanel : View() { val projectVal = withContext(Dispatchers.IO) { fullParseDirectory(controller.projectAfterPartialParsing.value) } try { - mainController.visualizeProject(projectVal.layers, projectVal.frames) - mainController.displayCatalogueFrames(projectVal.frames) + projectController.changeProject(projectVal) button.isDisable = true mainView.dialog.close() diff --git a/src/main/kotlin/solve/main/MainController.kt b/src/main/kotlin/solve/main/MainController.kt index 00e71e85c..f5370030a 100644 --- a/src/main/kotlin/solve/main/MainController.kt +++ b/src/main/kotlin/solve/main/MainController.kt @@ -2,17 +2,16 @@ package solve.main import solve.catalogue.controller.CatalogueController import solve.main.splitpane.SidePanelLocation +import solve.project.controller.ProjectController import solve.project.model.ProjectFrame import solve.project.model.ProjectLayer import solve.scene.SceneFacade -import tornadofx.Controller +import tornadofx.* class MainController : Controller() { private val view: MainView by inject() private val catalogueController: CatalogueController by inject() - fun displayCatalogueFrames(frames: List) = catalogueController.setCatalogueFrames(frames) - fun hideSidePanelContent(location: SidePanelLocation) { view.hideSidePanelContent(location) } @@ -21,6 +20,8 @@ class MainController : Controller() { view.showSidePanelContent(location) } + fun displayCatalogueFrames(frames: List) = catalogueController.setCatalogueFrames(frames) + fun visualizeProject(layers: List, frames: List) { SceneFacade.visualize(layers, frames, false) } diff --git a/src/main/kotlin/solve/project/controller/ProjectController.kt b/src/main/kotlin/solve/project/controller/ProjectController.kt new file mode 100644 index 000000000..da597200e --- /dev/null +++ b/src/main/kotlin/solve/project/controller/ProjectController.kt @@ -0,0 +1,30 @@ +package solve.project.controller + +import solve.main.MainController +import solve.project.model.Project +import solve.project.model.ProjectModel +import tornadofx.* + +class ProjectController : Controller() { + val model = ProjectModel() + + private val mainController: MainController by inject() + + init { + addMainControllerBindings() + } + + fun changeProject(newProject: Project) { + model.changeProject(newProject) + } + + private fun addMainControllerBindings() { + model.projectProperty.onChange { newProject -> + println(newProject) + newProject ?: return@onChange + + mainController.visualizeProject(newProject.layers, newProject.frames) + mainController.displayCatalogueFrames(newProject.frames) + } + } +} diff --git a/src/main/kotlin/solve/project/model/ProjectFrames.kt b/src/main/kotlin/solve/project/model/ProjectFrames.kt deleted file mode 100644 index 9f7cc37b4..000000000 --- a/src/main/kotlin/solve/project/model/ProjectFrames.kt +++ /dev/null @@ -1,5 +0,0 @@ -package solve.project.model - -class ProjectFrames { - -} \ No newline at end of file diff --git a/src/main/kotlin/solve/project/model/ProjectModel.kt b/src/main/kotlin/solve/project/model/ProjectModel.kt new file mode 100644 index 000000000..9dae3e794 --- /dev/null +++ b/src/main/kotlin/solve/project/model/ProjectModel.kt @@ -0,0 +1,15 @@ +package solve.project.model + +import javafx.beans.property.ReadOnlyObjectProperty +import javafx.beans.property.ReadOnlyObjectWrapper + +class ProjectModel { + private val _projectProperty = ReadOnlyObjectWrapper() + val projectProperty: ReadOnlyObjectProperty = _projectProperty.readOnlyProperty + val project: Project + get() = projectProperty.value + + fun changeProject(newProject: Project) { + _projectProperty.value = newProject + } +} From 6ea64ee09a62cfc7a236026c9f65e6252445a66b Mon Sep 17 00:00:00 2001 From: Stanislav Mishchenko Date: Fri, 12 May 2023 00:35:35 +0300 Subject: [PATCH 11/36] Fix a filter selection breaking bug --- .../filters/view/FilterPanelFieldsView.kt | 20 +++++++++---------- 1 file changed, 9 insertions(+), 11 deletions(-) diff --git a/src/main/kotlin/solve/filters/view/FilterPanelFieldsView.kt b/src/main/kotlin/solve/filters/view/FilterPanelFieldsView.kt index afc9cd08f..0fe740f1a 100644 --- a/src/main/kotlin/solve/filters/view/FilterPanelFieldsView.kt +++ b/src/main/kotlin/solve/filters/view/FilterPanelFieldsView.kt @@ -7,6 +7,7 @@ import io.github.palexdev.mfxcore.utils.converters.FunctionalStringConverter import javafx.application.Platform import javafx.beans.binding.Bindings import javafx.collections.ListChangeListener +import javafx.collections.MapChangeListener import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.delay @@ -88,18 +89,15 @@ class FilterPanelFieldsView : View() { items.onChange { forceCheckboxesVisualization() } Platform.runLater { currentWindow?.widthProperty()?.onChange { forceCheckboxesVisualization() } } - selectionModel.selection.onChange { change -> - val isFilterEnabled = change.wasAdded() - val filter = change.valueAdded ?: change.valueRemoved - filter.enabled = isFilterEnabled + selectionModel.selectionProperty().addListener( + MapChangeListener { change -> + val isFilterEnabled = change.wasAdded() + val filter = change.valueAdded ?: change.valueRemoved + filter.enabled = isFilterEnabled - filterPanelController.applyFilters() - } - - items.onChange { - selectionModel.selection - println(123) - } + filterPanelController.applyFilters() + } + ) } companion object { From fb590f66bd6a97c1b80ca49c35b76d5899f56311 Mon Sep 17 00:00:00 2001 From: Stanislav Mishchenko Date: Fri, 12 May 2023 00:48:10 +0300 Subject: [PATCH 12/36] Make filtered frames sorted in intial way --- .../kotlin/solve/filters/controller/FilterPanelController.kt | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/main/kotlin/solve/filters/controller/FilterPanelController.kt b/src/main/kotlin/solve/filters/controller/FilterPanelController.kt index 9af40a2ad..3eb16c72f 100644 --- a/src/main/kotlin/solve/filters/controller/FilterPanelController.kt +++ b/src/main/kotlin/solve/filters/controller/FilterPanelController.kt @@ -38,6 +38,9 @@ class FilterPanelController : Controller() { return projectFrames } - return enabledFilters.map { it.apply(projectFrames) }.flatten().distinct() + val notOrderedFilteredFrames = enabledFilters.map { it.apply(projectFrames) }.flatten().distinct() + val projectFrameToIndexMap = projectFrames.indices.associateBy { index -> projectFrames[index] } + + return notOrderedFilteredFrames.sortedBy { frame -> projectFrameToIndexMap[frame] } } } From 5e0c7453f3d4ebd1d6f66add0d83a4bf83ab26bf Mon Sep 17 00:00:00 2001 From: Stanislav Mishchenko Date: Fri, 12 May 2023 03:46:54 +0300 Subject: [PATCH 13/36] Fix bugs with checboxes selections and some design --- .../controller/FilterPanelController.kt | 3 - .../controller/FilterSettingsController.kt | 8 +- .../settings/view/FilterSettingsView.kt | 86 +++++++++++++++++-- .../filters/view/FilterPanelFieldsView.kt | 81 ++++++++++++----- .../solve/filters/view/FilterPanelView.kt | 5 +- .../solve/utils/materialfx/MFXNodesUtils.kt | 12 +-- 6 files changed, 152 insertions(+), 43 deletions(-) diff --git a/src/main/kotlin/solve/filters/controller/FilterPanelController.kt b/src/main/kotlin/solve/filters/controller/FilterPanelController.kt index 3eb16c72f..476dbd006 100644 --- a/src/main/kotlin/solve/filters/controller/FilterPanelController.kt +++ b/src/main/kotlin/solve/filters/controller/FilterPanelController.kt @@ -34,9 +34,6 @@ class FilterPanelController : Controller() { val projectFrames = projectController.model.project.frames val enabledFilters = model.filters.filter { it.enabled } - if (enabledFilters.isEmpty()) { - return projectFrames - } val notOrderedFilteredFrames = enabledFilters.map { it.apply(projectFrames) }.flatten().distinct() val projectFrameToIndexMap = projectFrames.indices.associateBy { index -> projectFrames[index] } diff --git a/src/main/kotlin/solve/filters/settings/controller/FilterSettingsController.kt b/src/main/kotlin/solve/filters/settings/controller/FilterSettingsController.kt index 37923f5c4..d305fbf5b 100644 --- a/src/main/kotlin/solve/filters/settings/controller/FilterSettingsController.kt +++ b/src/main/kotlin/solve/filters/settings/controller/FilterSettingsController.kt @@ -13,8 +13,10 @@ class FilterSettingsController : Controller() { panelController.addFilter(filter) } - fun createEditedFilter(oldFilter: Filter, editedFilterSettings: List>) { - val editedFilter = Filter(editedFilterSettings) - panelController.editFilter(oldFilter, editedFilter) + fun editFilter(editingFilter: Filter, newFilterSettings: List>) { + val editedFilter = Filter(newFilterSettings) + editedFilter.enabled = editingFilter.enabled + + panelController.editFilter(editingFilter, editedFilter) } } diff --git a/src/main/kotlin/solve/filters/settings/view/FilterSettingsView.kt b/src/main/kotlin/solve/filters/settings/view/FilterSettingsView.kt index 275f2abe5..d2051d7cc 100644 --- a/src/main/kotlin/solve/filters/settings/view/FilterSettingsView.kt +++ b/src/main/kotlin/solve/filters/settings/view/FilterSettingsView.kt @@ -2,6 +2,7 @@ package solve.filters.settings.view import io.github.palexdev.materialfx.enums.FloatMode import javafx.beans.InvalidationListener +import javafx.beans.property.SimpleObjectProperty import javafx.event.EventTarget import javafx.geometry.Insets import javafx.geometry.Pos @@ -39,6 +40,24 @@ class FilterSettingsView : View() { private lateinit var stepNumberIntegerTextField: MFXIntegerTextField private lateinit var uidIntegerTextField: MFXIntegerTextField + private var currentSettingsDialogModeProperty = SimpleObjectProperty(FilterSettingsDialogMode.CreationMode) + private var currentSettingsDialogMode by currentSettingsDialogModeProperty + + private val currentHeaderText: String + get() = if (currentSettingsDialogMode == FilterSettingsDialogMode.CreationMode) { + CreationModeDialogHeaderText + } else { + EditingModeDialogHeaderText + } + private val currentOKButtonText: String + get() = if (currentSettingsDialogMode == FilterSettingsDialogMode.CreationMode) { + CreationModeDialogOKButtonText + } else { + EditingModeDialogOKButtonText + } + + lateinit var editingModeOldFilter: Filter + private val checkboxToSettingNodeMap = mutableMapOf() private val nodeToNodeConnectorMap = mutableMapOf>>() @@ -61,7 +80,11 @@ class FilterSettingsView : View() { private val filterSettingsContentNode = borderpane { top = vbox { - dialogHeaderLabel("Create new filter") { + dialogHeaderLabel(currentHeaderText) { + currentSettingsDialogModeProperty.onChange { + text = currentHeaderText + } + padding = headerPadding } vbox(10) { @@ -81,7 +104,11 @@ class FilterSettingsView : View() { this@FilterSettingsView.close() } } - controlButton("CREATE") { + controlButton(currentOKButtonText) { + currentSettingsDialogModeProperty.onChange { + text = currentOKButtonText + } + fun updateDisableProperty() { isDisable = !canCreateFilter } @@ -94,8 +121,7 @@ class FilterSettingsView : View() { notEmptyTextFields.addListener(updateDisablePropertyChangeListener) action { - val filterSettings = getFilterSettings() - controller.createFilter(filterSettings) + onCreateFilter() this@FilterSettingsView.close() } } @@ -106,9 +132,34 @@ class FilterSettingsView : View() { } override val root = filterSettingsContentNode - fun showCreationDialog(parent: View) { + fun showCreationDialog() { + currentSettingsDialogMode = FilterSettingsDialogMode.CreationMode setDialogInitialState() + initializeAndShowDialog(this) + } + + fun showEditingDialog(oldFilter: Filter) { + currentSettingsDialogMode = FilterSettingsDialogMode.EditingMode + editingModeOldFilter = oldFilter + + setDialogInitialState() + setControlNodesSettingsFromFilter(editingModeOldFilter) + enableControlNodesCheckboxesFromFilter(editingModeOldFilter) + + initializeAndShowDialog(this) + } + private fun onCreateFilter() { + val filterSettings = getFilterSettings() + + if (currentSettingsDialogMode == FilterSettingsDialogMode.CreationMode) { + controller.createFilter(filterSettings) + } else { + controller.editFilter(editingModeOldFilter, filterSettings) + } + } + + private fun initializeAndShowDialog(parent: View) { val content = MaterialFXDialog.createGenericDialog(root) val dialog = MaterialFXDialog.createStageDialog(content, parent.currentStage, parent.root as Pane) dialog.isDraggable = false @@ -117,15 +168,15 @@ class FilterSettingsView : View() { dialog.centerOnScreen() } - fun showEditingDialog(parent: View, oldFilter: Filter) { - - } - private fun setDialogInitialState() { settingCheckboxes.forEach { it.isSelected = false } nodeToNodeConnectorMap.forEach { (node, connector) -> connector.setDefaultSettingNodeState(node) } + selectedSettingCheckboxes.clear() + validTextFields.clear() + enabledTextFields.clear() + notEmptyTextFields.clear() } private fun getNodeByFilterSetting(setting: FilterSetting): Node { @@ -145,6 +196,13 @@ class FilterSettingsView : View() { } } + private fun enableControlNodesCheckboxesFromFilter(filter: Filter) { + filter.settings.forEach { setting -> + val correspondingNode = getNodeByFilterSetting(setting) + checkboxToSettingNodeMap.getKeys(correspondingNode).first().isSelected = true + } + } + private fun Node.setEnabledByCheckboxSelection(checkBox: CheckBox) { enableWhen(checkBox.selectedProperty()) } @@ -283,10 +341,20 @@ class FilterSettingsView : View() { companion object { private const val IntegerTextFieldSymbolsLimit = Long.MIN_VALUE.toString().length + private const val CreationModeDialogHeaderText = "Create new filter" + private const val EditingModeDialogHeaderText = "Edit filter" + private const val CreationModeDialogOKButtonText = "CREATE" + private const val EditingModeDialogOKButtonText = "SAVE" + private const val FilterSettingNameFontSize = 14.0 private const val FilterSettingNonTextFieldPaddingTop = 8.0 private const val TimeLimitRangeSliderWidth = 300.0 } } +private enum class FilterSettingsDialogMode { + CreationMode, + EditingMode +} + private data class SettingFieldData(val node: HBox, val checkBox: CheckBox) diff --git a/src/main/kotlin/solve/filters/view/FilterPanelFieldsView.kt b/src/main/kotlin/solve/filters/view/FilterPanelFieldsView.kt index 0fe740f1a..a5f13fab4 100644 --- a/src/main/kotlin/solve/filters/view/FilterPanelFieldsView.kt +++ b/src/main/kotlin/solve/filters/view/FilterPanelFieldsView.kt @@ -6,7 +6,6 @@ import io.github.palexdev.materialfx.effects.DepthLevel import io.github.palexdev.mfxcore.utils.converters.FunctionalStringConverter import javafx.application.Platform import javafx.beans.binding.Bindings -import javafx.collections.ListChangeListener import javafx.collections.MapChangeListener import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers @@ -17,16 +16,19 @@ import solve.constants.IconsDeletePath import solve.constants.IconsEditPath import solve.filters.controller.FilterPanelController import solve.filters.model.Filter +import solve.filters.settings.view.FilterSettingsView import solve.styles.FilterPanelFieldsViewStylesheet import solve.utils.createHGrowHBox import solve.utils.imageViewIcon import solve.utils.loadResourcesImage import solve.utils.materialfx.mfxCheckListView +import solve.utils.materialfx.mfxCircleButton import tornadofx.* import java.util.function.Function class FilterPanelFieldsView : View() { private val filterPanelController: FilterPanelController by inject() + private val filterPanelSettingsView: FilterSettingsView by inject() private val editIconImage = loadResourcesImage(IconsEditPath) private val deleteIconImage = loadResourcesImage(IconsDeletePath) @@ -48,9 +50,48 @@ class FilterPanelFieldsView : View() { addFilterListViewBindings() paddingLeft = 1.5 + itemsProperty().onChange { + paddingBottom = if (items.isEmpty()) { + 0.0 + } else { + 14.0 + } + } useMaxWidth = true } + override val root = filtersListView + + private fun addButtonsToCell(cell: MFXCheckListCell) { + val filter = cell.data + + cell.add(createHGrowHBox()) + cell.add( + hbox { + mfxCircleButton(radius = FieldButtonPressRippleCircleRadius) { + graphic = imageViewIcon(editIconImage ?: return@mfxCircleButton, FieldButtonsIconsSize) + + action { + filterPanelSettingsView.showEditingDialog(filter) + } + } + mfxCircleButton(radius = FieldButtonPressRippleCircleRadius) { + imageViewIcon(deleteIconImage ?: return@mfxCircleButton, FieldButtonsIconsSize) + action { + // Needed because when an unselected element is removed, + // the whole selection is recreated with the addition of the remaining elements, + // which value changes causes changes of selectionProperty. + filtersListView.selectionModel.deselectItem(filter) + + filterPanelController.removeFilter(filter) + } + } + + paddingRight = 20.5 + } + ) + } + // Needed because mfx checkboxes are invisible until the first click and before a window hiding (mfx problem). private fun forceCheckboxesVisualization() { CoroutineScope(Dispatchers.JavaFx).launch { @@ -63,37 +104,32 @@ class FilterPanelFieldsView : View() { } } - override val root = filtersListView - - private fun addButtonsToCell(cell: MFXCheckListCell) { - cell.add(createHGrowHBox()) - cell.add( - hbox { - hbox { - imageViewIcon(editIconImage ?: return@hbox, 24.0) { - paddingTop = 3.5 - paddingRight = 7.0 - } - } - imageViewIcon(deleteIconImage ?: return@hbox, 24.0) - - paddingRight = 20.5 + private fun updateItemsSelection() { + val selection = filtersListView.selectionModel.selection + filtersListView.items.forEach { item -> + if (item.enabled && ! selection.containsValue(item)) { + filtersListView.selectionModel.selectItem(item) + } else if (!item.enabled && selection.containsValue(item)) { + filtersListView.selectionModel.deselectItem(item) } - ) + } } private fun MFXCheckListView.addFilterListViewBindings() { val itemsNumberProperty = Bindings.size(items) prefHeightProperty().bind(itemsNumberProperty.multiply(35.0)) - items.onChange { forceCheckboxesVisualization() } + items.onChange { + updateItemsSelection() + forceCheckboxesVisualization() + } + Platform.runLater { currentWindow?.widthProperty()?.onChange { forceCheckboxesVisualization() } } selectionModel.selectionProperty().addListener( MapChangeListener { change -> - val isFilterEnabled = change.wasAdded() val filter = change.valueAdded ?: change.valueRemoved - filter.enabled = isFilterEnabled + filter.enabled = change.wasAdded() filterPanelController.applyFilters() } @@ -101,6 +137,9 @@ class FilterPanelFieldsView : View() { } companion object { - private const val ListFieldSpawnTimeMillis = 20L + private const val ListFieldSpawnTimeMillis = 500L + + private const val FieldButtonsIconsSize = 24.0 + private const val FieldButtonPressRippleCircleRadius = 15.0 } } diff --git a/src/main/kotlin/solve/filters/view/FilterPanelView.kt b/src/main/kotlin/solve/filters/view/FilterPanelView.kt index ecb1171cb..c73afb90d 100644 --- a/src/main/kotlin/solve/filters/view/FilterPanelView.kt +++ b/src/main/kotlin/solve/filters/view/FilterPanelView.kt @@ -3,6 +3,7 @@ package solve.filters.view import javafx.scene.paint.Paint import solve.constants.IconsFiltersAddPath import solve.filters.settings.view.FilterSettingsView +import solve.project.controller.ProjectController import solve.styles.SeparatorStylesheet import solve.styles.Style import solve.utils.createHGrowHBox @@ -15,6 +16,7 @@ import tornadofx.* class FilterPanelView : View() { private val filterPanelFieldsView: FilterPanelFieldsView by inject() private val filterSettingsView: FilterSettingsView by inject() + private val projectController: ProjectController by inject() override val root = vbox { separator { @@ -41,8 +43,9 @@ class FilterPanelView : View() { } hbox { mfxCircleButton(addButtonGraphic, AddFilterButtonSize) { + enableWhen(projectController.model.projectProperty.booleanBinding { it != null }) action { - filterSettingsView.showCreationDialog(this@FilterPanelView) + filterSettingsView.showCreationDialog() } } paddingRight = 14.0 diff --git a/src/main/kotlin/solve/utils/materialfx/MFXNodesUtils.kt b/src/main/kotlin/solve/utils/materialfx/MFXNodesUtils.kt index 4abebee45..16886ab76 100644 --- a/src/main/kotlin/solve/utils/materialfx/MFXNodesUtils.kt +++ b/src/main/kotlin/solve/utils/materialfx/MFXNodesUtils.kt @@ -6,7 +6,10 @@ import io.github.palexdev.materialfx.controls.MFXCheckbox import io.github.palexdev.materialfx.controls.MFXContextMenu import io.github.palexdev.materialfx.controls.MFXContextMenuItem import io.github.palexdev.materialfx.controls.MFXTextField +import io.github.palexdev.materialfx.effects.ripple.RippleClipType +import io.github.palexdev.materialfx.factories.RippleClipTypeFactory import javafx.application.Platform +import javafx.beans.binding.Bindings import javafx.collections.ObservableList import javafx.event.EventTarget import javafx.scene.Node @@ -48,18 +51,15 @@ fun MFXContextMenu.lineSeparator() = this.addLineSeparator(MFXContextMenu.Builde fun MFXContextMenuItem.action(op: () -> Unit) = this.setOnAction { op() } -fun EventTarget.mfxCircleButton( +fun Node.mfxCircleButton( graphic: Node? = null, radius: Double = 20.0, text: String = "", op: MFXButton.() -> Unit = {} ) = mfxButton(text, graphic) { this.graphic = graphic - Platform.runLater { - val rippleCenter = boundsInLocal - rippleGenerator.clipSupplier = Supplier { - return@Supplier Circle(rippleCenter.centerX, rippleCenter.centerY, radius) - } + rippleGenerator.clipSupplier = Supplier { + RippleClipTypeFactory(RippleClipType.CIRCLE).setRadius(radius).build(this) } style { backgroundColor += Color.TRANSPARENT From 126459d5563de0a2f4ffa37a9d07749a813d5f11 Mon Sep 17 00:00:00 2001 From: Stanislav Mishchenko Date: Fri, 12 May 2023 08:32:23 +0300 Subject: [PATCH 14/36] Add a range slider uids visualization --- src/main/kotlin/solve/filters/model/Filter.kt | 30 ++++- .../settings/view/FilterSettingsView.kt | 127 +++++++++++++----- .../TimePeriodSettingNodeConnector.kt | 15 ++- .../filters/view/FilterPanelFieldsView.kt | 14 +- .../project/controller/ProjectController.kt | 1 - .../solve/utils/materialfx/MFXNodesUtils.kt | 24 +++- 6 files changed, 165 insertions(+), 46 deletions(-) diff --git a/src/main/kotlin/solve/filters/model/Filter.kt b/src/main/kotlin/solve/filters/model/Filter.kt index b4069709d..2f0bd75d7 100644 --- a/src/main/kotlin/solve/filters/model/Filter.kt +++ b/src/main/kotlin/solve/filters/model/Filter.kt @@ -1,11 +1,14 @@ 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>) { var enabled: Boolean = true - val preview: String by lazy { "filter" } + val preview: String by lazy { createPreviewText() } fun apply(frames: List): List { if (!enabled) { @@ -17,4 +20,29 @@ class Filter(val settings: List>) { 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.step} image " + } else { + "images " + } + val timePeriodPart = if (timePeriodSetting != null) { + "from ${timePeriodSetting.timePeriod.x} to ${timePeriodSetting.timePeriod.y} " + } else { + "" + } + val uidPart = if (uidSetting != null) { + "with landmark ${uidSetting.uid}" + } else { + "" + } + + return "$prefix$indicesStepPart$timePeriodPart$uidPart" + } } diff --git a/src/main/kotlin/solve/filters/settings/view/FilterSettingsView.kt b/src/main/kotlin/solve/filters/settings/view/FilterSettingsView.kt index d2051d7cc..d3b3495cc 100644 --- a/src/main/kotlin/solve/filters/settings/view/FilterSettingsView.kt +++ b/src/main/kotlin/solve/filters/settings/view/FilterSettingsView.kt @@ -14,6 +14,7 @@ import javafx.scene.layout.BorderPane import javafx.scene.layout.HBox import javafx.scene.layout.Pane import javafx.scene.text.Font +import org.controlsfx.control.RangeSlider import solve.filters.model.Filter import solve.filters.settings.controller.FilterSettingsController import solve.filters.settings.model.FilterSetting @@ -21,9 +22,9 @@ import solve.filters.settings.view.controls.FilterSettingNodeConnector import solve.filters.settings.view.controls.IndicesStepSettingNodeConnector import solve.filters.settings.view.controls.TimePeriodSettingNodeConnector import solve.filters.settings.view.controls.UIDSettingNodeConnector -import solve.project.model.ProjectFrame import solve.styles.Style import solve.styles.Style.headerPadding +import solve.utils.createHGrowHBox import solve.utils.getKeys import solve.utils.materialfx.MFXIntegerTextField import solve.utils.materialfx.MFXIntegerTextField.Companion.mfxIntegerTextField @@ -31,8 +32,10 @@ import solve.utils.materialfx.MaterialFXDialog import solve.utils.materialfx.controlButton import solve.utils.materialfx.dialogHeaderLabel import solve.utils.materialfx.mfxCheckbox -import solve.utils.materialfx.mfxRangeSlider +import solve.utils.materialfx.mfxIntegerRangeSlider import tornadofx.* +import kotlin.math.roundToInt +import kotlin.math.roundToLong class FilterSettingsView : View() { private val controller: FilterSettingsController by inject() @@ -92,7 +95,7 @@ class FilterSettingsView : View() { add(buildIndicesStepSettingNode()) add(buildUIDSettingField()) - paddingTop = 10.0 + paddingTop = 20.0 paddingLeft = 30.0 } @@ -245,25 +248,48 @@ class FilterSettingsView : View() { validTextFields.add(this) } } + + alignment = Pos.CENTER } - private fun EventTarget.settingLabel( - text: String, - settingCheckBox: CheckBox, + private fun EventTarget.settingFieldLabel( + text: String = "", + fieldCheckbox: CheckBox, + fontSize: Double = FilterSettingNameFontSize, op: Label.() -> Unit = {} ) = label(text) { - font = Font.font(Style.Font, FilterSettingNameFontSize) - setEnabledByCheckboxSelection(settingCheckBox) + font = Font.font(Style.Font, fontSize) + setEnabledByCheckboxSelection(fieldCheckbox) - attachTo(this@settingLabel, op) + attachTo(this@settingFieldLabel, op) } private fun createSettingField( name: String, + checkBox: CheckBox, settingNode: Node, activeNode: Node = settingNode - ): SettingFieldData { - val checkBox = mfxCheckbox { + ): HBox { + activeNode.setEnabledByCheckboxSelection(checkBox) + + val settingFieldNode = hbox(10) { + checkboxToSettingNodeMap[checkBox] = activeNode + hbox { + add(checkBox) + settingFieldLabel(name, checkBox) { + alignment = Pos.BOTTOM_CENTER + } + + paddingTop = FilterSettingNonTextFieldPaddingTop + } + add(settingNode) + } + + return settingFieldNode + } + + private fun createSettingFieldCheckbox(): CheckBox { + val checkbox = mfxCheckbox { paddingTop = -7.0 selectedProperty().onChange { selected -> @@ -274,52 +300,87 @@ class FilterSettingsView : View() { } } } - settingCheckboxes.add(checkBox) + settingCheckboxes.add(checkbox) - activeNode.setEnabledByCheckboxSelection(checkBox) - val settingFieldNode = hbox(10) { - checkboxToSettingNodeMap[checkBox] = activeNode - hbox { - add(checkBox) - settingLabel(name, checkBox) + return checkbox + } - paddingTop = FilterSettingNonTextFieldPaddingTop - } - add(settingNode) - } + private fun createIntegerRangeSliderRangeInfoString(rangeSlider: RangeSlider): String { + val fromValue = rangeSlider.lowValue.roundToLong() + val toValue = rangeSlider.highValue.roundToLong() - return SettingFieldData(settingFieldNode, checkBox) + return "$fromValue - $toValue" } private fun buildTimePeriodSettingNode(): HBox { - val timePeriodRangeSlider = mfxRangeSlider(0.0, 10.0, 1.0, 9.0) { + val timePeriodRangeSlider = mfxIntegerRangeSlider(0.0, 1.0, 0.0, 1.0) { prefWidth = TimeLimitRangeSliderWidth paddingTop = FilterSettingNonTextFieldPaddingTop + 2.0 } nodeToNodeConnectorMap[timePeriodRangeSlider] = TimePeriodSettingNodeConnector - return createSettingField("Time period:", timePeriodRangeSlider).node + val fieldCheckbox = createSettingFieldCheckbox() + val timePeriodSettingNode = stackpane { + hbox { + add(createHGrowHBox()) + settingFieldLabel(fieldCheckbox = fieldCheckbox) { + visibleWhen(fieldCheckbox.selectedProperty()) + timePeriodRangeSlider.lowValueProperty().onChange { + text = createIntegerRangeSliderRangeInfoString(timePeriodRangeSlider) + } + timePeriodRangeSlider.highValueProperty().onChange { + text = createIntegerRangeSliderRangeInfoString(timePeriodRangeSlider) + } + } + add(createHGrowHBox()) + + paddingBottom = 30.0 + } + add(timePeriodRangeSlider) + + paddingBottom = 8.0 + paddingLeft = 5.0 + } + + return createSettingField( + "Time period:", + fieldCheckbox, + timePeriodSettingNode, + timePeriodRangeSlider + ) } private fun buildIndicesStepSettingNode(): HBox { stepNumberIntegerTextField = buildIntegerTextField("Step must be an integer number", 60.0, 145.0) nodeToNodeConnectorMap[stepNumberIntegerTextField] = IndicesStepSettingNodeConnector - val fieldData = createSettingField("Show every", stepNumberIntegerTextField.root, stepNumberIntegerTextField) - fieldData.node.add( - settingLabel("image", fieldData.checkBox) { + val fieldCheckbox = createSettingFieldCheckbox() + + val fieldNode = createSettingField( + "Show every", + fieldCheckbox, + stepNumberIntegerTextField.root, + stepNumberIntegerTextField + ) + fieldNode.add( + settingFieldLabel("image", fieldCheckbox) { paddingTop = FilterSettingNonTextFieldPaddingTop } ) - return fieldData.node + return fieldNode } private fun buildUIDSettingField(): HBox { uidIntegerTextField = buildIntegerTextField("UID must be an integer number", 155.0, 155.0) nodeToNodeConnectorMap[uidIntegerTextField] = UIDSettingNodeConnector - return createSettingField("Show images with landmark:", uidIntegerTextField.root, uidIntegerTextField).node + return createSettingField( + "Show images with landmark:", + createSettingFieldCheckbox(), + uidIntegerTextField.root, + uidIntegerTextField + ) } private fun getFilterSettings(): List> { @@ -334,10 +395,6 @@ class FilterSettingsView : View() { } } - private fun getFramesMinTimestamp(frames: List) = frames.minOf { it.timestamp } - - private fun getFramesMaxTimestamp(frames: List) = frames.maxOf { it.timestamp } - companion object { private const val IntegerTextFieldSymbolsLimit = Long.MIN_VALUE.toString().length @@ -356,5 +413,3 @@ private enum class FilterSettingsDialogMode { CreationMode, EditingMode } - -private data class SettingFieldData(val node: HBox, val checkBox: CheckBox) diff --git a/src/main/kotlin/solve/filters/settings/view/controls/TimePeriodSettingNodeConnector.kt b/src/main/kotlin/solve/filters/settings/view/controls/TimePeriodSettingNodeConnector.kt index 9abbe7e9f..e844cf12b 100644 --- a/src/main/kotlin/solve/filters/settings/view/controls/TimePeriodSettingNodeConnector.kt +++ b/src/main/kotlin/solve/filters/settings/view/controls/TimePeriodSettingNodeConnector.kt @@ -2,14 +2,19 @@ package solve.filters.settings.view.controls import org.controlsfx.control.RangeSlider import solve.filters.settings.model.TimePeriodFilterSetting +import solve.project.controller.ProjectController +import solve.project.model.ProjectFrame import solve.utils.ceilToInt import solve.utils.floorToInt import solve.utils.structures.IntPoint +import tornadofx.* object TimePeriodSettingNodeConnector : FilterSettingNodeConnector( RangeSlider::class, TimePeriodFilterSetting::class ) { + private val projectController: ProjectController = find() + override fun extractFilterSettingsFromTypedSettingNode(settingNode: RangeSlider): TimePeriodFilterSetting? { val fromTimePeriod = settingNode.lowValue.floorToInt() val toTimePeriod = settingNode.highValue.ceilToInt() @@ -23,9 +28,15 @@ object TimePeriodSettingNodeConnector : FilterSettingNodeConnector) = frames.minOf { it.timestamp } + + private fun getFramesMaxTimestamp(frames: List) = frames.maxOf { it.timestamp } } diff --git a/src/main/kotlin/solve/filters/view/FilterPanelFieldsView.kt b/src/main/kotlin/solve/filters/view/FilterPanelFieldsView.kt index a5f13fab4..262c1788c 100644 --- a/src/main/kotlin/solve/filters/view/FilterPanelFieldsView.kt +++ b/src/main/kotlin/solve/filters/view/FilterPanelFieldsView.kt @@ -7,6 +7,8 @@ import io.github.palexdev.mfxcore.utils.converters.FunctionalStringConverter import javafx.application.Platform import javafx.beans.binding.Bindings import javafx.collections.MapChangeListener +import javafx.scene.control.Label +import javafx.scene.text.Font import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.delay @@ -18,6 +20,7 @@ import solve.filters.controller.FilterPanelController import solve.filters.model.Filter import solve.filters.settings.view.FilterSettingsView import solve.styles.FilterPanelFieldsViewStylesheet +import solve.styles.Style import solve.utils.createHGrowHBox import solve.utils.imageViewIcon import solve.utils.loadResourcesImage @@ -42,7 +45,7 @@ class FilterPanelFieldsView : View() { cellFactory = Function { val cell = MFXCheckListCell(this, it) - addButtonsToCell(cell) + initializeCellGraphic(cell) return@Function cell } @@ -62,9 +65,14 @@ class FilterPanelFieldsView : View() { override val root = filtersListView - private fun addButtonsToCell(cell: MFXCheckListCell) { + private fun initializeCellGraphic(cell: MFXCheckListCell) { val filter = cell.data + val cellTextLabel = cell.getChildList()?.firstOrNull { child -> child is Label } + cellTextLabel?.tooltip { + text = filter.preview + font = Font.font(Style.Font) + } cell.add(createHGrowHBox()) cell.add( hbox { @@ -86,7 +94,6 @@ class FilterPanelFieldsView : View() { filterPanelController.removeFilter(filter) } } - paddingRight = 20.5 } ) @@ -139,6 +146,7 @@ class FilterPanelFieldsView : View() { companion object { private const val ListFieldSpawnTimeMillis = 500L + private const val FieldLabelFontSize = 16.0 private const val FieldButtonsIconsSize = 24.0 private const val FieldButtonPressRippleCircleRadius = 15.0 } diff --git a/src/main/kotlin/solve/project/controller/ProjectController.kt b/src/main/kotlin/solve/project/controller/ProjectController.kt index da597200e..13f488daf 100644 --- a/src/main/kotlin/solve/project/controller/ProjectController.kt +++ b/src/main/kotlin/solve/project/controller/ProjectController.kt @@ -20,7 +20,6 @@ class ProjectController : Controller() { private fun addMainControllerBindings() { model.projectProperty.onChange { newProject -> - println(newProject) newProject ?: return@onChange mainController.visualizeProject(newProject.layers, newProject.frames) diff --git a/src/main/kotlin/solve/utils/materialfx/MFXNodesUtils.kt b/src/main/kotlin/solve/utils/materialfx/MFXNodesUtils.kt index 16886ab76..fdf4a0243 100644 --- a/src/main/kotlin/solve/utils/materialfx/MFXNodesUtils.kt +++ b/src/main/kotlin/solve/utils/materialfx/MFXNodesUtils.kt @@ -8,17 +8,17 @@ import io.github.palexdev.materialfx.controls.MFXContextMenuItem import io.github.palexdev.materialfx.controls.MFXTextField import io.github.palexdev.materialfx.effects.ripple.RippleClipType import io.github.palexdev.materialfx.factories.RippleClipTypeFactory -import javafx.application.Platform -import javafx.beans.binding.Bindings import javafx.collections.ObservableList import javafx.event.EventTarget import javafx.scene.Node import javafx.scene.paint.Color -import javafx.scene.shape.Circle import org.controlsfx.control.RangeSlider import solve.utils.materialfx.stylesheets.MFXRangeSliderStylesheet import tornadofx.* import java.util.function.Supplier +import kotlin.math.max +import kotlin.math.min +import kotlin.math.roundToLong fun EventTarget.mfxButton( text: String = "", @@ -99,6 +99,24 @@ fun EventTarget.mfxRangeSlider( return slider } +fun EventTarget.mfxIntegerRangeSlider( + min: Double, + max: Double, + lowValue: Double, + highValue: Double, + op: RangeSlider.() -> Unit = {} +): RangeSlider { + val slider = mfxRangeSlider(min, max, lowValue, highValue, op) + slider.lowValueProperty().onChange { newLowValue -> + slider.lowValue = max(slider.min, newLowValue.roundToLong().toDouble()) + } + slider.highValueProperty().onChange { newHighValue -> + slider.highValue = min(slider.max, newHighValue.roundToLong().toDouble()) + } + + return slider +} + val MFXTextField.validationMessage: String? get() { val constraints = validator.validate() From 8ae7c507df43e18b8a2b54632b76d1258652ce93 Mon Sep 17 00:00:00 2001 From: Stanislav Mishchenko Date: Fri, 12 May 2023 08:33:25 +0300 Subject: [PATCH 15/36] Codestyle --- src/main/kotlin/solve/filters/model/FilterPanelModel.kt | 2 +- .../solve/filters/settings/view/FilterSettingsView.kt | 1 - .../settings/view/controls/FilterSettingNodeConnector.kt | 6 +++--- .../view/controls/IndicesStepSettingNodeConnector.kt | 6 ++++-- src/main/kotlin/solve/filters/view/FilterPanelFieldsView.kt | 2 +- src/main/kotlin/solve/main/MainController.kt | 1 - 6 files changed, 9 insertions(+), 9 deletions(-) diff --git a/src/main/kotlin/solve/filters/model/FilterPanelModel.kt b/src/main/kotlin/solve/filters/model/FilterPanelModel.kt index a430fc952..cfbf025cb 100644 --- a/src/main/kotlin/solve/filters/model/FilterPanelModel.kt +++ b/src/main/kotlin/solve/filters/model/FilterPanelModel.kt @@ -24,4 +24,4 @@ class FilterPanelModel { _filters.remove(oldFilter) _filters.add(oldFilterIndex, newFilter) } -} \ No newline at end of file +} diff --git a/src/main/kotlin/solve/filters/settings/view/FilterSettingsView.kt b/src/main/kotlin/solve/filters/settings/view/FilterSettingsView.kt index d3b3495cc..6d80c91f8 100644 --- a/src/main/kotlin/solve/filters/settings/view/FilterSettingsView.kt +++ b/src/main/kotlin/solve/filters/settings/view/FilterSettingsView.kt @@ -34,7 +34,6 @@ import solve.utils.materialfx.dialogHeaderLabel import solve.utils.materialfx.mfxCheckbox import solve.utils.materialfx.mfxIntegerRangeSlider import tornadofx.* -import kotlin.math.roundToInt import kotlin.math.roundToLong class FilterSettingsView : View() { diff --git a/src/main/kotlin/solve/filters/settings/view/controls/FilterSettingNodeConnector.kt b/src/main/kotlin/solve/filters/settings/view/controls/FilterSettingNodeConnector.kt index dba1f39e7..262ee7c6b 100644 --- a/src/main/kotlin/solve/filters/settings/view/controls/FilterSettingNodeConnector.kt +++ b/src/main/kotlin/solve/filters/settings/view/controls/FilterSettingNodeConnector.kt @@ -5,9 +5,9 @@ import solve.filters.settings.model.FilterSetting import kotlin.reflect.KClass import kotlin.reflect.cast -abstract class FilterSettingNodeConnector > ( - private val nodeKClass : KClass, - val filterSettingKCLass : KClass +abstract class FilterSettingNodeConnector> ( + private val nodeKClass: KClass, + val filterSettingKCLass: KClass ) { private fun isExpectedNodeType(settingNode: Node) = nodeKClass.isInstance(settingNode) diff --git a/src/main/kotlin/solve/filters/settings/view/controls/IndicesStepSettingNodeConnector.kt b/src/main/kotlin/solve/filters/settings/view/controls/IndicesStepSettingNodeConnector.kt index 803466406..f0c9d3b9a 100644 --- a/src/main/kotlin/solve/filters/settings/view/controls/IndicesStepSettingNodeConnector.kt +++ b/src/main/kotlin/solve/filters/settings/view/controls/IndicesStepSettingNodeConnector.kt @@ -7,7 +7,9 @@ object IndicesStepSettingNodeConnector : FilterSettingNodeConnector - if (item.enabled && ! selection.containsValue(item)) { + if (item.enabled && !selection.containsValue(item)) { filtersListView.selectionModel.selectItem(item) } else if (!item.enabled && selection.containsValue(item)) { filtersListView.selectionModel.deselectItem(item) diff --git a/src/main/kotlin/solve/main/MainController.kt b/src/main/kotlin/solve/main/MainController.kt index f5370030a..321f1bdda 100644 --- a/src/main/kotlin/solve/main/MainController.kt +++ b/src/main/kotlin/solve/main/MainController.kt @@ -2,7 +2,6 @@ package solve.main import solve.catalogue.controller.CatalogueController import solve.main.splitpane.SidePanelLocation -import solve.project.controller.ProjectController import solve.project.model.ProjectFrame import solve.project.model.ProjectLayer import solve.scene.SceneFacade From 7bcda612391fc886077363a696c4a42b1cbf3d79 Mon Sep 17 00:00:00 2001 From: Stanislav Mishchenko Date: Fri, 12 May 2023 11:15:41 +0300 Subject: [PATCH 16/36] Trying to fix MFXListView bugs --- .../controller/FilterPanelController.kt | 4 +- .../solve/filters/model/FilterPanelModel.kt | 2 +- .../controller/FilterSettingsController.kt | 6 +- .../filters/view/FilterPanelFieldsView.kt | 78 ++++++++----------- 4 files changed, 39 insertions(+), 51 deletions(-) diff --git a/src/main/kotlin/solve/filters/controller/FilterPanelController.kt b/src/main/kotlin/solve/filters/controller/FilterPanelController.kt index 476dbd006..f8ecce51b 100644 --- a/src/main/kotlin/solve/filters/controller/FilterPanelController.kt +++ b/src/main/kotlin/solve/filters/controller/FilterPanelController.kt @@ -21,8 +21,8 @@ class FilterPanelController : Controller() { model.removeFilter(filter) } - fun editFilter(oldFilter: Filter, editedFilter: Filter) { - model.replaceFilter(oldFilter, editedFilter) + fun editFilter(editingFilter: Filter, newFilter: Filter) { + model.replaceFilter(editingFilter, newFilter) } fun applyFilters() { diff --git a/src/main/kotlin/solve/filters/model/FilterPanelModel.kt b/src/main/kotlin/solve/filters/model/FilterPanelModel.kt index cfbf025cb..e3dd6fedc 100644 --- a/src/main/kotlin/solve/filters/model/FilterPanelModel.kt +++ b/src/main/kotlin/solve/filters/model/FilterPanelModel.kt @@ -5,7 +5,7 @@ import javafx.collections.ObservableList class FilterPanelModel { private val _filters = FXCollections.observableArrayList() - val filters: ObservableList = FXCollections.unmodifiableObservableList(_filters) + val filters: ObservableList = FXCollections.synchronizedObservableList(_filters) fun addFilter(filter: Filter) { _filters.add(filter) diff --git a/src/main/kotlin/solve/filters/settings/controller/FilterSettingsController.kt b/src/main/kotlin/solve/filters/settings/controller/FilterSettingsController.kt index d305fbf5b..64e08dc05 100644 --- a/src/main/kotlin/solve/filters/settings/controller/FilterSettingsController.kt +++ b/src/main/kotlin/solve/filters/settings/controller/FilterSettingsController.kt @@ -14,9 +14,9 @@ class FilterSettingsController : Controller() { } fun editFilter(editingFilter: Filter, newFilterSettings: List>) { - val editedFilter = Filter(newFilterSettings) - editedFilter.enabled = editingFilter.enabled + val newFilter = Filter(newFilterSettings) + newFilter.enabled = editingFilter.enabled - panelController.editFilter(editingFilter, editedFilter) + panelController.editFilter(editingFilter, newFilter) } } diff --git a/src/main/kotlin/solve/filters/view/FilterPanelFieldsView.kt b/src/main/kotlin/solve/filters/view/FilterPanelFieldsView.kt index c2a4df284..b2d76f308 100644 --- a/src/main/kotlin/solve/filters/view/FilterPanelFieldsView.kt +++ b/src/main/kotlin/solve/filters/view/FilterPanelFieldsView.kt @@ -6,7 +6,9 @@ import io.github.palexdev.materialfx.effects.DepthLevel import io.github.palexdev.mfxcore.utils.converters.FunctionalStringConverter import javafx.application.Platform import javafx.beans.binding.Bindings +import javafx.beans.property.SimpleIntegerProperty import javafx.collections.MapChangeListener +import javafx.scene.control.CheckBox import javafx.scene.control.Label import javafx.scene.text.Font import kotlinx.coroutines.CoroutineScope @@ -36,7 +38,7 @@ class FilterPanelFieldsView : View() { private val editIconImage = loadResourcesImage(IconsEditPath) private val deleteIconImage = loadResourcesImage(IconsDeletePath) - val filtersListView = mfxCheckListView(filterPanelController.model.filters) { + val filtersListView = mfxCheckListView(observableListOf()) { addStylesheet(FilterPanelFieldsViewStylesheet::class) converter = FunctionalStringConverter.to { it.preview } @@ -45,7 +47,7 @@ class FilterPanelFieldsView : View() { cellFactory = Function { val cell = MFXCheckListCell(this, it) - initializeCellGraphic(cell) + initializeCellGraphic(cell, it) return@Function cell } @@ -53,21 +55,12 @@ class FilterPanelFieldsView : View() { addFilterListViewBindings() paddingLeft = 1.5 - itemsProperty().onChange { - paddingBottom = if (items.isEmpty()) { - 0.0 - } else { - 14.0 - } - } useMaxWidth = true } override val root = filtersListView - private fun initializeCellGraphic(cell: MFXCheckListCell) { - val filter = cell.data - + private fun initializeCellGraphic(cell: MFXCheckListCell, filter: Filter) { val cellTextLabel = cell.getChildList()?.firstOrNull { child -> child is Label } cellTextLabel?.tooltip { text = filter.preview @@ -97,28 +90,9 @@ class FilterPanelFieldsView : View() { paddingRight = 20.5 } ) - } - - // Needed because mfx checkboxes are invisible until the first click and before a window hiding (mfx problem). - private fun forceCheckboxesVisualization() { - CoroutineScope(Dispatchers.JavaFx).launch { - delay(ListFieldSpawnTimeMillis) - - val visualizationForceNode = pane() - visualizationForceNode.isManaged = false - add(visualizationForceNode) - getChildList()?.remove(visualizationForceNode) - } - } - - private fun updateItemsSelection() { - val selection = filtersListView.selectionModel.selection - filtersListView.items.forEach { item -> - if (item.enabled && !selection.containsValue(item)) { - filtersListView.selectionModel.selectItem(item) - } else if (!item.enabled && selection.containsValue(item)) { - filtersListView.selectionModel.deselectItem(item) - } + Platform.runLater { + val cellCheckbox = (cell.getChildList()?.firstOrNull { it is CheckBox } as? CheckBox) ?: return@runLater + cellCheckbox.isSelected = filter.enabled } } @@ -126,28 +100,42 @@ class FilterPanelFieldsView : View() { val itemsNumberProperty = Bindings.size(items) prefHeightProperty().bind(itemsNumberProperty.multiply(35.0)) - items.onChange { - updateItemsSelection() - forceCheckboxesVisualization() - } - - Platform.runLater { currentWindow?.widthProperty()?.onChange { forceCheckboxesVisualization() } } - selectionModel.selectionProperty().addListener( MapChangeListener { change -> val filter = change.valueAdded ?: change.valueRemoved filter.enabled = change.wasAdded() - + filterPanelController.model.filters filterPanelController.applyFilters() } ) + + // Needed because mfx list view does not work correct in some cases. + // When an element is replaced in the filter list (deletion and addition), + // the mfx list skips the operation of deletion of a replaced element, + // but adds a new element and deletes the first element in the list. + // Also, mfx library fully reinitialize list during the addition and removing, + // so it causes calls of selectionProperty changes and checkboxes selection state is incorrect. + filterPanelController.model.filters.onChange { change -> + while (change.next()) { + CoroutineScope(Dispatchers.JavaFx).launch { + change.removed.forEach { + if (it.enabled && selectionModel.selection.containsValue(it)) { + selectionModel.deselectItem(it) + } + } + filtersListView.items.removeAll(change.removed) + + delay(DelayBetweenMFXListViewChangeOperationsMillis) + + filtersListView.items.addAll(change.addedSubList) + } + } + } } companion object { - private const val ListFieldSpawnTimeMillis = 500L - - private const val FieldLabelFontSize = 16.0 private const val FieldButtonsIconsSize = 24.0 private const val FieldButtonPressRippleCircleRadius = 15.0 + private const val DelayBetweenMFXListViewChangeOperationsMillis = 50L } } From 1a306fb8c017793b7457054e62152469d24865c3 Mon Sep 17 00:00:00 2001 From: Stanislav Mishchenko Date: Fri, 12 May 2023 11:17:04 +0300 Subject: [PATCH 17/36] Small fix --- .../solve/filters/settings/model/IndicesStepFilterSetting.kt | 2 +- src/main/kotlin/solve/filters/view/FilterPanelFieldsView.kt | 3 +-- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/src/main/kotlin/solve/filters/settings/model/IndicesStepFilterSetting.kt b/src/main/kotlin/solve/filters/settings/model/IndicesStepFilterSetting.kt index bf6dc23a0..26c856a41 100644 --- a/src/main/kotlin/solve/filters/settings/model/IndicesStepFilterSetting.kt +++ b/src/main/kotlin/solve/filters/settings/model/IndicesStepFilterSetting.kt @@ -6,7 +6,7 @@ class IndicesStepFilterSetting(step: Int) : FilterSetting { var step: Int = step private set - override fun apply(fields: List) = fields.slice(0..fields.lastIndex step this.step) + override fun apply(fields: List) = fields.slice(step..fields.lastIndex step this.step) override fun edit(newValue: Int) { step = newValue diff --git a/src/main/kotlin/solve/filters/view/FilterPanelFieldsView.kt b/src/main/kotlin/solve/filters/view/FilterPanelFieldsView.kt index b2d76f308..835a6c080 100644 --- a/src/main/kotlin/solve/filters/view/FilterPanelFieldsView.kt +++ b/src/main/kotlin/solve/filters/view/FilterPanelFieldsView.kt @@ -6,7 +6,6 @@ import io.github.palexdev.materialfx.effects.DepthLevel import io.github.palexdev.mfxcore.utils.converters.FunctionalStringConverter import javafx.application.Platform import javafx.beans.binding.Bindings -import javafx.beans.property.SimpleIntegerProperty import javafx.collections.MapChangeListener import javafx.scene.control.CheckBox import javafx.scene.control.Label @@ -38,7 +37,7 @@ class FilterPanelFieldsView : View() { private val editIconImage = loadResourcesImage(IconsEditPath) private val deleteIconImage = loadResourcesImage(IconsDeletePath) - val filtersListView = mfxCheckListView(observableListOf()) { + private val filtersListView = mfxCheckListView(observableListOf()) { addStylesheet(FilterPanelFieldsViewStylesheet::class) converter = FunctionalStringConverter.to { it.preview } From de30c33a4c7b93da513faf5a1dc0d2480ae12576 Mon Sep 17 00:00:00 2001 From: Stanislav Mishchenko Date: Fri, 12 May 2023 11:42:26 +0300 Subject: [PATCH 18/36] Fix validation bugs --- .../settings/view/FilterSettingsView.kt | 21 +++++++++++++++++-- 1 file changed, 19 insertions(+), 2 deletions(-) diff --git a/src/main/kotlin/solve/filters/settings/view/FilterSettingsView.kt b/src/main/kotlin/solve/filters/settings/view/FilterSettingsView.kt index 6d80c91f8..42ba6fe58 100644 --- a/src/main/kotlin/solve/filters/settings/view/FilterSettingsView.kt +++ b/src/main/kotlin/solve/filters/settings/view/FilterSettingsView.kt @@ -1,5 +1,6 @@ package solve.filters.settings.view +import io.github.palexdev.materialfx.controls.MFXTextField import io.github.palexdev.materialfx.enums.FloatMode import javafx.beans.InvalidationListener import javafx.beans.property.SimpleObjectProperty @@ -72,11 +73,11 @@ class FilterSettingsView : View() { private val selectedSettingCheckboxes = observableSetOf() private val areAllEnabledTextFieldsValid: Boolean - get() = validTextFields.toSet() == enabledTextFields.toSet() + get() = validTextFields.containsAll(enabledTextFields) private val haveSelectedCheckboxes: Boolean get() = selectedSettingCheckboxes.isNotEmpty() private val areAllEnabledTextFieldsNotEmpty: Boolean - get() = validTextFields.toSet() == notEmptyTextFields.toSet() + get() = notEmptyTextFields.containsAll(enabledTextFields) private val canCreateFilter: Boolean get() = haveSelectedCheckboxes && areAllEnabledTextFieldsValid && areAllEnabledTextFieldsNotEmpty @@ -112,6 +113,11 @@ class FilterSettingsView : View() { } fun updateDisableProperty() { + println(validTextFields) + println(enabledTextFields) + println(selectedSettingCheckboxes) + println(notEmptyTextFields) + isDisable = !canCreateFilter } @@ -147,6 +153,7 @@ class FilterSettingsView : View() { setDialogInitialState() setControlNodesSettingsFromFilter(editingModeOldFilter) enableControlNodesCheckboxesFromFilter(editingModeOldFilter) + initializeTextFieldListsFromFilter(editingModeOldFilter) initializeAndShowDialog(this) } @@ -189,6 +196,16 @@ class FilterSettingsView : View() { return nodeToNodeConnectorMap.getKeys(correspondingSettingControl).first() } + private fun initializeTextFieldListsFromFilter(filter: Filter) { + filter.settings.forEach { setting -> + val settingTextField = getNodeByFilterSetting(setting) as? TextField ?: return@forEach + + validTextFields.add(settingTextField) + enabledTextFields.add(settingTextField) + notEmptyTextFields.add(settingTextField) + } + } + private fun setControlNodesSettingsFromFilter(filter: Filter) { filter.settings.forEach { setting -> val correspondingNode = getNodeByFilterSetting(setting) From d7f80bf178f98df230ae957b5b3edd0d3d81768c Mon Sep 17 00:00:00 2001 From: Stanislav Mishchenko Date: Fri, 12 May 2023 11:43:43 +0300 Subject: [PATCH 19/36] Remove redundant code --- .../filters/settings/view/FilterSettingsView.kt | 16 ---------------- 1 file changed, 16 deletions(-) diff --git a/src/main/kotlin/solve/filters/settings/view/FilterSettingsView.kt b/src/main/kotlin/solve/filters/settings/view/FilterSettingsView.kt index 42ba6fe58..93b071c42 100644 --- a/src/main/kotlin/solve/filters/settings/view/FilterSettingsView.kt +++ b/src/main/kotlin/solve/filters/settings/view/FilterSettingsView.kt @@ -113,11 +113,6 @@ class FilterSettingsView : View() { } fun updateDisableProperty() { - println(validTextFields) - println(enabledTextFields) - println(selectedSettingCheckboxes) - println(notEmptyTextFields) - isDisable = !canCreateFilter } @@ -153,7 +148,6 @@ class FilterSettingsView : View() { setDialogInitialState() setControlNodesSettingsFromFilter(editingModeOldFilter) enableControlNodesCheckboxesFromFilter(editingModeOldFilter) - initializeTextFieldListsFromFilter(editingModeOldFilter) initializeAndShowDialog(this) } @@ -196,16 +190,6 @@ class FilterSettingsView : View() { return nodeToNodeConnectorMap.getKeys(correspondingSettingControl).first() } - private fun initializeTextFieldListsFromFilter(filter: Filter) { - filter.settings.forEach { setting -> - val settingTextField = getNodeByFilterSetting(setting) as? TextField ?: return@forEach - - validTextFields.add(settingTextField) - enabledTextFields.add(settingTextField) - notEmptyTextFields.add(settingTextField) - } - } - private fun setControlNodesSettingsFromFilter(filter: Filter) { filter.settings.forEach { setting -> val correspondingNode = getNodeByFilterSetting(setting) From f48d099677f20e7b9b60a7892f5a8b0e07625c17 Mon Sep 17 00:00:00 2001 From: Stanislav Mishchenko Date: Fri, 12 May 2023 11:44:17 +0300 Subject: [PATCH 20/36] Codestyle --- .../kotlin/solve/filters/settings/view/FilterSettingsView.kt | 1 - 1 file changed, 1 deletion(-) diff --git a/src/main/kotlin/solve/filters/settings/view/FilterSettingsView.kt b/src/main/kotlin/solve/filters/settings/view/FilterSettingsView.kt index 93b071c42..70cdb50da 100644 --- a/src/main/kotlin/solve/filters/settings/view/FilterSettingsView.kt +++ b/src/main/kotlin/solve/filters/settings/view/FilterSettingsView.kt @@ -1,6 +1,5 @@ package solve.filters.settings.view -import io.github.palexdev.materialfx.controls.MFXTextField import io.github.palexdev.materialfx.enums.FloatMode import javafx.beans.InvalidationListener import javafx.beans.property.SimpleObjectProperty From c412bb0e822d5db3fd4a664e705359b0b0320538 Mon Sep 17 00:00:00 2001 From: Stanislav Mishchenko Date: Fri, 12 May 2023 11:51:46 +0300 Subject: [PATCH 21/36] Small fix --- .../solve/filters/settings/model/IndicesStepFilterSetting.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/kotlin/solve/filters/settings/model/IndicesStepFilterSetting.kt b/src/main/kotlin/solve/filters/settings/model/IndicesStepFilterSetting.kt index 26c856a41..e7b165fa8 100644 --- a/src/main/kotlin/solve/filters/settings/model/IndicesStepFilterSetting.kt +++ b/src/main/kotlin/solve/filters/settings/model/IndicesStepFilterSetting.kt @@ -6,7 +6,7 @@ class IndicesStepFilterSetting(step: Int) : FilterSetting { var step: Int = step private set - override fun apply(fields: List) = fields.slice(step..fields.lastIndex step this.step) + override fun apply(fields: List) = fields.slice((step - 1)..fields.lastIndex step this.step) override fun edit(newValue: Int) { step = newValue From 9da6563380700f486de934c403cbdd9990837fcc Mon Sep 17 00:00:00 2001 From: Stanislav Mishchenko Date: Sun, 14 May 2023 17:55:19 +0300 Subject: [PATCH 22/36] Naming --- .../kotlin/solve/filters/controller/FilterPanelController.kt | 4 ++-- .../filters/settings/controller/FilterSettingsController.kt | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/main/kotlin/solve/filters/controller/FilterPanelController.kt b/src/main/kotlin/solve/filters/controller/FilterPanelController.kt index f8ecce51b..a2a0e734d 100644 --- a/src/main/kotlin/solve/filters/controller/FilterPanelController.kt +++ b/src/main/kotlin/solve/filters/controller/FilterPanelController.kt @@ -21,8 +21,8 @@ class FilterPanelController : Controller() { model.removeFilter(filter) } - fun editFilter(editingFilter: Filter, newFilter: Filter) { - model.replaceFilter(editingFilter, newFilter) + fun editFilter(editingFilter: Filter, editedFilter: Filter) { + model.replaceFilter(editingFilter, editedFilter) } fun applyFilters() { diff --git a/src/main/kotlin/solve/filters/settings/controller/FilterSettingsController.kt b/src/main/kotlin/solve/filters/settings/controller/FilterSettingsController.kt index 64e08dc05..41f6adc4c 100644 --- a/src/main/kotlin/solve/filters/settings/controller/FilterSettingsController.kt +++ b/src/main/kotlin/solve/filters/settings/controller/FilterSettingsController.kt @@ -13,8 +13,8 @@ class FilterSettingsController : Controller() { panelController.addFilter(filter) } - fun editFilter(editingFilter: Filter, newFilterSettings: List>) { - val newFilter = Filter(newFilterSettings) + fun editFilter(editingFilter: Filter, editedFilterSettings: List>) { + val newFilter = Filter(editedFilterSettings) newFilter.enabled = editingFilter.enabled panelController.editFilter(editingFilter, newFilter) From 52f5b6afdc3cee249cd74eb6948431b8dd6840d3 Mon Sep 17 00:00:00 2001 From: Stanislav Mishchenko Date: Sun, 14 May 2023 18:22:46 +0300 Subject: [PATCH 23/36] Refactor the filter setting class --- src/main/kotlin/solve/filters/model/Filter.kt | 6 +++--- .../settings/controller/FilterSettingsController.kt | 2 +- .../kotlin/solve/filters/settings/model/FilterSetting.kt | 9 ++++++--- .../filters/settings/model/IndicesStepFilterSetting.kt | 9 +++------ .../filters/settings/model/TimePeriodFilterSetting.kt | 9 +++------ .../solve/filters/settings/model/UIDFilterSetting.kt | 9 +++------ .../view/controls/IndicesStepSettingNodeConnector.kt | 2 +- .../view/controls/TimePeriodSettingNodeConnector.kt | 4 ++-- .../settings/view/controls/UIDSettingNodeConnector.kt | 2 +- 9 files changed, 23 insertions(+), 29 deletions(-) diff --git a/src/main/kotlin/solve/filters/model/Filter.kt b/src/main/kotlin/solve/filters/model/Filter.kt index 2f0bd75d7..269baa666 100644 --- a/src/main/kotlin/solve/filters/model/Filter.kt +++ b/src/main/kotlin/solve/filters/model/Filter.kt @@ -28,17 +28,17 @@ class Filter(val settings: List>) { val prefix = "Show " val indicesStepPart = if (indicesStepSetting != null) { - "every ${indicesStepSetting.step} image " + "every ${indicesStepSetting.settingValue} image " } else { "images " } val timePeriodPart = if (timePeriodSetting != null) { - "from ${timePeriodSetting.timePeriod.x} to ${timePeriodSetting.timePeriod.y} " + "from ${timePeriodSetting.settingValue.x} to ${timePeriodSetting.settingValue.y} " } else { "" } val uidPart = if (uidSetting != null) { - "with landmark ${uidSetting.uid}" + "with landmark ${uidSetting.settingValue}" } else { "" } diff --git a/src/main/kotlin/solve/filters/settings/controller/FilterSettingsController.kt b/src/main/kotlin/solve/filters/settings/controller/FilterSettingsController.kt index 41f6adc4c..cb665c01c 100644 --- a/src/main/kotlin/solve/filters/settings/controller/FilterSettingsController.kt +++ b/src/main/kotlin/solve/filters/settings/controller/FilterSettingsController.kt @@ -6,7 +6,7 @@ import solve.filters.settings.model.FilterSetting import tornadofx.* class FilterSettingsController : Controller() { - val panelController: FilterPanelController by inject() + private val panelController: FilterPanelController by inject() fun createFilter(filterSettings: List>) { val filter = Filter(filterSettings) diff --git a/src/main/kotlin/solve/filters/settings/model/FilterSetting.kt b/src/main/kotlin/solve/filters/settings/model/FilterSetting.kt index a13fa1f28..4fa232f4f 100644 --- a/src/main/kotlin/solve/filters/settings/model/FilterSetting.kt +++ b/src/main/kotlin/solve/filters/settings/model/FilterSetting.kt @@ -2,8 +2,11 @@ package solve.filters.settings.model import solve.project.model.ProjectFrame -interface FilterSetting { - fun apply(fields: List): List +abstract class FilterSetting(settingValue: T) { + var settingValue: T = settingValue + protected set - fun edit(newValue: T) + abstract fun apply(fields: List): List + + abstract fun edit(newValue: T) } diff --git a/src/main/kotlin/solve/filters/settings/model/IndicesStepFilterSetting.kt b/src/main/kotlin/solve/filters/settings/model/IndicesStepFilterSetting.kt index e7b165fa8..485f67799 100644 --- a/src/main/kotlin/solve/filters/settings/model/IndicesStepFilterSetting.kt +++ b/src/main/kotlin/solve/filters/settings/model/IndicesStepFilterSetting.kt @@ -2,13 +2,10 @@ package solve.filters.settings.model import solve.project.model.ProjectFrame -class IndicesStepFilterSetting(step: Int) : FilterSetting { - var step: Int = step - private set - - override fun apply(fields: List) = fields.slice((step - 1)..fields.lastIndex step this.step) +class IndicesStepFilterSetting(step: Int) : FilterSetting(step) { + override fun apply(fields: List) = fields.slice((settingValue - 1)..fields.lastIndex step this.settingValue) override fun edit(newValue: Int) { - step = newValue + settingValue = newValue } } diff --git a/src/main/kotlin/solve/filters/settings/model/TimePeriodFilterSetting.kt b/src/main/kotlin/solve/filters/settings/model/TimePeriodFilterSetting.kt index b560b334b..a28bb38c6 100644 --- a/src/main/kotlin/solve/filters/settings/model/TimePeriodFilterSetting.kt +++ b/src/main/kotlin/solve/filters/settings/model/TimePeriodFilterSetting.kt @@ -3,14 +3,11 @@ package solve.filters.settings.model import solve.project.model.ProjectFrame import solve.utils.structures.IntPoint -class TimePeriodFilterSetting(timePeriod: IntPoint) : FilterSetting { - var timePeriod: IntPoint = timePeriod - private set - +class TimePeriodFilterSetting(timePeriod: IntPoint) : FilterSetting(timePeriod) { override fun apply(fields: List): List = - fields.filter { it.timestamp in timePeriod.x..timePeriod.y } + fields.filter { it.timestamp in settingValue.x..settingValue.y } override fun edit(newValue: IntPoint) { - timePeriod = newValue + settingValue = newValue } } diff --git a/src/main/kotlin/solve/filters/settings/model/UIDFilterSetting.kt b/src/main/kotlin/solve/filters/settings/model/UIDFilterSetting.kt index 99e09cf42..a34a4b040 100644 --- a/src/main/kotlin/solve/filters/settings/model/UIDFilterSetting.kt +++ b/src/main/kotlin/solve/filters/settings/model/UIDFilterSetting.kt @@ -2,13 +2,10 @@ package solve.filters.settings.model import solve.project.model.ProjectFrame -class UIDFilterSetting(uid: Long) : FilterSetting { - var uid: Long = uid - private set - - override fun apply(fields: List) = fields.filter { it.uids.contains(uid) } +class UIDFilterSetting(uid: Long) : FilterSetting(uid) { + override fun apply(fields: List) = fields.filter { it.uids.contains(settingValue) } override fun edit(newValue: Long) { - uid = newValue + settingValue = newValue } } diff --git a/src/main/kotlin/solve/filters/settings/view/controls/IndicesStepSettingNodeConnector.kt b/src/main/kotlin/solve/filters/settings/view/controls/IndicesStepSettingNodeConnector.kt index f0c9d3b9a..a7805aa01 100644 --- a/src/main/kotlin/solve/filters/settings/view/controls/IndicesStepSettingNodeConnector.kt +++ b/src/main/kotlin/solve/filters/settings/view/controls/IndicesStepSettingNodeConnector.kt @@ -23,7 +23,7 @@ object IndicesStepSettingNodeConnector : FilterSettingNodeConnector Date: Sun, 14 May 2023 18:23:45 +0300 Subject: [PATCH 24/36] Codestyle --- .../solve/filters/settings/model/IndicesStepFilterSetting.kt | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/main/kotlin/solve/filters/settings/model/IndicesStepFilterSetting.kt b/src/main/kotlin/solve/filters/settings/model/IndicesStepFilterSetting.kt index 485f67799..a2d24ae59 100644 --- a/src/main/kotlin/solve/filters/settings/model/IndicesStepFilterSetting.kt +++ b/src/main/kotlin/solve/filters/settings/model/IndicesStepFilterSetting.kt @@ -3,7 +3,9 @@ package solve.filters.settings.model import solve.project.model.ProjectFrame class IndicesStepFilterSetting(step: Int) : FilterSetting(step) { - override fun apply(fields: List) = fields.slice((settingValue - 1)..fields.lastIndex step this.settingValue) + override fun apply(fields: List) = fields.slice( + (settingValue - 1)..fields.lastIndex step this.settingValue + ) override fun edit(newValue: Int) { settingValue = newValue From bdaf6793f290690ae47ad9f6d4909d4ef01ea6b7 Mon Sep 17 00:00:00 2001 From: Stanislav Mishchenko Date: Sun, 14 May 2023 18:31:23 +0300 Subject: [PATCH 25/36] Codestyle --- .../view/controls/FilterSettingNodeConnector.kt | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/main/kotlin/solve/filters/settings/view/controls/FilterSettingNodeConnector.kt b/src/main/kotlin/solve/filters/settings/view/controls/FilterSettingNodeConnector.kt index 262ee7c6b..d869163d1 100644 --- a/src/main/kotlin/solve/filters/settings/view/controls/FilterSettingNodeConnector.kt +++ b/src/main/kotlin/solve/filters/settings/view/controls/FilterSettingNodeConnector.kt @@ -9,10 +9,6 @@ abstract class FilterSettingNodeConnector> private val nodeKClass: KClass, val filterSettingKCLass: KClass ) { - private fun isExpectedNodeType(settingNode: Node) = nodeKClass.isInstance(settingNode) - - private fun castNodeToExpectedType(settingNode: Node) = nodeKClass.cast(settingNode) - fun extractFilterSettings(settingNode: Node): K? { if (!isExpectedNodeType(settingNode)) { return null @@ -21,8 +17,6 @@ abstract class FilterSettingNodeConnector> return extractFilterSettingsFromTypedSettingNode(castNodeToExpectedType(settingNode)) } - protected abstract fun extractFilterSettingsFromTypedSettingNode(settingNode: T): K? - fun updateSettingNodeWithSettings(settingNode: Node, setting: FilterSetting) { if (!isExpectedNodeType(settingNode) || !filterSettingKCLass.isInstance(setting)) { return @@ -34,8 +28,6 @@ abstract class FilterSettingNodeConnector> ) } - protected abstract fun updateTypedSettingNodeWithSettings(settingNode: T, setting: K) - fun setDefaultSettingNodeState(settingNode: Node) { if (!isExpectedNodeType(settingNode)) { return @@ -44,5 +36,13 @@ abstract class FilterSettingNodeConnector> return setDefaultTypedSettingNodeState(castNodeToExpectedType(settingNode)) } + protected abstract fun extractFilterSettingsFromTypedSettingNode(settingNode: T): K? + + protected abstract fun updateTypedSettingNodeWithSettings(settingNode: T, setting: K) + protected abstract fun setDefaultTypedSettingNodeState(settingNode: T) + + private fun isExpectedNodeType(settingNode: Node) = nodeKClass.isInstance(settingNode) + + private fun castNodeToExpectedType(settingNode: Node) = nodeKClass.cast(settingNode) } From 9651b95769c42baf98f1fa40a71cae547143e942 Mon Sep 17 00:00:00 2001 From: Stanislav Mishchenko Date: Tue, 16 May 2023 15:34:55 +0300 Subject: [PATCH 26/36] Codestyle --- .../solve/filters/settings/view/FilterSettingsView.kt | 8 ++++---- .../FilterSettingNodeConnector.kt | 2 +- .../IndicesStepSettingNodeConnector.kt | 2 +- .../TimePeriodSettingNodeConnector.kt | 2 +- .../{controls => connectors}/UIDSettingNodeConnector.kt | 2 +- .../visualization/popover/LayerSettingsPopOverNode.kt | 2 +- 6 files changed, 9 insertions(+), 9 deletions(-) rename src/main/kotlin/solve/filters/settings/view/{controls => connectors}/FilterSettingNodeConnector.kt (97%) rename src/main/kotlin/solve/filters/settings/view/{controls => connectors}/IndicesStepSettingNodeConnector.kt (95%) rename src/main/kotlin/solve/filters/settings/view/{controls => connectors}/TimePeriodSettingNodeConnector.kt (97%) rename src/main/kotlin/solve/filters/settings/view/{controls => connectors}/UIDSettingNodeConnector.kt (94%) diff --git a/src/main/kotlin/solve/filters/settings/view/FilterSettingsView.kt b/src/main/kotlin/solve/filters/settings/view/FilterSettingsView.kt index 70cdb50da..203b111cf 100644 --- a/src/main/kotlin/solve/filters/settings/view/FilterSettingsView.kt +++ b/src/main/kotlin/solve/filters/settings/view/FilterSettingsView.kt @@ -18,10 +18,10 @@ import org.controlsfx.control.RangeSlider import solve.filters.model.Filter import solve.filters.settings.controller.FilterSettingsController import solve.filters.settings.model.FilterSetting -import solve.filters.settings.view.controls.FilterSettingNodeConnector -import solve.filters.settings.view.controls.IndicesStepSettingNodeConnector -import solve.filters.settings.view.controls.TimePeriodSettingNodeConnector -import solve.filters.settings.view.controls.UIDSettingNodeConnector +import solve.filters.settings.view.connectors.FilterSettingNodeConnector +import solve.filters.settings.view.connectors.IndicesStepSettingNodeConnector +import solve.filters.settings.view.connectors.TimePeriodSettingNodeConnector +import solve.filters.settings.view.connectors.UIDSettingNodeConnector import solve.styles.Style import solve.styles.Style.headerPadding import solve.utils.createHGrowHBox diff --git a/src/main/kotlin/solve/filters/settings/view/controls/FilterSettingNodeConnector.kt b/src/main/kotlin/solve/filters/settings/view/connectors/FilterSettingNodeConnector.kt similarity index 97% rename from src/main/kotlin/solve/filters/settings/view/controls/FilterSettingNodeConnector.kt rename to src/main/kotlin/solve/filters/settings/view/connectors/FilterSettingNodeConnector.kt index d869163d1..97e6f24c7 100644 --- a/src/main/kotlin/solve/filters/settings/view/controls/FilterSettingNodeConnector.kt +++ b/src/main/kotlin/solve/filters/settings/view/connectors/FilterSettingNodeConnector.kt @@ -1,4 +1,4 @@ -package solve.filters.settings.view.controls +package solve.filters.settings.view.connectors import javafx.scene.Node import solve.filters.settings.model.FilterSetting diff --git a/src/main/kotlin/solve/filters/settings/view/controls/IndicesStepSettingNodeConnector.kt b/src/main/kotlin/solve/filters/settings/view/connectors/IndicesStepSettingNodeConnector.kt similarity index 95% rename from src/main/kotlin/solve/filters/settings/view/controls/IndicesStepSettingNodeConnector.kt rename to src/main/kotlin/solve/filters/settings/view/connectors/IndicesStepSettingNodeConnector.kt index a7805aa01..cdf40d7f5 100644 --- a/src/main/kotlin/solve/filters/settings/view/controls/IndicesStepSettingNodeConnector.kt +++ b/src/main/kotlin/solve/filters/settings/view/connectors/IndicesStepSettingNodeConnector.kt @@ -1,4 +1,4 @@ -package solve.filters.settings.view.controls +package solve.filters.settings.view.connectors import solve.filters.settings.model.IndicesStepFilterSetting import solve.utils.materialfx.MFXIntegerTextField diff --git a/src/main/kotlin/solve/filters/settings/view/controls/TimePeriodSettingNodeConnector.kt b/src/main/kotlin/solve/filters/settings/view/connectors/TimePeriodSettingNodeConnector.kt similarity index 97% rename from src/main/kotlin/solve/filters/settings/view/controls/TimePeriodSettingNodeConnector.kt rename to src/main/kotlin/solve/filters/settings/view/connectors/TimePeriodSettingNodeConnector.kt index 718ca9148..dbf0d4d92 100644 --- a/src/main/kotlin/solve/filters/settings/view/controls/TimePeriodSettingNodeConnector.kt +++ b/src/main/kotlin/solve/filters/settings/view/connectors/TimePeriodSettingNodeConnector.kt @@ -1,4 +1,4 @@ -package solve.filters.settings.view.controls +package solve.filters.settings.view.connectors import org.controlsfx.control.RangeSlider import solve.filters.settings.model.TimePeriodFilterSetting diff --git a/src/main/kotlin/solve/filters/settings/view/controls/UIDSettingNodeConnector.kt b/src/main/kotlin/solve/filters/settings/view/connectors/UIDSettingNodeConnector.kt similarity index 94% rename from src/main/kotlin/solve/filters/settings/view/controls/UIDSettingNodeConnector.kt rename to src/main/kotlin/solve/filters/settings/view/connectors/UIDSettingNodeConnector.kt index ced68aa46..18fca11ba 100644 --- a/src/main/kotlin/solve/filters/settings/view/controls/UIDSettingNodeConnector.kt +++ b/src/main/kotlin/solve/filters/settings/view/connectors/UIDSettingNodeConnector.kt @@ -1,4 +1,4 @@ -package solve.filters.settings.view.controls +package solve.filters.settings.view.connectors import solve.filters.settings.model.UIDFilterSetting import solve.utils.materialfx.MFXIntegerTextField diff --git a/src/main/kotlin/solve/settings/visualization/popover/LayerSettingsPopOverNode.kt b/src/main/kotlin/solve/settings/visualization/popover/LayerSettingsPopOverNode.kt index 04588f6e6..dc636de1c 100644 --- a/src/main/kotlin/solve/settings/visualization/popover/LayerSettingsPopOverNode.kt +++ b/src/main/kotlin/solve/settings/visualization/popover/LayerSettingsPopOverNode.kt @@ -11,7 +11,7 @@ import tornadofx.* abstract class LayerSettingsPopOverNode { protected val popOver: VBox = VBox() - fun addSettingField(name: String, settingNode: Node, settingsNodeAlignment: Alignment = Alignment.Center) { + protected fun addSettingField(name: String, settingNode: Node, settingsNodeAlignment: Alignment = Alignment.Center) { val fieldLabel = Label(name) fieldLabel.font = Font.font(LayerSettingLabelFontSize) fieldLabel.paddingLeft = LayerSettingsLabelPaddingLeft From 28e909a9eb01f401142403d0d3b67dc91172f70f Mon Sep 17 00:00:00 2001 From: Stanislav Mishchenko Date: Tue, 16 May 2023 15:57:33 +0300 Subject: [PATCH 27/36] Fix bug with empty catalogue after a removing all filters --- .../solve/filters/controller/FilterPanelController.kt | 5 ++++- src/main/kotlin/solve/filters/view/FilterPanelFieldsView.kt | 6 ++++-- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/src/main/kotlin/solve/filters/controller/FilterPanelController.kt b/src/main/kotlin/solve/filters/controller/FilterPanelController.kt index a2a0e734d..faf604fcd 100644 --- a/src/main/kotlin/solve/filters/controller/FilterPanelController.kt +++ b/src/main/kotlin/solve/filters/controller/FilterPanelController.kt @@ -33,8 +33,11 @@ class FilterPanelController : Controller() { private fun getFilteredProjectFrames(): List { val projectFrames = projectController.model.project.frames - val enabledFilters = model.filters.filter { it.enabled } + if (model.filters.isEmpty()) { + 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] } diff --git a/src/main/kotlin/solve/filters/view/FilterPanelFieldsView.kt b/src/main/kotlin/solve/filters/view/FilterPanelFieldsView.kt index 835a6c080..bdc226577 100644 --- a/src/main/kotlin/solve/filters/view/FilterPanelFieldsView.kt +++ b/src/main/kotlin/solve/filters/view/FilterPanelFieldsView.kt @@ -99,12 +99,14 @@ class FilterPanelFieldsView : View() { val itemsNumberProperty = Bindings.size(items) prefHeightProperty().bind(itemsNumberProperty.multiply(35.0)) + filterPanelController.model.filters.onChange { + filterPanelController.applyFilters() + } + selectionModel.selectionProperty().addListener( MapChangeListener { change -> val filter = change.valueAdded ?: change.valueRemoved filter.enabled = change.wasAdded() - filterPanelController.model.filters - filterPanelController.applyFilters() } ) From 5d5dffae64ab474c11cc8d457ce0179fc4b30fec Mon Sep 17 00:00:00 2001 From: Stanislav Mishchenko Date: Tue, 16 May 2023 16:01:46 +0300 Subject: [PATCH 28/36] Keep text if textfield is empty --- .../kotlin/solve/filters/settings/view/FilterSettingsView.kt | 1 - 1 file changed, 1 deletion(-) diff --git a/src/main/kotlin/solve/filters/settings/view/FilterSettingsView.kt b/src/main/kotlin/solve/filters/settings/view/FilterSettingsView.kt index 203b111cf..88f6933dc 100644 --- a/src/main/kotlin/solve/filters/settings/view/FilterSettingsView.kt +++ b/src/main/kotlin/solve/filters/settings/view/FilterSettingsView.kt @@ -226,7 +226,6 @@ class FilterSettingsView : View() { disableProperty().onChange { isDisable -> if (isDisable) { - text = "" isAllowEdit = false enabledTextFields.remove(this) } else { From e18552b80ee1fa3dc3b8ff7502b8ba1d491631ec Mon Sep 17 00:00:00 2001 From: Stanislav Mishchenko Date: Tue, 16 May 2023 16:20:34 +0300 Subject: [PATCH 29/36] Apply filters when reimport a project --- .../filters/controller/FilterPanelController.kt | 15 ++++++++++++++- .../solve/filters/view/FilterPanelFieldsView.kt | 7 +++---- 2 files changed, 17 insertions(+), 5 deletions(-) diff --git a/src/main/kotlin/solve/filters/controller/FilterPanelController.kt b/src/main/kotlin/solve/filters/controller/FilterPanelController.kt index faf604fcd..0ba825177 100644 --- a/src/main/kotlin/solve/filters/controller/FilterPanelController.kt +++ b/src/main/kotlin/solve/filters/controller/FilterPanelController.kt @@ -13,6 +13,10 @@ class FilterPanelController : Controller() { private val projectController: ProjectController by inject() private val catalogueController: CatalogueController by inject() + init { + addBindings() + } + fun addFilter(filter: Filter) { model.addFilter(filter) } @@ -25,7 +29,16 @@ class FilterPanelController : Controller() { model.replaceFilter(editingFilter, editedFilter) } - fun applyFilters() { + private fun addBindings() { + projectController.model.projectProperty.onChange { + applyFilters() + } + model.filters.onChange { + applyFilters() + } + } + + private fun applyFilters() { val filteredFrames = getFilteredProjectFrames() catalogueController.setCatalogueFrames(filteredFrames) } diff --git a/src/main/kotlin/solve/filters/view/FilterPanelFieldsView.kt b/src/main/kotlin/solve/filters/view/FilterPanelFieldsView.kt index bdc226577..740b8b445 100644 --- a/src/main/kotlin/solve/filters/view/FilterPanelFieldsView.kt +++ b/src/main/kotlin/solve/filters/view/FilterPanelFieldsView.kt @@ -20,6 +20,9 @@ import solve.constants.IconsEditPath import solve.filters.controller.FilterPanelController import solve.filters.model.Filter import solve.filters.settings.view.FilterSettingsView +import solve.project.controller.ProjectController +import solve.scene.SceneFacade +import solve.scene.controller.SceneController import solve.styles.FilterPanelFieldsViewStylesheet import solve.styles.Style import solve.utils.createHGrowHBox @@ -99,10 +102,6 @@ class FilterPanelFieldsView : View() { val itemsNumberProperty = Bindings.size(items) prefHeightProperty().bind(itemsNumberProperty.multiply(35.0)) - filterPanelController.model.filters.onChange { - filterPanelController.applyFilters() - } - selectionModel.selectionProperty().addListener( MapChangeListener { change -> val filter = change.valueAdded ?: change.valueRemoved From 7b082355f2b28091e5fc2f824b47011a4e739f96 Mon Sep 17 00:00:00 2001 From: Stanislav Mishchenko Date: Tue, 16 May 2023 17:16:34 +0300 Subject: [PATCH 30/36] Fix bug with not working uids filter setting --- src/main/kotlin/solve/importer/FullParserForImport.kt | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/src/main/kotlin/solve/importer/FullParserForImport.kt b/src/main/kotlin/solve/importer/FullParserForImport.kt index 68a18f600..2b1103f46 100644 --- a/src/main/kotlin/solve/importer/FullParserForImport.kt +++ b/src/main/kotlin/solve/importer/FullParserForImport.kt @@ -1,8 +1,10 @@ package solve.importer import solve.importer.model.ProjectAfterPartialParsing +import solve.parsers.lines.CSVLinesParser import solve.parsers.planes.ImagePlanesParser import solve.project.model.LandmarkFile +import solve.project.model.LayerKind import solve.project.model.Project import solve.project.model.ProjectFrame import solve.project.model.ProjectLayer @@ -28,8 +30,13 @@ object FullParserForImport { if (!layers.contains(currentLayer)) { layers.add((currentLayer)) } + val uids = when(output.kind) { + LayerKind.Keypoint -> CSVLinesParser.extractUIDs(output.path) + LayerKind.Line -> CSVLinesParser.extractUIDs(output.path) + LayerKind.Plane -> ImagePlanesParser.extractUIDs(output.path) + } landmarks[longName]?.add( - LandmarkFile(currentLayer, Path(output.path), ImagePlanesParser.extractUIDs(output.path)) + LandmarkFile(currentLayer, Path(output.path), uids) ) } landmarks[longName]?.toList() From 0cff312f54ca8b067f243c013b2f505e893d6172 Mon Sep 17 00:00:00 2001 From: Stanislav Mishchenko Date: Tue, 16 May 2023 17:19:11 +0300 Subject: [PATCH 31/36] Codestyle --- src/main/kotlin/solve/filters/view/FilterPanelFieldsView.kt | 3 --- src/main/kotlin/solve/importer/FullParserForImport.kt | 2 +- .../visualization/popover/LayerSettingsPopOverNode.kt | 6 +++++- 3 files changed, 6 insertions(+), 5 deletions(-) diff --git a/src/main/kotlin/solve/filters/view/FilterPanelFieldsView.kt b/src/main/kotlin/solve/filters/view/FilterPanelFieldsView.kt index 740b8b445..cde3fed55 100644 --- a/src/main/kotlin/solve/filters/view/FilterPanelFieldsView.kt +++ b/src/main/kotlin/solve/filters/view/FilterPanelFieldsView.kt @@ -20,9 +20,6 @@ import solve.constants.IconsEditPath import solve.filters.controller.FilterPanelController import solve.filters.model.Filter import solve.filters.settings.view.FilterSettingsView -import solve.project.controller.ProjectController -import solve.scene.SceneFacade -import solve.scene.controller.SceneController import solve.styles.FilterPanelFieldsViewStylesheet import solve.styles.Style import solve.utils.createHGrowHBox diff --git a/src/main/kotlin/solve/importer/FullParserForImport.kt b/src/main/kotlin/solve/importer/FullParserForImport.kt index 2b1103f46..034958837 100644 --- a/src/main/kotlin/solve/importer/FullParserForImport.kt +++ b/src/main/kotlin/solve/importer/FullParserForImport.kt @@ -30,7 +30,7 @@ object FullParserForImport { if (!layers.contains(currentLayer)) { layers.add((currentLayer)) } - val uids = when(output.kind) { + val uids = when (output.kind) { LayerKind.Keypoint -> CSVLinesParser.extractUIDs(output.path) LayerKind.Line -> CSVLinesParser.extractUIDs(output.path) LayerKind.Plane -> ImagePlanesParser.extractUIDs(output.path) diff --git a/src/main/kotlin/solve/settings/visualization/popover/LayerSettingsPopOverNode.kt b/src/main/kotlin/solve/settings/visualization/popover/LayerSettingsPopOverNode.kt index dc636de1c..b7225849c 100644 --- a/src/main/kotlin/solve/settings/visualization/popover/LayerSettingsPopOverNode.kt +++ b/src/main/kotlin/solve/settings/visualization/popover/LayerSettingsPopOverNode.kt @@ -11,7 +11,11 @@ import tornadofx.* abstract class LayerSettingsPopOverNode { protected val popOver: VBox = VBox() - protected fun addSettingField(name: String, settingNode: Node, settingsNodeAlignment: Alignment = Alignment.Center) { + protected fun addSettingField( + name: String, + settingNode: Node, + settingsNodeAlignment: Alignment = Alignment.Center + ) { val fieldLabel = Label(name) fieldLabel.font = Font.font(LayerSettingLabelFontSize) fieldLabel.paddingLeft = LayerSettingsLabelPaddingLeft From 953a44f6a2aae545df3c163d0b676549030ac178 Mon Sep 17 00:00:00 2001 From: Stanislav Mishchenko Date: Tue, 16 May 2023 17:30:22 +0300 Subject: [PATCH 32/36] Fix bug with two layer visibility buttons --- .../view/VisualizationSettingsLayerCell.kt | 28 +++++++++++-------- 1 file changed, 16 insertions(+), 12 deletions(-) diff --git a/src/main/kotlin/solve/settings/visualization/fields/view/VisualizationSettingsLayerCell.kt b/src/main/kotlin/solve/settings/visualization/fields/view/VisualizationSettingsLayerCell.kt index 413863d8c..eae99d238 100644 --- a/src/main/kotlin/solve/settings/visualization/fields/view/VisualizationSettingsLayerCell.kt +++ b/src/main/kotlin/solve/settings/visualization/fields/view/VisualizationSettingsLayerCell.kt @@ -68,7 +68,7 @@ class VisualizationSettingsLayerCell( if (layerType != LandmarkType.Plane) { add(createLayerEditButton(layerType)) } - add(createLayerVisibilityButton()) + add(createLayerVisibilityButtonNode() ?: return@hbox) alignment = Pos.CENTER_LEFT paddingRight = LayerFieldHBoxPaddingRight @@ -131,24 +131,28 @@ class VisualizationSettingsLayerCell( alignment = Pos.CENTER_RIGHT } - private fun createLayerVisibilityButton(): Node = hbox { - layerVisibleIconImage ?: return@hbox - layerInvisibleIconImage ?: return@hbox + private fun createLayerVisibilityButtonNode(): Node? { + layerVisibleIconImage ?: return null + layerInvisibleIconImage ?: return null val layerVisibleImageViewIcon = imageViewIcon(layerVisibleIconImage, LayerVisibilityIconSize) val layerInvisibleImageViewIcon = imageViewIcon(layerInvisibleIconImage, LayerVisibilityIconSize) - fun getCurrentVisibilityImageViewIcon() = - if (item.enabled) layerVisibleImageViewIcon else layerInvisibleImageViewIcon + val layerVisibilityButtonNode = hbox { + fun getCurrentVisibilityImageViewIcon() = + if (item.enabled) layerVisibleImageViewIcon else layerInvisibleImageViewIcon - button { - graphic = getCurrentVisibilityImageViewIcon() - action { - item.enabled = !item.enabled + button { graphic = getCurrentVisibilityImageViewIcon() + action { + item.enabled = !item.enabled + graphic = getCurrentVisibilityImageViewIcon() + } } + alignment = Pos.CENTER_RIGHT + paddingLeft = LayerVisibilityIconPaddingLeft } - alignment = Pos.CENTER_RIGHT - paddingLeft = LayerVisibilityIconPaddingLeft + + return layerVisibilityButtonNode } private fun calculatePopOverShowPosition(spawnNode: Node, layerType: LandmarkType): DoublePoint { From cd0342a77ba8a3494b0edee1a3c657a274490635 Mon Sep 17 00:00:00 2001 From: Stanislav Mishchenko Date: Tue, 16 May 2023 22:23:32 +0300 Subject: [PATCH 33/36] Small fix --- .../solve/filters/controller/FilterPanelController.kt | 10 +++++----- .../kotlin/solve/filters/view/FilterPanelFieldsView.kt | 1 + 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/src/main/kotlin/solve/filters/controller/FilterPanelController.kt b/src/main/kotlin/solve/filters/controller/FilterPanelController.kt index 0ba825177..cf83ba80c 100644 --- a/src/main/kotlin/solve/filters/controller/FilterPanelController.kt +++ b/src/main/kotlin/solve/filters/controller/FilterPanelController.kt @@ -29,6 +29,11 @@ class FilterPanelController : Controller() { model.replaceFilter(editingFilter, editedFilter) } + fun applyFilters() { + val filteredFrames = getFilteredProjectFrames() + catalogueController.setCatalogueFrames(filteredFrames) + } + private fun addBindings() { projectController.model.projectProperty.onChange { applyFilters() @@ -38,11 +43,6 @@ class FilterPanelController : Controller() { } } - private fun applyFilters() { - val filteredFrames = getFilteredProjectFrames() - catalogueController.setCatalogueFrames(filteredFrames) - } - private fun getFilteredProjectFrames(): List { val projectFrames = projectController.model.project.frames diff --git a/src/main/kotlin/solve/filters/view/FilterPanelFieldsView.kt b/src/main/kotlin/solve/filters/view/FilterPanelFieldsView.kt index cde3fed55..f0c58fc9e 100644 --- a/src/main/kotlin/solve/filters/view/FilterPanelFieldsView.kt +++ b/src/main/kotlin/solve/filters/view/FilterPanelFieldsView.kt @@ -103,6 +103,7 @@ class FilterPanelFieldsView : View() { MapChangeListener { change -> val filter = change.valueAdded ?: change.valueRemoved filter.enabled = change.wasAdded() + filterPanelController.applyFilters() } ) From 802c07fddbfecf625f5535f18ab9b0d1b7f2304a Mon Sep 17 00:00:00 2001 From: Stanislav Mishchenko Date: Wed, 17 May 2023 22:28:04 +0300 Subject: [PATCH 34/36] Fix bug with not working dragged catalogue files image --- .../catalogue/view/fields/CatalogueFileNamesFieldsView.kt | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/main/kotlin/solve/catalogue/view/fields/CatalogueFileNamesFieldsView.kt b/src/main/kotlin/solve/catalogue/view/fields/CatalogueFileNamesFieldsView.kt index 5e6997e72..0b469f769 100644 --- a/src/main/kotlin/solve/catalogue/view/fields/CatalogueFileNamesFieldsView.kt +++ b/src/main/kotlin/solve/catalogue/view/fields/CatalogueFileNamesFieldsView.kt @@ -2,8 +2,10 @@ package solve.catalogue.view.fields import javafx.scene.control.Labeled import javafx.scene.image.ImageView +import javafx.scene.text.Font import solve.catalogue.model.CatalogueField import solve.constants.IconsCatalogueImagePath +import solve.styles.Style import solve.utils.loadResourcesImage import tornadofx.* @@ -44,7 +46,9 @@ class CatalogueFileNamesFieldsView : CatalogueFieldsView() { if (iconImageView != null) { add(iconImageView) } - label(it.fileName) + text(it.fileName) { + font = Font.font(Style.Font, 14.0) + } } } } From 5ca3090d36c4b481b4f99e98dbf6dac6a86b9461 Mon Sep 17 00:00:00 2001 From: Mishchenko Stanislav Date: Sun, 18 Jun 2023 18:31:25 +0300 Subject: [PATCH 35/36] Fix an all filters are disabled bug --- .../kotlin/solve/filters/controller/FilterPanelController.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/kotlin/solve/filters/controller/FilterPanelController.kt b/src/main/kotlin/solve/filters/controller/FilterPanelController.kt index cf83ba80c..8d68a0b01 100644 --- a/src/main/kotlin/solve/filters/controller/FilterPanelController.kt +++ b/src/main/kotlin/solve/filters/controller/FilterPanelController.kt @@ -46,7 +46,7 @@ class FilterPanelController : Controller() { private fun getFilteredProjectFrames(): List { val projectFrames = projectController.model.project.frames - if (model.filters.isEmpty()) { + if (model.filters.isEmpty() || model.filters.all { !it.enabled }) { return projectFrames } From 0a0bf4d0c6de4d2d926bc42fa30872a6bb9f6c33 Mon Sep 17 00:00:00 2001 From: Mishchenko Stanislav Date: Sun, 18 Jun 2023 18:59:42 +0300 Subject: [PATCH 36/36] Add logic for a saving unchecked settings --- .../solve/filters/settings/model/FilterSetting.kt | 10 +++++++++- .../settings/model/IndicesStepFilterSetting.kt | 2 +- .../settings/model/TimePeriodFilterSetting.kt | 2 +- .../filters/settings/model/UIDFilterSetting.kt | 2 +- .../filters/settings/view/FilterSettingsView.kt | 13 ++++++------- .../view/connectors/FilterSettingNodeConnector.kt | 7 +++++-- 6 files changed, 23 insertions(+), 13 deletions(-) diff --git a/src/main/kotlin/solve/filters/settings/model/FilterSetting.kt b/src/main/kotlin/solve/filters/settings/model/FilterSetting.kt index 4fa232f4f..b2a61ef24 100644 --- a/src/main/kotlin/solve/filters/settings/model/FilterSetting.kt +++ b/src/main/kotlin/solve/filters/settings/model/FilterSetting.kt @@ -3,10 +3,18 @@ package solve.filters.settings.model import solve.project.model.ProjectFrame abstract class FilterSetting(settingValue: T) { + var enabled: Boolean = true + var settingValue: T = settingValue protected set - abstract fun apply(fields: List): List + fun apply(fields: List) : List { + if (!enabled) + return fields; + + return applySetting(fields) + } + protected abstract fun applySetting(fields: List): List abstract fun edit(newValue: T) } diff --git a/src/main/kotlin/solve/filters/settings/model/IndicesStepFilterSetting.kt b/src/main/kotlin/solve/filters/settings/model/IndicesStepFilterSetting.kt index a2d24ae59..344b9a3db 100644 --- a/src/main/kotlin/solve/filters/settings/model/IndicesStepFilterSetting.kt +++ b/src/main/kotlin/solve/filters/settings/model/IndicesStepFilterSetting.kt @@ -3,7 +3,7 @@ package solve.filters.settings.model import solve.project.model.ProjectFrame class IndicesStepFilterSetting(step: Int) : FilterSetting(step) { - override fun apply(fields: List) = fields.slice( + override fun applySetting(fields: List) = fields.slice( (settingValue - 1)..fields.lastIndex step this.settingValue ) diff --git a/src/main/kotlin/solve/filters/settings/model/TimePeriodFilterSetting.kt b/src/main/kotlin/solve/filters/settings/model/TimePeriodFilterSetting.kt index a28bb38c6..133a92310 100644 --- a/src/main/kotlin/solve/filters/settings/model/TimePeriodFilterSetting.kt +++ b/src/main/kotlin/solve/filters/settings/model/TimePeriodFilterSetting.kt @@ -4,7 +4,7 @@ import solve.project.model.ProjectFrame import solve.utils.structures.IntPoint class TimePeriodFilterSetting(timePeriod: IntPoint) : FilterSetting(timePeriod) { - override fun apply(fields: List): List = + override fun applySetting(fields: List): List = fields.filter { it.timestamp in settingValue.x..settingValue.y } override fun edit(newValue: IntPoint) { diff --git a/src/main/kotlin/solve/filters/settings/model/UIDFilterSetting.kt b/src/main/kotlin/solve/filters/settings/model/UIDFilterSetting.kt index a34a4b040..a53b29bcc 100644 --- a/src/main/kotlin/solve/filters/settings/model/UIDFilterSetting.kt +++ b/src/main/kotlin/solve/filters/settings/model/UIDFilterSetting.kt @@ -3,7 +3,7 @@ package solve.filters.settings.model import solve.project.model.ProjectFrame class UIDFilterSetting(uid: Long) : FilterSetting(uid) { - override fun apply(fields: List) = fields.filter { it.uids.contains(settingValue) } + override fun applySetting(fields: List) = fields.filter { it.uids.contains(settingValue) } override fun edit(newValue: Long) { settingValue = newValue diff --git a/src/main/kotlin/solve/filters/settings/view/FilterSettingsView.kt b/src/main/kotlin/solve/filters/settings/view/FilterSettingsView.kt index 88f6933dc..7685afb6d 100644 --- a/src/main/kotlin/solve/filters/settings/view/FilterSettingsView.kt +++ b/src/main/kotlin/solve/filters/settings/view/FilterSettingsView.kt @@ -201,7 +201,7 @@ class FilterSettingsView : View() { private fun enableControlNodesCheckboxesFromFilter(filter: Filter) { filter.settings.forEach { setting -> val correspondingNode = getNodeByFilterSetting(setting) - checkboxToSettingNodeMap.getKeys(correspondingNode).first().isSelected = true + checkboxToSettingNodeMap.getKeys(correspondingNode).first().isSelected = setting.enabled } } @@ -381,17 +381,16 @@ class FilterSettingsView : View() { ) } - private fun getFilterSettings(): List> { - val enabledSettingNodes = selectedSettingCheckboxes.map { checkboxToSettingNodeMap[it] } - - return enabledSettingNodes.mapNotNull { settingNode -> + private fun getFilterSettings(): List> = + settingCheckboxes.mapNotNull { settingCheckbox -> + val settingNode = checkboxToSettingNodeMap[settingCheckbox] settingNode ?: return@mapNotNull null val correspondingControl = nodeToNodeConnectorMap[settingNode] + val enabledSetting = settingCheckbox.isSelected - return@mapNotNull correspondingControl?.extractFilterSettings(settingNode) + return@mapNotNull correspondingControl?.extractFilterSettings(settingNode, enabledSetting) } - } companion object { private const val IntegerTextFieldSymbolsLimit = Long.MIN_VALUE.toString().length diff --git a/src/main/kotlin/solve/filters/settings/view/connectors/FilterSettingNodeConnector.kt b/src/main/kotlin/solve/filters/settings/view/connectors/FilterSettingNodeConnector.kt index 97e6f24c7..aa1b169bd 100644 --- a/src/main/kotlin/solve/filters/settings/view/connectors/FilterSettingNodeConnector.kt +++ b/src/main/kotlin/solve/filters/settings/view/connectors/FilterSettingNodeConnector.kt @@ -9,12 +9,15 @@ abstract class FilterSettingNodeConnector> private val nodeKClass: KClass, val filterSettingKCLass: KClass ) { - fun extractFilterSettings(settingNode: Node): K? { + fun extractFilterSettings(settingNode: Node, enabled: Boolean): K? { if (!isExpectedNodeType(settingNode)) { return null } - return extractFilterSettingsFromTypedSettingNode(castNodeToExpectedType(settingNode)) + val filterSetting = extractFilterSettingsFromTypedSettingNode(castNodeToExpectedType(settingNode)) + filterSetting?.enabled = enabled + + return filterSetting } fun updateSettingNodeWithSettings(settingNode: Node, setting: FilterSetting) {