Skip to content

Commit 3e31e62

Browse files
authoredDec 29, 2024··
Optionally snap slider to range (#74)
Also require Julia 1.10, update CI and require TestImages 1.9 for tests.
1 parent 473a018 commit 3e31e62

File tree

7 files changed

+90
-26
lines changed

7 files changed

+90
-26
lines changed
 

‎.github/workflows/CI.yml

+3-4
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ jobs:
1515
fail-fast: false
1616
matrix:
1717
os: [ubuntu-latest, windows-latest, macOS-latest]
18-
version: ['1.6', '1', 'nightly']
18+
version: ['1', 'nightly']
1919
arch: [x64, x86]
2020
include:
2121
- os: ubuntu-latest
@@ -29,9 +29,8 @@ jobs:
2929
with:
3030
version: ${{ matrix.version }}
3131
arch: ${{ matrix.arch }}
32-
- uses: julia-actions/cache@v1
33-
- uses: julia-actions/julia-buildpkg@latest
34-
- uses: julia-actions/julia-runtest@latest
32+
- uses: julia-actions/cache@v2
33+
- uses: julia-actions/julia-runtest@master
3534
with:
3635
prefix: ${{ matrix.prefix }}
3736
- uses: julia-actions/julia-processcoverage@latest

‎Project.toml

+3-13
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
name = "GtkObservables"
22
uuid = "8710efd8-4ad6-11eb-33ea-2d5ceb25a41c"
3-
version = "2.1.4"
3+
version = "2.2.0"
44

55
[deps]
66
Cairo = "159f3aea-2a34-519c-b102-8c37f9878175"
@@ -19,7 +19,7 @@ RoundingIntegers = "d5f540fe-1c90-5db3-b776-2e2f362d9394"
1919
[compat]
2020
Cairo = "1"
2121
Colors = "0.12, 0.13"
22-
Dates = "1.6"
22+
Dates = "1.10"
2323
FixedPointNumbers = "0.8"
2424
Graphics = "1"
2525
Gtk4 = "0.6, 0.7"
@@ -29,15 +29,5 @@ Observables = "0.4, 0.5"
2929
PrecompileTools = "1"
3030
Reexport = "0.2, 1"
3131
RoundingIntegers = "0.2, 1"
32-
julia = "1.6"
32+
julia = "1.10"
3333

34-
[extras]
35-
Cairo = "159f3aea-2a34-519c-b102-8c37f9878175"
36-
FileIO = "5789e2e9-d7fb-5bc7-8068-2c6fae9b9549"
37-
IdentityRanges = "bbac6d45-d8f3-5730-bfe4-7a449cd117ca"
38-
ImageIO = "82e4d734-157c-48bb-816b-45c225c6df19"
39-
Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40"
40-
TestImages = "5e47fb64-e119-507b-a336-dd2b206d9990"
41-
42-
[targets]
43-
test = ["FileIO", "IdentityRanges", "ImageIO", "Cairo", "Test", "TestImages"]

‎docs/src/controls.md

+6-1
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ At present, this slider is not affiliated with any window. Let's
2929
create one and add the slider to the window. We'll put it inside a
3030
`Box` so that we can later add more things to this GUI (this
3131
illustrates usage of some of
32-
[Gtk's layout tools](https://juliagtk.github.io/Gtk4.jl/dev/manual/layout/):
32+
[Gtk4's layout tools](https://juliagtk.github.io/Gtk4.jl/dev/manual/layout/):
3333

3434
```jldoctest demo1
3535
julia> win = GtkWindow("Testing"); win[] = bx = GtkBox(:v); # a window containing a vertical Box for layout
@@ -74,6 +74,11 @@ end
7474

7575
Now if you check the window, you'll see that the slider is at 1.
7676

77+
Note: The `slider` function used above to construct a slider widget by default does not
78+
snap to elements in the provided range (except in some circumstances). By setting
79+
the keyword argument `snap` to `true`, the `GtkScale` should reliably snap to
80+
elements of the range. This does not affect observable's `setindex!` method.
81+
7782
Realistic GUIs may have many different widgets. Let's add a second way
7883
to adjust the value of that observable, by allowing the user to type a
7984
value into a textbox:

‎examples/snap_slider.jl

+13
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
using GtkObservables, Gtk4
2+
3+
r=1:10:91
4+
#r=0.1:0.05:10 also works
5+
n = slider(r; snap=true)
6+
7+
mainwin = GtkWindow("GtkObservables", 300, 200)
8+
vbox = GtkBox(:v)
9+
push!(vbox, GtkLabel("Demonstrates slider that snaps to $(r)"))
10+
push!(vbox, n)
11+
push!(mainwin, vbox)
12+
show(mainwin)
13+

‎src/widgets.jl

+40-8
Original file line numberDiff line numberDiff line change
@@ -95,38 +95,65 @@ struct Slider{T<:Number} <: InputWidget{T}
9595
observable::Observable{T}
9696
widget::GtkScaleLeaf
9797
id::Culong
98+
snap_handler_id::Ref{Culong}
9899
preserved::Vector{Any}
99100

100-
function Slider{T}(observable::Observable{T}, widget, id, preserved) where T
101-
obj = new{T}(observable, widget, id, preserved)
101+
function Slider{T}(observable::Observable{T}, widget, id, snap_id, preserved) where T
102+
obj = new{T}(observable, widget, id, snap_id, preserved)
102103
gc_preserve(widget, obj)
103104
obj
104105
end
105106
end
106-
Slider(observable::Observable{T}, widget::GtkScale, id, preserved) where {T} =
107-
Slider{T}(observable, widget, id, preserved)
107+
Slider(observable::Observable{T}, widget::GtkScale, id, snap_id, preserved) where {T} =
108+
Slider{T}(observable, widget, id, snap_id, preserved)
108109

109110
medianidx(r) = (ax = axes(r)[1]; return (first(ax)+last(ax))÷2)
110111
# differs from median(r) in that it always returns an element of the range
111112
medianelement(r::AbstractRange) = r[medianidx(r)]
112113

113-
slider(observable::Observable, widget::GtkScale, id, preserved = []) =
114-
Slider(observable, widget, id, preserved)
114+
# simplified version of function in https://github.com/joshday/SearchSortedNearest.jl
115+
function searchsortednearest(a, x)
116+
i = searchsortedfirst(a, x)
117+
if i == 1
118+
elseif i > length(a)
119+
i = length(a)
120+
elseif a[i] == x
121+
else
122+
i = isless(abs(a[i] - x), abs(a[i - 1] - x)) ? i : i - 1
123+
end
124+
return i
125+
end
126+
127+
@guarded Cint(0) function slider_changevalue_cb(ptr::Ptr, scroll, value::Float64, user_data)
128+
scale = convert(GtkScale, ptr)
129+
r, idr = user_data
130+
i = searchsortednearest(r, value)
131+
signal_handler_block(scale, idr[])
132+
signal_emit(scale, "change-value", Bool, Gtk4.ScrollType(scroll), Float64(r[i]))
133+
signal_handler_unblock(scale, idr[])
134+
return Cint(1)
135+
end
136+
137+
138+
slider(observable::Observable, widget::GtkScale, id, snap_id, preserved = []) =
139+
Slider(observable, widget, id, snap_id, preserved)
115140

116141
"""
117-
slider(range; widget=nothing, value=nothing, observable=nothing, orientation="horizontal")
142+
slider(range; widget=nothing, value=nothing, observable=nothing, orientation="horizontal", snap=false)
118143
119144
Create a slider widget with the specified `range`. Optionally provide:
120145
- the GtkScale `widget` (by default, creates a new one)
121146
- the starting `value` (defaults to the median of `range`)
122147
- the (Observables.jl) `observable` coupled to this slider (by default, creates a new observable)
123148
- the `orientation` of the slider.
149+
- whether to `snap` the slider value to an element of `range`.
124150
"""
125151
function slider(range::AbstractRange;
126152
widget=nothing,
127153
value=nothing,
128154
observable=nothing,
129155
orientation="horizontal",
156+
snap=false,
130157
syncsig=true,
131158
own=nothing)
132159
obsin = observable
@@ -155,6 +182,11 @@ function slider(range::AbstractRange;
155182
observable[] = defaultgetter(w)
156183
end
157184

185+
ref_snap_id = Ref{Culong}(0)
186+
if snap
187+
ref_snap_id[] = Gtk4.on_change_value(slider_changevalue_cb, widget, (range,ref_snap_id))
188+
end
189+
158190
## observable -> widget
159191
preserved = []
160192
if syncsig
@@ -164,7 +196,7 @@ function slider(range::AbstractRange;
164196
ondestroy(widget, preserved)
165197
end
166198

167-
Slider(observable, widget, id, preserved)
199+
Slider(observable, widget, id, ref_snap_id, preserved)
168200
end
169201

170202
# Adjust the range on a slider

‎test/Project.toml

+18
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
[deps]
2+
Cairo = "159f3aea-2a34-519c-b102-8c37f9878175"
3+
Colors = "5ae59095-9a9b-59fe-a467-6f913c188581"
4+
Dates = "ade2ca70-3891-5945-98fb-dc099432e06a"
5+
FileIO = "5789e2e9-d7fb-5bc7-8068-2c6fae9b9549"
6+
FixedPointNumbers = "53c48c17-4a7d-5ca2-90c5-79b7896eea93"
7+
Graphics = "a2bd30eb-e257-5431-a919-1863eab51364"
8+
Gtk4 = "9db2cae5-386f-4011-9d63-a5602296539b"
9+
IdentityRanges = "bbac6d45-d8f3-5730-bfe4-7a449cd117ca"
10+
ImageIO = "82e4d734-157c-48bb-816b-45c225c6df19"
11+
IntervalSets = "8197267c-284f-5f27-9208-e0e47529a953"
12+
Observables = "510215fc-4207-5dde-b226-833fc4488ee2"
13+
RoundingIntegers = "d5f540fe-1c90-5db3-b776-2e2f362d9394"
14+
Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40"
15+
TestImages = "5e47fb64-e119-507b-a336-dd2b206d9990"
16+
17+
[compat]
18+
TestImages = "=1.9"

‎test/runtests.jl

+7
Original file line numberDiff line numberDiff line change
@@ -142,6 +142,13 @@ Gtk4.GLib.start_main_loop()
142142
@test s[] == 5
143143
sleep(0.1)
144144

145+
# Snap to range element
146+
s = slider(0.1:0.05:10.0; snap = true)
147+
sleep(0.01)
148+
signal_emit(widget(s),"change-value",Bool, Gtk4.ScrollType(0), 0.16)
149+
sleep(0.01)
150+
@test s[] == 0.15
151+
145152
## dropdown
146153
dd = dropdown(("Strawberry", "Vanilla", "Chocolate"))
147154
@test dd[] === "Strawberry"

0 commit comments

Comments
 (0)
Please sign in to comment.