Skip to content

Commit

Permalink
Add Scale widget
Browse files Browse the repository at this point in the history
* debug attempt

* #83 Add first attempt at scale widget

* #83 Fix types on bindings

double in C translates to cdouble in nim, not cfloat.

* #83 improve examples and implementation

* stuff

* #83 Add support for initial widget value

* #83 Add support for inverting widget

* #83 fix some bugs

* #83 add update of ScaleState to change proc

* #83 let pageSize just default to double the pageSize

* #83 add support for showing or hiding fill level and precision

* #83 Fix bug causing infinite window growth

* #83 refactor state-variables to use property hooks

* #83 implement suggestion for value 2way bindings

* #83 Fix example

* #83 Add support for configuring value position

* #83 Make orientation configurable

* #83 Fix example datatype (this was int but needs to be float64)

* #83 Fix semantics on marks

* #83 Ensure order of hooks is the same as order of properties

* #83 Add build hook to make initial value assignment work again

* #83 slightly beautify example

* #83 Adjust proc-calling to code conventions

* #83 Add widgets example

* ##83 add doc comments to Scale widget

* #83 Update widgets.md

* Add notice that there's a nimble task to generate docs as well

* #83 Remove nonsense workflow change

* #83 Add scale to example README and mild improvements

* #83 Mild doc comment improvements

* #83 Fix error introduced by last commit in doc comments

* #83 Remove build hook

* #83 Move float64 to float

* #83 Minor doc update

* #83 Improve Scale example to make it more configurable

* Improve example

* Fix Text View example image width

---------

Co-authored-by: Can Lehmann <can.l@posteo.de>
  • Loading branch information
PhilippMDoerner and can-lehmann authored Oct 2, 2023
1 parent 38d2604 commit 90e5db1
Show file tree
Hide file tree
Showing 8 changed files with 388 additions and 0 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ examples/widgets/popover_menu
examples/widgets/picture
examples/widgets/grid
examples/widgets/radio_group
examples/widgets/scale
examples/widgets/adw/window_title
examples/widgets/adw/preferences_group
examples/widgets/adw/flap
Expand Down
2 changes: 2 additions & 0 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@ Since it is automatically generated from the `owlkettle/widgets.nim` module, run

```bash
make -C docs
# Alternatively
nimble genDocs
```

**Note:** Please do never change the `docs/widgets.md` file manually.
Expand Down
Binary file added docs/assets/examples/scale.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
46 changes: 46 additions & 0 deletions docs/widgets.md
Original file line number Diff line number Diff line change
Expand Up @@ -1430,3 +1430,49 @@ AboutDialog:
```


## Scale

```nim
renderable Scale of BaseWidget
```

A slider for choosing a numeric value within a range.

###### Fields

- All fields from [BaseWidget](#BaseWidget)
- `min: float = 0` Lower end of the range displayed by the scale
- `max: float = 100` Upper end of the range displayed by the scale
- `value: float = 0` The value the Scale widget displays. Remember to update it via your `valueChanged` proc to reflect the new value on the Scale widget.
- `marks: seq[ScaleMark] = @[]` Adds marks to the Scale at points where `ScaleMark.value` would be placed. If `ScaleMark.label` is provided, it will be rendered next to the mark. `ScaleMark.position` determines the mark's position (and its label) relative to the scale. Note that ScaleLeft and ScaleRight are only sensible when the Scale is vertically oriented (`orient` = `OrientY`), while ScaleTop and ScaleBottom are only sensible when it is horizontally oriented (`orient` = `OrientX`)
- `inverted: bool = false` Determines whether the min and max value of the Scale are ordered (low value) left => right (high value) in the case of `inverted = false` or (high value) left <= right (low value) in the case of `inverted = true`.
- `showValue: bool = true` Determines whether to display the numeric value as a label on the widget (`showValue = true`) or not (`showValue = false`)
- `stepSize: float = 5` Determines the value increment/decrement when the widget is in focus and the user presses arrow keys.
- `pageSize: float = 10` Determines the value increment/decrement when the widget is in focus and the user presses page keys. Typically larger than stepSize.
- `orient: Orient = OrientX` The orientation of the widget. Orients the widget either horizontally (`orient = OrientX`) or vertically (`orient = OrientY`)
- `showFillLevel: bool = true` Determines whether to color the Scale from the "origin" to the place where the slider on the Scale sits. The Scale is filled left => right/top => bottom if `inverted = false` and left <= right/top <= bottom if `inverted = true`
- `precision: int64 = 1` Number of decimal places to display for the value. `precision = 1` enables values like 1.2, while `precision = 2` enables values like 1.23 and so on.
- `valuePosition: ScalePosition` Specifies where the label of the Scale widget's value should be placed. This setting has no effect if `showValue = false`.

###### Events

- valueChanged: `proc (newValue: float)` Emitted when the range value changes from an interaction triggered by the user.

###### Example

```nim
Scale:
value = app.value
showFillLevel = false
min = 0
max = 1
marks = @[ScaleMark(some("Just a mark"), ScaleLeft, 0.5)]
inverted = true
showValue = false
proc valueChanged(newValue: float) =
echo "New value is ", newValue
app.value = newValue
```


4 changes: 4 additions & 0 deletions examples/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -159,6 +159,10 @@ The `widgets` directory contains examples for how to use different widgets.
<td><a href="https://github.com/can-lehmann/owlkettle/blob/main/examples/widgets/radio_group.nim">Radio Group</a></td>
<td><img alt="Radio Group" src="../docs/assets/examples/radio_group.png" width="522px"></td>
</tr>
<tr>
<td><a href="https://github.com/can-lehmann/owlkettle/blob/main/examples/widgets/scale.nim">Scale</a></td>
<td><img alt="Scale Widget" src="../docs/assets/examples/scale.png" width="922px"></td>
</tr>
<tr>
<td><a href="https://github.com/can-lehmann/owlkettle/blob/main/examples/widgets/text_view.nim">Text View</a></td>
<td><img alt="Text View" src="../docs/assets/examples/text_view.png" width="757px"></td>
Expand Down
198 changes: 198 additions & 0 deletions examples/widgets/scale.nim
Original file line number Diff line number Diff line change
@@ -0,0 +1,198 @@
# MIT License
#
# Copyright (c) 2022 Can Joshua Lehmann
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in all
# copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE.

import owlkettle, owlkettle/[dataentries, adw]
import std/[options, sequtils]

viewable App:
min: float = 0
max: float = 100
value: float = 50
marks: seq[ScaleMark] = @[]
inverted: bool = false
showValue: bool = true
stepSize: float = 5
pageSize: float = 10
orient: Orient = OrientX
showFillLevel: bool = true
precision: int64 = 1
valuePosition: ScalePosition

method view(app: AppState): Widget =
result = gui:
WindowSurface:
defaultSize = (800, 600)

Box(orient = OrientX):
Box(orient = OrientY) {.expand: false.}:
sizeRequest = (300, -1)

HeaderBar {.expand: false.}:
showTitleButtons = false

ScrolledWindow:
PreferencesGroup:
title = "Fields"
margin = 12

ActionRow:
title = "min"
NumberEntry {.addSuffix.}:
value = app.min
xAlign = 1.0
maxWidth = 8
proc changed(value: float) =
app.min = value

ActionRow:
title = "max"
NumberEntry {.addSuffix.}:
value = app.max
xAlign = 1.0
maxWidth = 8
proc changed(value: float) =
app.max = value

ActionRow:
title = "value"
NumberEntry {.addSuffix.}:
value = app.value
xAlign = 1.0
maxWidth = 8
proc changed(value: float) =
app.value = value

ActionRow:
title = "inverted"
Switch {.addSuffix.}:
state = app.inverted
proc changed(state: bool) =
app.inverted = state

ActionRow:
title = "showValue"
Switch {.addSuffix.}:
state = app.showValue
proc changed(state: bool) =
app.showValue = state

ActionRow:
title = "stepSize"
NumberEntry {.addSuffix.}:
value = app.stepSize
xAlign = 1.0
maxWidth = 8
proc changed(value: float) =
app.stepSize = value

ActionRow:
title = "pageSize"
NumberEntry {.addSuffix.}:
value = app.pageSize
xAlign = 1.0
maxWidth = 8
proc changed(value: float) =
app.pageSize = value

ActionRow:
title = "showFillLevel"
Switch {.addSuffix.}:
state = app.showFillLevel
proc changed(state: bool) =
app.showFillLevel = state

ActionRow:
title = "precision"
NumberEntry {.addSuffix.}:
value = app.precision.float
xAlign = 1.0
maxWidth = 8
proc changed(value: float) =
app.precision = value.int

ComboRow:
title = "orient"
items = mapIt(low(Orient)..high(Orient), $it)
selected = ord(app.orient)
proc select(index: int) =
app.orient = Orient(index)

ComboRow:
title = "valuePosition"
items = mapIt(low(ScalePosition)..high(ScalePosition), $it)
selected = ord(app.valuePosition)
proc select(index: int) =
app.valuePosition = ScalePosition(index)

ExpanderRow:
title = "marks"

for it, mark in app.marks:
ActionRow {.addRow.}:
title = "Mark " & $it

NumberEntry {.addSuffix.}:
value = mark.value
xAlign = 1.0
maxWidth = 8
proc changed(value: float) =
app.marks[it].value = value

Button {.addSuffix.}:
icon = "user-trash-symbolic"
proc clicked() =
app.marks.delete(it)

ListBoxRow {.addRow.}:
Button:
icon = "list-add-symbolic"
style = [ButtonFlat]
proc clicked() =
app.marks.add(ScaleMark())

Separator() {.expand: false.}

Box(orient = OrientY):
HeaderBar {.expand: false.}:
WindowTitle {.addTitle.}:
title = "Scale Example"
subtitle = "Value: " & $app.value

Scale:
min = app.min
max = app.max
value = app.value
marks = app.marks
inverted = app.inverted
showValue = app.showValue
stepSize = app.stepSize
pageSize = app.pageSize
orient = app.orient
showFillLevel = app.showFillLevel
precision = app.precision
valuePosition = app.valuePosition

proc valueChanged(newValue: float64) =
app.value = newValue
echo "New value from Scale is ", $newValue

adw.brew(gui(App()))
18 changes: 18 additions & 0 deletions owlkettle/gtk.nim
Original file line number Diff line number Diff line change
Expand Up @@ -672,6 +672,24 @@ proc gtk_scrolled_window_get_hadjustment*(window: GtkWidget): GtkAdjustment
proc gtk_scrolled_window_get_vadjustment*(window: GtkWidget): GtkAdjustment
proc gtk_scrolled_window_set_child*(window, child: GtkWidget)

# Gtk.Range
proc gtk_range_get_value*(widget: GtkWidget): cdouble
proc gtk_range_set_increments*(widget: GtkWidget; step, page: cdouble)
proc gtk_range_set_range*(widget: GtkWidget; min, max: cdouble)
proc gtk_range_set_value*(widget: GtkWidget, value: cdouble)
proc gtk_range_set_inverted*(widget: GtkWidget, setting: cbool)
proc gtk_range_get_inverted*(widget: GtkWidget): cbool
proc gtk_range_set_slider_size_fixed*(widget: GtkWidget, size_fixed: cbool)

# Gtk.Scale
proc gtk_scale_new*(widget: GtkOrientation, adjustment: GtkAdjustment): GtkWidget
proc gtk_scale_add_mark*(widget: GtkWidget, value: cdouble, position: GtkPositionType, markup: cstring)
proc gtk_scale_clear_marks*(widget: GtkWidget)
proc gtk_scale_set_draw_value*(widget: GtkWidget, draw_value: cbool)
proc gtk_scale_set_has_origin*(widget: GtkWidget, has_origin: cbool)
proc gtk_scale_set_value_pos*(widget: GtkWidget, pos: GtkPositionType)
proc gtk_scale_set_digits*(widget: GtkWidget, digits: cint)

# Gtk.IconTheme
proc gtk_icon_theme_new*(): GtkIconTheme
proc gtk_icon_theme_get_for_display*(display: GdkDisplay): GtkIconTheme
Expand Down
Loading

0 comments on commit 90e5db1

Please sign in to comment.