diff --git a/ganttproject/src/main/java/biz/ganttproject/ganttview/ColumnManager.kt b/ganttproject/src/main/java/biz/ganttproject/ganttview/ColumnManager.kt index b9b8f6d32d..051f2ba434 100644 --- a/ganttproject/src/main/java/biz/ganttproject/ganttview/ColumnManager.kt +++ b/ganttproject/src/main/java/biz/ganttproject/ganttview/ColumnManager.kt @@ -25,6 +25,7 @@ import biz.ganttproject.core.table.ColumnList import biz.ganttproject.customproperty.* import biz.ganttproject.core.option.Completion import biz.ganttproject.lib.fx.VBoxBuilder +import biz.ganttproject.lib.fx.createToggleSwitch import biz.ganttproject.lib.fx.vbox import de.jensd.fx.glyphs.materialicons.MaterialIcon import de.jensd.fx.glyphs.materialicons.MaterialIconView @@ -99,19 +100,20 @@ class ColumnManager( // and they are ordered the same way are shown in the table. listItems.setAll(mergedColumns.sortedWith { col1, col2 -> columnsOrder(col1, col2) }.map { col -> val isCustom = customColumnsManager.definitions.find { it.id == col.id } != null - ColumnAsListItem(col, col.isVisible, isCustom, customColumnsManager) + ColumnAsListItem(col, col.isVisible, isCustom, customColumnsManager, customPropertyEditor::updateVisibility) }) customColumnsManager.definitions.forEach { def -> if (mergedColumns.find { it.id == def.id } == null) { val columnStub = ColumnList.ColumnStub(def.id, def.name, false, -1, -1) mergedColumns.add(columnStub) - listItems.add(ColumnAsListItem(columnStub, columnStub.isVisible, true, customColumnsManager)) + listItems.add(ColumnAsListItem(columnStub, columnStub.isVisible, true, customColumnsManager, customPropertyEditor::updateVisibility)) } } listView.items = listItems listView.cellFactory = Callback { CellImpl() } val propertySheetBox = vbox { addClasses("property-sheet-box") + add(customPropertyEditor.visibilityTogglePane) add(customPropertyEditor.propertySheetLabel, Pos.CENTER_LEFT, Priority.NEVER) add(customPropertyEditor.propertySheet.node, Pos.CENTER, Priority.ALWAYS) add(errorPane) @@ -132,7 +134,7 @@ class ColumnManager( private fun onAddColumn() { val item = ColumnAsListItem( null, - isVisible = true, isCustom = true, customColumnsManager + isVisible = true, isCustom = true, customColumnsManager, customPropertyEditor::updateVisibility ).also { val title = RootLocalizer.create("addCustomColumn").also { it.update("") @@ -274,8 +276,10 @@ internal class CustomPropertyEditor( } } val fallback2 = RootLocalizer.createWithRootKey("option.taskProperties.customColumn", fallback1) - RootLocalizer.createWithRootKey("option.customPropertyDialog", fallback2) + val fallback3 = RootLocalizer.createWithRootKey("option.customPropertyDialog", fallback2) + RootLocalizer.createWithRootKey("", fallback3) } + private val nameOption = ObservableString(id = "name", validator = {value -> listItems.find { it.title == value }?.let { if (it != selectedItem?.cloneOf) { @@ -323,6 +327,12 @@ internal class CustomPropertyEditor( internal val propertySheetLabel = Label().also { it.styleClass.add("title") } + private val visibilityToggle = createToggleSwitch() + internal val visibilityTogglePane = HBox().also { + it.styleClass.add("visibility-pane") + it.children.add(visibilityToggle) + it.children.add(Label(localizer.formatText("customPropertyDialog.visibility.label"))) + } private var isPropertyChangeIgnored = false var selectedItem: ColumnAsListItem? = null set(selectedItem) { @@ -332,6 +342,7 @@ internal class CustomPropertyEditor( nameOption.set(selectedItem.title) typeOption.set(selectedItem.type) defaultValueOption.set(selectedItem.defaultValue) + visibilityToggle.isSelected = selectedItem.isVisible if (selectedItem.isCustom) { propertySheetLabel.text = ourLocalizer.formatText("propertyPane.title.custom") @@ -352,6 +363,10 @@ internal class CustomPropertyEditor( init { allOptions.forEach { it.addWatcher { onEdit() } } + + visibilityToggle.selectedProperty().addListener { _, _, _ -> + onEdit() + } propertySheet.validationErrors.addListener(MapChangeListener { if (propertySheet.validationErrors.isEmpty()) { errorUi(null) @@ -362,9 +377,21 @@ internal class CustomPropertyEditor( }) } + internal fun updateVisibility(item: ColumnAsListItem) { + selectedItem?.let { + if (it.column?.id == item.column?.id) { + if (it.isVisible != item.isVisible) { + it.isVisible = item.isVisible + visibilityToggle.isSelected = item.isVisible + } + } + } + + } private fun onEdit() { if (!isPropertyChangeIgnored) { selectedItem?.let {selected -> + selected.isVisible = visibilityToggle.isSelected selected.title = nameOption.value ?: "" selected.type = typeOption.value selected.defaultValue = defaultValueOption.value ?: "" @@ -383,13 +410,20 @@ internal class CustomPropertyEditor( } } +/** + * Objects stored in the list view on the left side. + */ internal class ColumnAsListItem( val column: ColumnList.Column?, - var isVisible: Boolean, + isVisible: Boolean, val isCustom: Boolean, - val customColumnsManager: CustomPropertyManager + val customColumnsManager: CustomPropertyManager, + val changeListener: (ColumnAsListItem)->Unit ) { - constructor(cloneOf: ColumnAsListItem): this(cloneOf.column, cloneOf.isVisible, cloneOf.isCustom, cloneOf.customColumnsManager) { + constructor(cloneOf: ColumnAsListItem): this( + cloneOf.column, cloneOf.isVisible, cloneOf.isCustom, cloneOf.customColumnsManager, + cloneOf.changeListener) { + this.cloneOf = cloneOf this.title = cloneOf.title this.type = cloneOf.type @@ -399,6 +433,14 @@ internal class ColumnAsListItem( } internal var cloneOf: ColumnAsListItem? = null + var isVisible: Boolean = false + set(value) { + if (field != value) { + field = value + changeListener(this) + } + } + var title: String = "" var type: PropertyType = PropertyType.STRING @@ -432,6 +474,7 @@ internal class ColumnAsListItem( fun clone(): ColumnAsListItem = ColumnAsListItem(this) init { + this.isVisible = isVisible if (column != null) { title = column.name val customColumn = customColumnsManager.definitions.find { it.id == column.id } @@ -455,6 +498,9 @@ internal class ColumnAsListItem( } } +/** + * UI components that render columns in the list view. + */ private class CellImpl : ListCell() { private val iconVisible = MaterialIconView(MaterialIcon.VISIBILITY) private val iconHidden = MaterialIconView(MaterialIcon.VISIBILITY_OFF) diff --git a/ganttproject/src/main/sass/biz/ganttproject/app/Dialog.scss b/ganttproject/src/main/sass/biz/ganttproject/app/Dialog.scss index 3ffd5e91f3..3d57cfad4b 100644 --- a/ganttproject/src/main/sass/biz/ganttproject/app/Dialog.scss +++ b/ganttproject/src/main/sass/biz/ganttproject/app/Dialog.scss @@ -56,40 +56,42 @@ along with GanttProject. If not, see . } } -//.btn-regular { -// -fx-background-color: $gp-light-gray; -// -fx-border-color: transparent; -// -fx-border-radius: 0; -// -fx-text-fill: $gp-dark-gray; -// /* -fx-font-size: 110%;*/ -// -fx-padding: 0.75ex 2em; -// -fx-cursor: hand; -// -// &.secondary { -// -fx-background-color: transparent; -// -fx-border-color: $gp-medium-gray; -// -// &:hover, &:focused { -// -fx-background-color: derive($gp-light-gray, 10%); -// } -// } -// &:hover, &:focused { -// -fx-effect: dropshadow(gaussian, $gp-light-gray, 4, 0, 0, 4); -// -fx-background-color: derive($gp-light-gray, 10%); -// -fx-text-fill: derive($gp-dark-gray, -10%); -// } -// -// &:disabled { -// -fx-opacity: 0.5; -// -fx-cursor: default; -// } -// -// &:pressed { -// -fx-effect: none; -// -fx-background-color: derive($gp-medium-gray, 10%); -// -fx-text-fill: derive($gp-dark-gray, -10%); -// } -//} +.btn-regular { + -fx-background-color: $gp-light-gray; + -fx-border-color: transparent; + -fx-border-radius: 0; + -fx-text-fill: $gp-dark-gray; + /* -fx-font-size: 110%;*/ + -fx-font-weight: bold; + -fx-padding: 0.75ex 2em; + -fx-cursor: hand; + + &.secondary { + -fx-background-color: transparent; + -fx-border-color: $gp-medium-gray; + + &:hover, &:focused { + -fx-background-color: derive($gp-light-gray, 10%); + } + } + &:hover, &:focused { + -fx-effect: dropshadow(gaussian, $gp-light-gray, 4, 0, 0, 4); + -fx-background-color: derive($gp-light-gray, 10%); + -fx-text-fill: derive($gp-dark-gray, -10%); + } + + &:disabled { + -fx-opacity: 0.5; + -fx-cursor: default; + } + + &:pressed { + -fx-effect: none; + -fx-background-color: derive($gp-light-gray, 10%); + -fx-text-fill: derive($gp-dark-gray, -10%); + } +} + .border-etched { -fx-border-base: gray; diff --git a/ganttproject/src/main/sass/biz/ganttproject/app/StatusBar.scss b/ganttproject/src/main/sass/biz/ganttproject/app/StatusBar.scss new file mode 100644 index 0000000000..f7ef4fd722 --- /dev/null +++ b/ganttproject/src/main/sass/biz/ganttproject/app/StatusBar.scss @@ -0,0 +1,69 @@ +/*! + * Copyright 2024 BarD Software s.r.o., Dmitry Barashev. + * + * This file is part of GanttProject, an opensource project management tool. + * + * GanttProject is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * GanttProject is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GanttProject. If not, see . + */ +@import "theme"; +@import "../lib/fx/ToggleSwitch"; + +.statusbar { + -fx-background-color: transparent; + -fx-text-fill: $gp-dark-gray; + -fx-padding: 1ex 24px; + -fx-alignment: center-left; + -fx-min-width: 50em; + + Button { + -fx-min-height: 26px; + -fx-alignment: center; + -fx-background-color: transparent; + -fx-padding: 0 0.5em; + + &:hover { + -fx-fill: $gp-dark-gray; + -fx-cursor: hand; + + &.glyph-icon { + -fx-fill: $gp-dark-gray; + } + } + .glyph-icon { + -fx-alignment: center; + -fx-fill: $gp-medium-gray; + -glyph-size: 20px; + } + } + Button:pressed .glyph-icon, .statusbar Button:hover:pressed .glyph-icon { + -fx-fill: derive($gp-dark-gray, -15%); + } +} + +.statusbar > * { + -fx-text-fill: $gp-medium-gray; +} + +.statusbar Button .decoration-warning, +.statusbar Button:hover .decoration-warning, +.statusbar Button:hover:pressed .decoration-warning { + -fx-fill: #ff6f85; + -fx-background-color: #ff6f85; + -fx-border-color: none; +} + +.dlg-connect { + -fx-pref-height: 400; + -fx-pref-width: 400; +} diff --git a/ganttproject/src/main/sass/biz/ganttproject/ganttview/ColumnManager.scss b/ganttproject/src/main/sass/biz/ganttproject/ganttview/ColumnManager.scss index ca8309dc6f..cecdc934a9 100644 --- a/ganttproject/src/main/sass/biz/ganttproject/ganttview/ColumnManager.scss +++ b/ganttproject/src/main/sass/biz/ganttproject/ganttview/ColumnManager.scss @@ -2,6 +2,7 @@ @import "../app/theme"; @import "../app/typography"; @import "../app/validation"; +@import "../lib/fx/ToggleSwitch"; .dlg-column-manager { -fx-min-height: 400; @@ -46,6 +47,9 @@ } .property-sheet-box { -fx-background-color: whitesmoke; + .visibility-pane { + -fx-padding: 10 10 30 10; + } .title { -fx-padding: 10 10 10 10; -fx-font-size: 110%; diff --git a/ganttproject/src/main/sass/biz/ganttproject/lib/fx/ToggleSwitch.scss b/ganttproject/src/main/sass/biz/ganttproject/lib/fx/ToggleSwitch.scss new file mode 100644 index 0000000000..934603cebf --- /dev/null +++ b/ganttproject/src/main/sass/biz/ganttproject/lib/fx/ToggleSwitch.scss @@ -0,0 +1,25 @@ +/*! + * Copyright 2024 BarD Software s.r.o., Dmitry Barashev. + * + * This file is part of GanttProject, an opensource project management tool. + * + * GanttProject is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * GanttProject is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GanttProject. If not, see . + */ +@import "../../app/theme"; + +.toggle-switch:selected .thumb-area{ + -fx-background-color: linear-gradient(to bottom, derive(-fx-text-box-border, -20%), derive(-fx-text-box-border, -30%)), + linear-gradient(to bottom, derive($gp-orange, 30%), $gp-orange); + -fx-background-insets: 0, 1; +} \ No newline at end of file