From c0c79c331b6c8dee0a9acde5fdc19e1f219ba9ec Mon Sep 17 00:00:00 2001 From: Achraf Dridi Date: Tue, 23 Jul 2024 08:41:27 +0100 Subject: [PATCH 1/2] Display color value without entering the record [APPS-02A6] --- .../kotlin/org/kopi/galite/visual/base/Utils.kt | 13 +++++++++++++ .../org/kopi/galite/visual/form/VColorField.kt | 17 +++-------------- .../galite/visual/ui/vaadin/form/DGridBlock.kt | 16 +++++++++++++--- 3 files changed, 29 insertions(+), 17 deletions(-) diff --git a/galite-core/src/main/kotlin/org/kopi/galite/visual/base/Utils.kt b/galite-core/src/main/kotlin/org/kopi/galite/visual/base/Utils.kt index a2e1542a79..da0faab319 100644 --- a/galite-core/src/main/kotlin/org/kopi/galite/visual/base/Utils.kt +++ b/galite-core/src/main/kotlin/org/kopi/galite/visual/base/Utils.kt @@ -36,6 +36,7 @@ import java.util.zip.GZIPInputStream import java.util.zip.GZIPOutputStream import org.kopi.galite.util.base.InconsistencyException +import java.awt.Color /** * loading of image @@ -231,6 +232,18 @@ open class Utils : org.kopi.galite.util.base.Utils() { } } + /** + * Convert a java.awt.Color to HexString + */ + fun colorToRgbString(c: Color?): String { + val color = c ?: Color(0,0,0) + val redHex = String.format("%02x", color.red) + val greenHex = String.format("%02x", color.green) + val blueHex = String.format("%02x", color.blue) + + return "$redHex$greenHex$blueHex" + } + // ---------------------------------------------------------------------- // PRIVATE DATA // ---------------------------------------------------------------------- diff --git a/galite-core/src/main/kotlin/org/kopi/galite/visual/form/VColorField.kt b/galite-core/src/main/kotlin/org/kopi/galite/visual/form/VColorField.kt index 3730066972..2392b6ce32 100644 --- a/galite-core/src/main/kotlin/org/kopi/galite/visual/form/VColorField.kt +++ b/galite-core/src/main/kotlin/org/kopi/galite/visual/form/VColorField.kt @@ -27,10 +27,11 @@ import kotlin.reflect.KClass import org.jetbrains.exposed.sql.Column import org.jetbrains.exposed.sql.ResultRow import org.jetbrains.exposed.sql.statements.api.ExposedBlob +import org.kopi.galite.visual.VlibProperties +import org.kopi.galite.visual.base.Utils import org.kopi.galite.visual.list.VColorColumn import org.kopi.galite.visual.list.VListColumn import org.kopi.galite.util.base.InconsistencyException -import org.kopi.galite.visual.VlibProperties class VColorField(val bufferSize: Int) : VField(1,1) { @@ -171,7 +172,7 @@ class VColorField(val bufferSize: Int) : VField(1,1) { /** * Returns the SQL representation of field value of given record. */ - override fun getSqlImpl(r: Int): String? = if (value[r] == null) null else colorToRgbString(value[r]) + override fun getSqlImpl(r: Int): String? = if (value[r] == null) null else Utils.colorToRgbString(value[r]) /** * Copies the value of a record to another @@ -247,16 +248,4 @@ class VColorField(val bufferSize: Int) : VField(1,1) { return byteArrayOf(red.toByte(), green.toByte(), blue.toByte()) } - - /** - * Convert a java.awt.Color to HexString - */ - fun colorToRgbString(c: Color?): String { - val color = c ?: Color(0,0,0) - val redHex = String.format("%02x", color.red) - val greenHex = String.format("%02x", color.green) - val blueHex = String.format("%02x", color.blue) - - return "$redHex$greenHex$blueHex" - } } diff --git a/galite-core/src/main/kotlin/org/kopi/galite/visual/ui/vaadin/form/DGridBlock.kt b/galite-core/src/main/kotlin/org/kopi/galite/visual/ui/vaadin/form/DGridBlock.kt index 4b520724a3..25b0d2b6b3 100644 --- a/galite-core/src/main/kotlin/org/kopi/galite/visual/ui/vaadin/form/DGridBlock.kt +++ b/galite-core/src/main/kotlin/org/kopi/galite/visual/ui/vaadin/form/DGridBlock.kt @@ -47,6 +47,7 @@ import com.vaadin.flow.component.grid.HeaderRow import com.vaadin.flow.component.grid.editor.Editor import com.vaadin.flow.component.grid.editor.EditorImpl import com.vaadin.flow.component.html.Div +import com.vaadin.flow.component.html.Input import com.vaadin.flow.component.icon.Icon import com.vaadin.flow.component.icon.VaadinIcon import com.vaadin.flow.component.textfield.TextField @@ -59,6 +60,7 @@ import com.vaadin.flow.data.value.ValueChangeMode import com.vaadin.flow.function.SerializableConsumer import com.vaadin.flow.function.SerializablePredicate import com.vaadin.flow.internal.ExecutionContext +import org.kopi.galite.visual.base.Utils /** * Grid based chart block implementation. @@ -497,9 +499,17 @@ open class DGridBlock(parent: DForm, model: VBlock) : DBlock(parent, model) { // Create a div element and set its background and foreground colors val div = Div() - div.text = if (value is Color) "" else columnView.editorField.format(value)?.toString() ?: "" - backgroundColor?.let { div.style.set("background-color", "rgb(${it.red}, ${it.green}, ${it.blue})") } - foregroundColor?.let { div.style.set("color", "rgb(${it.red}, ${it.green}, ${it.blue})") } + if (value is Color) { + val input = Input() + + input.type = "color" + input.value = "#" + Utils.colorToRgbString(value) + div.add(input) + } else { + div.text = columnView.editorField.format(value)?.toString() ?: "" + backgroundColor?.let { div.style.set("background-color", "rgb(${it.red}, ${it.green}, ${it.blue})") } + foregroundColor?.let { div.style.set("color", "rgb(${it.red}, ${it.green}, ${it.blue})") } + } div }) .setKey(i.toString()) From 07713c04f065df31c3ab8e09c26c3d09ab9fbf22 Mon Sep 17 00:00:00 2001 From: Achraf Dridi Date: Mon, 12 Aug 2024 11:37:38 +0100 Subject: [PATCH 2/2] change test class + add readOnly on color field [APPS-02A6] --- .../visual/ui/vaadin/form/DGridBlock.kt | 1 + .../org/kopi/galite/demo/client/ClientForm.kt | 27 ----- .../kopi/galite/demo/command/CommandForm.kt | 113 +++++++++++++++++- .../kopi/galite/demo/database/Migration.kt | 38 +++--- .../org/kopi/galite/demo/database/Tables.kt | 2 +- .../galite/tests/ui/vaadin/form/FormTests.kt | 36 +----- 6 files changed, 137 insertions(+), 80 deletions(-) diff --git a/galite-core/src/main/kotlin/org/kopi/galite/visual/ui/vaadin/form/DGridBlock.kt b/galite-core/src/main/kotlin/org/kopi/galite/visual/ui/vaadin/form/DGridBlock.kt index 25b0d2b6b3..4f2ccbb56f 100644 --- a/galite-core/src/main/kotlin/org/kopi/galite/visual/ui/vaadin/form/DGridBlock.kt +++ b/galite-core/src/main/kotlin/org/kopi/galite/visual/ui/vaadin/form/DGridBlock.kt @@ -504,6 +504,7 @@ open class DGridBlock(parent: DForm, model: VBlock) : DBlock(parent, model) { input.type = "color" input.value = "#" + Utils.colorToRgbString(value) + input.isReadOnly = true div.add(input) } else { div.text = columnView.editorField.format(value)?.toString() ?: "" diff --git a/galite-demo/galite-vaadin/src/main/kotlin/org/kopi/galite/demo/client/ClientForm.kt b/galite-demo/galite-vaadin/src/main/kotlin/org/kopi/galite/demo/client/ClientForm.kt index da36a37f82..447c17532b 100644 --- a/galite-demo/galite-vaadin/src/main/kotlin/org/kopi/galite/demo/client/ClientForm.kt +++ b/galite-demo/galite-vaadin/src/main/kotlin/org/kopi/galite/demo/client/ClientForm.kt @@ -26,11 +26,9 @@ import org.kopi.galite.demo.database.Client import org.kopi.galite.demo.database.Product import org.kopi.galite.demo.database.Purchase import org.kopi.galite.demo.desktop.runForm -import org.kopi.galite.visual.VColor import org.kopi.galite.visual.VExecFailedException import org.kopi.galite.visual.database.transaction import org.kopi.galite.visual.domain.BOOL -import org.kopi.galite.visual.domain.COLOR import org.kopi.galite.visual.domain.DECIMAL import org.kopi.galite.visual.domain.INT import org.kopi.galite.visual.domain.ListDomain @@ -43,7 +41,6 @@ import org.kopi.galite.visual.dsl.form.DictionaryForm import org.kopi.galite.visual.dsl.form.FieldOption import org.kopi.galite.visual.dsl.form.Key import org.kopi.galite.visual.form.VBlock -import org.kopi.galite.visual.form.VField class ClientForm : DictionaryForm(title = "Clients", locale = Locale.UK) { @@ -136,11 +133,6 @@ class ClientForm : DictionaryForm(title = "Clients", locale = Locale.UK) { val PostqryTrigger = trigger(POSTQRY) { salesBlock.clientID[0] = clientID.value salesBlock.load() - for (rec in 0 until salesBlock.block.bufferSize) { - if (salesBlock.block.isRecordFilled(rec)) { - salesBlock.colorier(rec) - } - } } /** @@ -211,11 +203,6 @@ class ClientForm : DictionaryForm(title = "Clients", locale = Locale.UK) { help = "The item price" columns(P.price) } - val testColor = visit(domain = COLOR, at(2,2)) { - label = "Color" - help = "Color field [for test purpose]" - columns(P.color) - } init { border = Border.LINE @@ -226,20 +213,6 @@ class ClientForm : DictionaryForm(title = "Clients", locale = Locale.UK) { command(item = dynamicReport) { createDynamicReport() } command(item = list) { recursiveQuery() } } - - fun colorier(rec: Int) { - if (block.isRecordFilled(rec)) { - var backgroundColor = VColor.WHITE - - testColor.vField.getColor(rec)?.let { backgroundColor = VColor(it.red, it.green, it.blue) } - for (field in block.fields) { - if (field.getType() != VField.MDL_FLD_COLOR) { - field.setColor(rec, VColor.BLACK, backgroundColor) - field.fireColorChanged(rec) - } - } - } - } } object ClientID : ListDomain(30) { diff --git a/galite-demo/galite-vaadin/src/main/kotlin/org/kopi/galite/demo/command/CommandForm.kt b/galite-demo/galite-vaadin/src/main/kotlin/org/kopi/galite/demo/command/CommandForm.kt index 7e6a921d74..125568fe0e 100644 --- a/galite-demo/galite-vaadin/src/main/kotlin/org/kopi/galite/demo/command/CommandForm.kt +++ b/galite-demo/galite-vaadin/src/main/kotlin/org/kopi/galite/demo/command/CommandForm.kt @@ -20,13 +20,21 @@ import java.util.Locale import org.kopi.galite.demo.database.Client import org.kopi.galite.demo.database.Command +import org.kopi.galite.demo.database.Purchase import org.kopi.galite.demo.desktop.runForm +import org.kopi.galite.visual.VColor +import org.kopi.galite.visual.VExecFailedException +import org.kopi.galite.visual.form.VBlock +import org.kopi.galite.visual.form.VField +import org.kopi.galite.visual.database.transaction +import org.kopi.galite.visual.domain.COLOR import org.kopi.galite.visual.domain.CodeDomain import org.kopi.galite.visual.domain.INT import org.kopi.galite.visual.dsl.common.Icon import org.kopi.galite.visual.dsl.common.Mode import org.kopi.galite.visual.dsl.form.Access import org.kopi.galite.visual.dsl.form.Block +import org.kopi.galite.visual.dsl.form.BlockOption import org.kopi.galite.visual.dsl.form.DictionaryForm import org.kopi.galite.visual.dsl.form.Key @@ -65,9 +73,15 @@ class CommandForm : DictionaryForm(title = "Commands", locale = Locale.UK) { command(item = dynamicReport) { createDynamicReport() } + command(item = list) { recursiveQuery() } + command(item = save, Mode.INSERT, Mode.UPDATE) { save(block) } } - class BlockCommand : Block("Commands", 1, 10) { + val purchases = page.insertBlock(BlockPurchase()) { + command(item = deleteLine) { deleteLine(block) } + } + + inner class BlockCommand : Block("Commands", 1, 10) { val u = table(Command) val v = table(Client) @@ -102,6 +116,103 @@ class CommandForm : DictionaryForm(title = "Commands", locale = Locale.UK) { init { blockVisibility(Access.VISIT, Mode.QUERY) } + + val PostqryTrigger = trigger(POSTQRY) { + purchases.idClt[0] = idClt.value + purchases.load() + for (rec in 0 until purchases.block.bufferSize) { + if (purchases.block.isRecordFilled(rec)) { + purchases.colorRecord(rec) + } + } + } + + /** + * Save block + */ + fun save(b: VBlock) { + tb1.block.validate() + + if (!purchases.isFilled()) { + purchases.currentRecord = 0 + throw VExecFailedException("Purchase block is empty.") + } + + transaction { + tb1.block.save() + purchases.block.save() + } + + b.form.reset() + } + } + + inner class BlockPurchase : Block("Purchase", 10, 10) { + val u = table(Purchase) + + val numPurchase = hidden(domain = INT(20)) { + label = "Number" + help = "The purchase number" + columns(u.id) + } + val idClt = mustFill(domain = INT(25), position = at(1, 1)) { + label = "Client ID" + help = "The client ID" + columns(u.idClt) + } + val idProduct = mustFill(domain = INT(20), position = at(2, 1)) { + label = "Product ID" + help = "The Product ID" + columns(u.idPdt) + } + val quantity = mustFill(domain = INT(7), position = at(4, 1)) { + label = "Quantity" + help = "quantity of the current purchase" + columns(u.quantity) + } + + val color = mustFill(domain = COLOR, position = at(4, 1)) { + label = "color" + help = "color [for test purpose] " + columns(u.color) + } + + init { + blockVisibility(Access.VISIT, Mode.QUERY) + options(BlockOption.NODETAIL) + } + + /** + * Delete line from no detail block. + */ + fun deleteLine(b: VBlock) { + val rec: Int = b.activeRecord + + if ( rec == -1) return + if (b.isRecordFilled(rec)) { + b.setRecordDeleted(rec, true) + transaction { b.refreshLookup(rec) } + } + b.form.gotoBlock(b) + b.gotoRecord(rec + 1) + } + + /** + * Color record using color field. + */ + fun colorRecord(rec: Int) { + if (block.isRecordFilled(rec)) { + var backgroundColor = VColor.WHITE + + color.vField.getColor(rec)?.let { backgroundColor = VColor(it.red, it.green, it.blue) } + for (field in block.fields) { + if (field.getType() != VField.MDL_FLD_COLOR) { + field.setColor(rec, VColor.BLACK, backgroundColor) + field.fireColorChanged(rec) + } + } + } + } } object Payment : CodeDomain() { diff --git a/galite-demo/galite-vaadin/src/main/kotlin/org/kopi/galite/demo/database/Migration.kt b/galite-demo/galite-vaadin/src/main/kotlin/org/kopi/galite/demo/database/Migration.kt index 85f8696921..fac11d3401 100644 --- a/galite-demo/galite-vaadin/src/main/kotlin/org/kopi/galite/demo/database/Migration.kt +++ b/galite-demo/galite-vaadin/src/main/kotlin/org/kopi/galite/demo/database/Migration.kt @@ -246,10 +246,10 @@ fun addClient(firstName: String, fun addProducts() { // This data is used in automated tests - addProduct("description Product 0", 1, "tax 1", "Men", "Supplier 0", BigDecimal("100"), ExposedBlob(byteArrayOf(52.toByte(), 178.toByte(), 216.toByte()))) - addProduct("description Product 1", 2, "tax 2", "Men","Supplier 0", BigDecimal("200"), ExposedBlob(byteArrayOf(216.toByte(), 52.toByte(), 200.toByte()))) - addProduct("description Product 2", 3, "tax 2", "Women","Supplier 0", BigDecimal("300"), ExposedBlob(byteArrayOf(236.toByte(), 158.toByte(), 41.toByte()))) - addProduct("description Product 3", 1, "tax 3", "Children","Supplier 0", BigDecimal("400"),ExposedBlob(byteArrayOf(255.toByte(), 102.toByte(), 102.toByte()))) + addProduct("description Product 0", 1, "tax 1", "Men", "Supplier 0", BigDecimal("100")) + addProduct("description Product 1", 2, "tax 2", "Men","Supplier 0", BigDecimal("200")) + addProduct("description Product 2", 3, "tax 2", "Women","Supplier 0", BigDecimal("300")) + addProduct("description Product 3", 1, "tax 3", "Children","Supplier 0", BigDecimal("400")) for (i in 4..499) { val description = "description Product $i" val category = (1..5).random() @@ -259,11 +259,11 @@ fun addProducts() { val price = (50..500).random().toBigDecimal() val color = ExposedBlob(byteArrayOf((1..255).random().toByte(), (1..255).random().toByte(), (10..255).random().toByte())) - addProduct(description, category, tax, gender, supplier, price, color) + addProduct(description, category, tax, gender, supplier, price) } } -fun addProduct(description: String, category: Int, taxName: String, department: String, supplier: String, price: BigDecimal, col: ExposedBlob) { +fun addProduct(description: String, category: Int, taxName: String, department: String, supplier: String, price: BigDecimal) { Product.insert { it[Product.description] = description it[Product.department] = department @@ -271,29 +271,29 @@ fun addProduct(description: String, category: Int, taxName: String, department: it[Product.category] = category it[Product.taxName] = taxName it[Product.price] = price - it[Product.color] = col } } fun addSales() { - addSale(1, 1, 1) - addSale(1, 2, 1) - addSale(1, 3, 2) - addSale(1, 4, 3) - addSale(2, 1, 1) - addSale(2, 2, 2) - addSale(2, 3, 4) - addSale(3, 4, 2) - addSale(3, 1, 1) - addSale(3, 2, 3) - addSale(4, 2, 10) + addSale(1, 1, 1, ExposedBlob(byteArrayOf(52.toByte(), 178.toByte(), 216.toByte()))) + addSale(1, 2, 1, ExposedBlob(byteArrayOf(216.toByte(), 178.toByte(), 216.toByte()))) + addSale(1, 3, 2, ExposedBlob(byteArrayOf(52.toByte(), 52.toByte(), 216.toByte()))) + addSale(1, 4, 3, ExposedBlob(byteArrayOf(52.toByte(), 178.toByte(), 200.toByte()))) + addSale(2, 1, 1, ExposedBlob(byteArrayOf(236.toByte(), 102.toByte(), 216.toByte()))) + addSale(2, 2, 2, ExposedBlob(byteArrayOf(102.toByte(), 178.toByte(), 216.toByte()))) + addSale(2, 3, 4, ExposedBlob(byteArrayOf(200.toByte(), 155.toByte(), 14.toByte()))) + addSale(3, 4, 2, ExposedBlob(byteArrayOf(52.toByte(), 158.toByte(), 158.toByte()))) + addSale(3, 1, 1, ExposedBlob(byteArrayOf(52.toByte(), 178.toByte(), 216.toByte()))) + addSale(3, 2, 3, ExposedBlob(byteArrayOf(122.toByte(), 216.toByte(), 100.toByte()))) + addSale(4, 2, 10, ExposedBlob(byteArrayOf(200.toByte(), 25.toByte(), 52.toByte()))) } -fun addSale(client: Int, product: Int, qty: Int) { +fun addSale(client: Int, product: Int, qty: Int, col: ExposedBlob) { Purchase.insert { it[idClt] = client it[idPdt] = product it[quantity] = qty + it[color] = col } } diff --git a/galite-demo/galite-vaadin/src/main/kotlin/org/kopi/galite/demo/database/Tables.kt b/galite-demo/galite-vaadin/src/main/kotlin/org/kopi/galite/demo/database/Tables.kt index ce6ce1f7dd..2263adff2d 100644 --- a/galite-demo/galite-vaadin/src/main/kotlin/org/kopi/galite/demo/database/Tables.kt +++ b/galite-demo/galite-vaadin/src/main/kotlin/org/kopi/galite/demo/database/Tables.kt @@ -41,6 +41,7 @@ object Purchase: Table("PURCHASE") { val idClt = integer("CLIENT").references(Client.idClt) val idPdt = integer("PRODUCT").references(Product.idPdt) val quantity = integer("QUANTITY") + val color = blob("COLOR") } object Product : Table("PRODUCTS") { @@ -52,7 +53,6 @@ object Product : Table("PRODUCTS") { val taxName = varchar("TAX", 20).references(TaxRule.taxName) val price = decimal("UNIT_PRICE_EXCLUDING_VAT", 9, 3) val photo = blob("PHOTO").nullable() - val color = blob("COLOR") override val primaryKey = PrimaryKey(idPdt) } diff --git a/galite-demo/galite-vaadin/src/test/kotlin/org/kopi/galite/tests/ui/vaadin/form/FormTests.kt b/galite-demo/galite-vaadin/src/test/kotlin/org/kopi/galite/tests/ui/vaadin/form/FormTests.kt index 62cb675ab4..0fcdfd32c6 100644 --- a/galite-demo/galite-vaadin/src/test/kotlin/org/kopi/galite/tests/ui/vaadin/form/FormTests.kt +++ b/galite-demo/galite-vaadin/src/test/kotlin/org/kopi/galite/tests/ui/vaadin/form/FormTests.kt @@ -110,38 +110,10 @@ class FormTests: GaliteVUITestBase() { clientForm.list.triggerCommand() val block = clientForm.salesBlock.findMultiBlock() val data = arrayOf( - arrayOf( - "1".asDiv(arrayOf(52, 178, 216), arrayOf(0, 0, 0)), - "1".asDiv(arrayOf(52, 178, 216), arrayOf(0, 0, 0)), - "description Product 0".asDiv(arrayOf(52, 178, 216), arrayOf(0, 0, 0)), - "1".asDiv(arrayOf(52, 178, 216), arrayOf(0, 0, 0)), - "100,00000".asDiv(arrayOf(52, 178, 216), arrayOf(0, 0, 0)), - null.asDiv() - ), - arrayOf( - "2".asDiv(arrayOf(216, 52, 200), arrayOf(0, 0, 0)), - "2".asDiv(arrayOf(216, 52, 200), arrayOf(0, 0, 0)), - "description Product 1".asDiv(arrayOf(216, 52, 200), arrayOf(0, 0, 0)), - "1".asDiv(arrayOf(216, 52, 200), arrayOf(0, 0, 0)), - "200,00000".asDiv(arrayOf(216, 52, 200), arrayOf(0, 0, 0)), - null.asDiv() - ), - arrayOf( - "3".asDiv(arrayOf(236, 158, 41), arrayOf(0, 0, 0)), - "3".asDiv(arrayOf(236, 158, 41), arrayOf(0, 0, 0)), - "description Product 2".asDiv(arrayOf(236, 158, 41), arrayOf(0, 0, 0)), - "2".asDiv(arrayOf(236, 158, 41), arrayOf(0, 0, 0)), - "300,00000".asDiv(arrayOf(236, 158, 41), arrayOf(0, 0, 0)), - null.asDiv() - ), - arrayOf( - "4".asDiv(arrayOf(255, 102, 102), arrayOf(0, 0, 0)), - "4".asDiv(arrayOf(255, 102, 102), arrayOf(0, 0, 0)), - "description Product 3".asDiv(arrayOf(255, 102, 102), arrayOf(0, 0, 0)), - "3".asDiv(arrayOf(255, 102, 102), arrayOf(0, 0, 0)), - "400,00000".asDiv(arrayOf(255, 102, 102), arrayOf(0, 0, 0)), - null.asDiv() - ) + arrayOf("1".asDiv(), "1".asDiv(), "description Product 0".asDiv(), "1".asDiv(), "100,00000".asDiv()), + arrayOf("2".asDiv(), "2".asDiv(), "description Product 1".asDiv(), "1".asDiv(), "200,00000".asDiv()), + arrayOf("3".asDiv(), "3".asDiv(), "description Product 2".asDiv(), "2".asDiv(), "300,00000".asDiv()), + arrayOf("4".asDiv(), "4".asDiv(), "description Product 3".asDiv(), "3".asDiv(), "400,00000".asDiv()) ) data.forEachIndexed { index, row ->