• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

Martomate / TriPaint / 9275511596

28 May 2024 07:51PM UTC coverage: 29.682% (+1.8%) from 27.877%
9275511596

push

github

Martomate
Updated to Scala 3.4 and MUnit 1.0

2 of 11 new or added lines in 9 files covered. (18.18%)

430 existing lines in 38 files now uncovered.

401 of 1351 relevant lines covered (29.68%)

0.3 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

0.0
/src/main/scala/tripaint/view/gui/MainStage.scala
1
package tripaint.view.gui
2

3
import tripaint.model.{Color, TriPaintModel}
4
import tripaint.model.effects.{BlurEffect, MotionBlurEffect, RandomNoiseEffect}
5
import tripaint.model.image.{GridCell, ImagePool, ImageStorage}
6
import tripaint.model.image.format.{RecursiveStorageFormat, SimpleStorageFormat}
7
import tripaint.view.{
8
  EditMode,
9
  FileOpenSettings,
10
  FileSaveSettings,
11
  TriPaintView,
12
  TriPaintViewListener
13
}
14
import tripaint.view.image.ImageGridPane
15

16
import scalafx.application.JFXApp3.PrimaryStage
17
import scalafx.scene.Scene
18
import scalafx.scene.control.*
19
import scalafx.scene.control.Alert.AlertType
20
import scalafx.scene.control.ButtonBar.ButtonData
21
import scalafx.scene.layout.{AnchorPane, BorderPane, TilePane, VBox}
22
import scalafx.scene.paint.Color as FXColor
23
import scalafx.stage.FileChooser
24
import scalafx.stage.FileChooser.ExtensionFilter
25

26
import java.io.File
27
import scala.util.Try
28

29
class MainStage(controls: TriPaintViewListener, model: TriPaintModel)
30
    extends PrimaryStage
31
    with TriPaintView {
UNCOV
32
  private val imageDisplay: ImageGridPane = new ImageGridPane(model.imageGrid)
×
33

34
  private val menuBar: MenuBar = TheMenuBar.create(controls)
×
35
  private val toolBar: ToolBar = TheToolBar.create(controls)
×
UNCOV
36
  private val toolBox: TilePane = ToolBox.create(EditMode.modes)
×
37
  private val imageTabs: TilePane =
38
    ImageTabs.fromImagePool(model.imageGrid, model.imagePool, controls.requestImageRemoval)
×
UNCOV
39
  private val colorBox: VBox = makeColorBox()
×
40

41
  private var currentFolder: Option[File] = None
42

43
  title = "TriPaint"
×
44
  onCloseRequest = e => {
×
UNCOV
45
    if (!controls.requestExit()) e.consume()
×
46
  }
47
  scene = new Scene(720, 720) {
×
48
    delegate.getStylesheets.add(getClass.getResource("/styles/application.css").toExternalForm)
×
49
    root = new BorderPane {
×
50
      top = new VBox(menuBar, toolBar)
×
51
      center = new AnchorPane {
×
52
        AnchorPane.setAnchors(imageDisplay, 0, 0, 0, 0)
×
UNCOV
53
        imageDisplay.clip === this.clip
×
54

55
        AnchorPane.setLeftAnchor(toolBox, 0)
×
UNCOV
56
        AnchorPane.setTopAnchor(toolBox, 0)
×
57

58
        AnchorPane.setLeftAnchor(colorBox, 10)
×
UNCOV
59
        AnchorPane.setBottomAnchor(colorBox, 10)
×
60

61
        AnchorPane.setRightAnchor(imageTabs, 10)
×
62
        AnchorPane.setTopAnchor(imageTabs, 10)
×
63
        AnchorPane.setBottomAnchor(imageTabs, 10)
×
UNCOV
64
        this.children = Seq(imageDisplay, toolBox, colorBox, imageTabs)
×
65
      }
66
    }
67
  }
68

69
  EditMode.modes
×
70
    .filter(_.shortCut != null)
×
UNCOV
71
    .foreach(m => scene().getAccelerators.put(m.shortCut, () => m.toolboxButton.fire()))
×
72

UNCOV
73
  private def makeColorBox() = {
×
74
    // overlay and imageDisplay
75
    val colorPicker1 = new ColorPicker(new FXColor(imageDisplay.colors.primaryColor().toFXColor))
×
UNCOV
76
    val colorPicker2 = new ColorPicker(new FXColor(imageDisplay.colors.secondaryColor().toFXColor))
×
77

78
    colorPicker1.value.onChange: (_, from, to) =>
×
UNCOV
79
      if from != to then imageDisplay.colors.setPrimaryColor(new FXColor(to))
×
80

81
    colorPicker2.value.onChange: (_, from, to) =>
×
UNCOV
82
      if from != to then imageDisplay.colors.setSecondaryColor(new FXColor(to))
×
83

UNCOV
84
    imageDisplay.colors.primaryColor.onChange: (_, from, to) =>
×
UNCOV
85
      if from != to then colorPicker1.value = to.toFXColor
×
86

UNCOV
87
    imageDisplay.colors.secondaryColor.onChange: (_, from, to) =>
×
UNCOV
88
      if from != to then colorPicker2.value = to.toFXColor
×
89

90
    new VBox(
×
UNCOV
91
      new Label("Primary color:"),
×
92
      colorPicker1,
UNCOV
93
      new Label("Secondary color:"),
×
94
      colorPicker2
95
    )
96
  }
97

UNCOV
98
  override def backgroundColor: Color = imageDisplay.colors.secondaryColor()
×
99

100
  override def askForSaveFile(image: GridCell): Option[File] = {
×
101
    val chooser = new FileChooser
×
102
    currentFolder.foreach(chooser.initialDirectory = _)
×
103
    chooser.title = "Save file"
×
104
    chooser.extensionFilters.add(new ExtensionFilter("PNG", "*.png"))
×
105
    val result = Option(chooser.showSaveDialog(this))
×
UNCOV
106
    result.foreach(r => currentFolder = Some(r.getParentFile))
×
107
    result
108
  }
109

110
  override def askForFileSaveSettings(file: File, image: GridCell): Option[FileSaveSettings] = {
×
111
    AskForFileSaveSettingsDialog.askForFileSaveSettings(
×
UNCOV
112
      image.storage,
×
113
      file,
114
      Seq(
×
115
        SimpleStorageFormat -> "Simple format",
×
UNCOV
116
        RecursiveStorageFormat -> "Recursive format"
×
117
      ),
118
      0
119
    )
120
  }
121

122
  override def askForFileToOpen(): Option[File] = {
×
123
    val chooser = new FileChooser
×
124
    chooser.title = "Open file"
×
125
    val result = Option(chooser.showOpenDialog(this))
×
UNCOV
126
    result.foreach(r => currentFolder = Some(r.getParentFile))
×
127
    result
128
  }
129

130
  override def askForWhereToPutImage(): Option[(Int, Int)] = {
×
UNCOV
131
    AskForXYDialog.askForXY(
×
132
      title = "New image",
133
      headerText = "Please enter where it should be placed."
134
    )
135
  }
136

137
  override def askForBlurRadius(): Option[Int] = {
×
138
    val selectedImagesCoords = model.imageGrid.selectedImages.map(_.coords)
×
UNCOV
139
    DialogUtils.getValueFromDialog[Int](
×
140
      model.imagePool,
UNCOV
141
      model.imageGrid.selectedImages,
×
142
      "Blur images",
143
      "How much should the images be blurred?",
144
      "Radius:",
145
      TextFieldRestriction.uintRestriction,
UNCOV
146
      str => Try(str.toInt).getOrElse(0),
×
147
      Some((radius, grid) =>
UNCOV
148
        if radius > 0 then new BlurEffect(radius).action(selectedImagesCoords, grid)
×
149
      )
150
    )
151
  }
152

153
  override def askForMotionBlurRadius(): Option[Int] = {
×
154
    val selectedImagesCoords = model.imageGrid.selectedImages.map(_.coords)
×
UNCOV
155
    DialogUtils.getValueFromDialog[Int](
×
156
      model.imagePool,
UNCOV
157
      model.imageGrid.selectedImages,
×
158
      "Motion blur images",
159
      "How much should the images be motion blurred?",
160
      "Radius:",
161
      TextFieldRestriction.uintRestriction,
UNCOV
162
      str => Try(str.toInt).getOrElse(0),
×
163
      Some((radius, grid) =>
UNCOV
164
        if radius > 0 then new MotionBlurEffect(radius).action(selectedImagesCoords, grid)
×
165
      )
166
    )
167
  }
168

169
  override def askForRandomNoiseColors(): Option[(Color, Color)] = {
×
170
    val images = model.imageGrid.selectedImages
×
171
    val selectedImagesCoords = model.imageGrid.selectedImages.map(_.coords)
×
172
    val loColorPicker = new ColorPicker(FXColor.Black)
×
UNCOV
173
    val hiColorPicker = new ColorPicker(FXColor.White)
×
174
    import DialogUtils._
175

UNCOV
176
    val (previewPane, updatePreview) = DialogUtils.makeImagePreviewList(images, model.imagePool)
×
177

178
    def updatePreviewFromInputs(): Unit =
×
179
      val lo = new FXColor(loColorPicker.value())
×
180
      val hi = new FXColor(hiColorPicker.value())
×
UNCOV
181
      updatePreview(grid => new RandomNoiseEffect(lo, hi).action(selectedImagesCoords, grid))
×
182

183
    loColorPicker.value.onChange((_, _, _) => updatePreviewFromInputs())
×
UNCOV
184
    hiColorPicker.value.onChange((_, _, _) => updatePreviewFromInputs())
×
185

UNCOV
186
    updatePreviewFromInputs()
×
187

UNCOV
188
    getValueFromCustomDialog[(Color, Color)](
×
189
      title = "Fill images randomly",
190
      headerText = "Which color-range should be used?",
191
      graphic = previewPane,
192
      content = Seq(
×
193
        makeGridPane(
×
194
          Seq(
×
195
            Seq(new Label("Minimum color:"), loColorPicker),
×
UNCOV
196
            Seq(new Label("Maximum color:"), hiColorPicker)
×
197
          )
198
        )
199
      ),
200
      resultConverter = {
201
        case ButtonType.OK =>
×
202
          Try((new FXColor(loColorPicker.value()), new FXColor(hiColorPicker.value())))
×
203
            .map((lo, hi) => (Color.fromFXColor(lo), Color.fromFXColor(hi)))
×
204
            .getOrElse(null)
×
UNCOV
205
        case _ => null
×
206
      },
UNCOV
207
      buttons = Seq(ButtonType.OK, ButtonType.Cancel)
×
208
    )
209
  }
210

211
  override def askSaveBeforeClosing(images: Seq[GridCell]): Option[Boolean] = {
×
212
    saveBeforeClosingAlert(images).showAndWait().map(_.buttonData) flatMap {
×
213
      case ButtonData.Yes => Some(true)
×
214
      case ButtonData.No  => Some(false)
×
UNCOV
215
      case _              => None
×
216
    }
217
  }
218

219
  private def saveBeforeClosingAlert(images: Seq[GridCell]): Alert = {
×
UNCOV
220
    val (previewPane, _) = DialogUtils.makeImagePreviewList(images, model.imagePool)
×
221

222
    val alert = new Alert(AlertType.Confirmation)
×
223
    alert.title = "Save before closing?"
×
224
    alert.headerText = "Do you want to save " + (if (images.size == 1) "this image"
×
225
                                                 else "these images") + " before closing the tab?"
×
UNCOV
226
    alert.graphic = previewPane
×
227

228
    alert.buttonTypes = Seq(
×
229
      new ButtonType("Save", ButtonData.Yes),
×
230
      new ButtonType("Don't save", ButtonData.No),
×
UNCOV
231
      new ButtonType("Cancel", ButtonData.CancelClose)
×
232
    )
233
    alert
234
  }
235

UNCOV
236
  override def askForFileOpenSettings(
×
237
      file: File,
238
      imageSize: Int,
239
      xCount: Int,
240
      yCount: Int
241
  ): Option[FileOpenSettings] = {
UNCOV
242
    AskForFileOpenSettingsDialog.askForFileOpenSettings(
×
243
      imagePreview = (file, imageSize, xCount, yCount),
244
      Seq(
×
245
        SimpleStorageFormat -> "Simple format",
×
UNCOV
246
        RecursiveStorageFormat -> "Recursive format"
×
247
      ),
248
      0,
249
      model.fileSystem
250
    )
251
  }
252

UNCOV
253
  override def shouldReplaceImage(
×
254
      currentImage: ImageStorage,
255
      newImage: ImageStorage,
256
      location: ImagePool.SaveLocation
257
  ): Option[Boolean] = {
258
    val tri1 = model.imageGrid.findByStorage(newImage).orNull
×
259
    val tri2 = model.imageGrid.findByStorage(currentImage).orNull
×
UNCOV
260
    val (previewPane, _) = DialogUtils.makeImagePreviewList(Seq(tri1, tri2), model.imagePool)
×
261

262
    val alert = new Alert(AlertType.Confirmation)
×
263
    alert.title = "Collision"
×
264
    alert.headerText = "The image already exists on the screen. Which one should be used?"
×
UNCOV
265
    alert.graphic = previewPane
×
266

267
    alert.buttonTypes = Seq(
×
268
      new ButtonType("Left", ButtonData.Yes),
×
269
      new ButtonType("Right", ButtonData.No),
×
UNCOV
270
      new ButtonType("Cancel", ButtonData.CancelClose)
×
271
    )
UNCOV
272
    alert.showAndWait().map(_.buttonData == ButtonData.Yes)
×
273
  }
274

275
  override def askForImageSize(): Option[Int] = {
×
276
    val dialog = new TextInputDialog("32")
×
277
    dialog.title = "Image Size"
×
278
    dialog.headerText = "What should be the image size? (#rows)"
×
279
    val sizeStr = dialog.showAndWait()
×
UNCOV
280
    sizeStr.map(str => Try(str.toInt).toOption.filter(_ > 0).getOrElse(32))
×
281
  }
282
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc