Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add scrollbar widget + Full wrapping of GtkAdjustment #126

Draft
wants to merge 10 commits into
base: main
Choose a base branch
from
62 changes: 62 additions & 0 deletions examples/widgets/scrollbar.nim
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
# 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 std/[sequtils]
import owlkettle, owlkettle/[dataentries, playground, adw]

viewable App:
items: seq[string] = (1..100).mapIt("Entry " & $it)
orient: Orient = OrientY
sensitive: bool = true
tooltip: string = ""
sizeRequest: tuple[x, y: int] = (-1, -1)

#Adjustment
value: float = 0.0
lower: float = 0.0
upper: float = 100.0
stepIncrement: float = 0.0
pageIncrement: float = 0.0
pageSize: float = 0.0

method view(app: AppState): Widget =
let adj = newAdjustment(app.value, app.lower, app.upper, app.stepIncrement, app.pageIncrement, app.pageSize)
result = gui:
Window():
title = "Orient Example"
defaultSize = (800, 600)
HeaderBar() {.addTitlebar.}:
insert(app.toAutoFormMenu()) {.addRight.}

ScrolledWindow:
ScrollBar:
orient = app.orient
adjustment = adj
sensitive = app.sensitive
tooltip = app.tooltip
sizeRequest = app.sizeRequest

proc scrolled(newValue: float) =
echo "newValue: ", newValue

proc adjustmentChanged() = echo "Adjustment changed"
adw.brew(gui(App()))
18 changes: 18 additions & 0 deletions owlkettle/bindings/gtk.nim
Original file line number Diff line number Diff line change
Expand Up @@ -763,6 +763,21 @@ proc gtk_adjustment_set_upper*(adjustment: GtkAdjustment, value: cdouble)
proc gtk_adjustment_set_step_increment*(adjustment: GtkAdjustment, value: cdouble)
proc gtk_adjustment_set_page_increment*(adjustment: GtkAdjustment, value: cdouble)
proc gtk_adjustment_set_page_size*(adjustment: GtkAdjustment, value: cdouble)
proc gtk_adjustment_clamp_page*(adjustment: GtkAdjustment, lower: cdouble, upper: cdouble)
proc gtk_adjustment_configure*(adjustment: GtkAdjustment, value: cdouble, lower: cdouble, upper: cdouble, step_increment: cdouble, page_increment: cdouble, page_siz: cdouble)
proc gtk_adjustment_get_lower*(adjustment: GtkAdjustment): cdouble
proc gtk_adjustment_get_minimum_increment*(adjustment: GtkAdjustment): cdouble
proc gtk_adjustment_get_page_increment*(adjustment: GtkAdjustment): cdouble
proc gtk_adjustment_get_page_size*(adjustment: GtkAdjustment): cdouble
proc gtk_adjustment_get_step_increment*(adjustment: GtkAdjustment): cdouble
proc gtk_adjustment_get_upper*(adjustment: GtkAdjustment): cdouble
proc gtk_adjustment_get_value*(adjustment: GtkAdjustment): cdouble


# Gtk.Scrollbar
proc gtk_scrollbar_new*(orientation: GtkOrientation, adjustment: GtkAdjustment): GtkWidget
proc gtk_scrollbar_set_adjustment*(self: GtkWidget, adjustment: GtkAdjustment)
proc gtk_scrollbar_get_adjustment*(self: GtkWidget): GtkAdjustment

# Gtk.ScrolledWindow
proc gtk_scrolled_window_new*(hAdjustment, vAdjustment: GtkAdjustment): GtkWidget
Expand Down Expand Up @@ -1261,6 +1276,9 @@ proc g_signal_connect*(app: GtkListItemFactory, signal: cstring, closure, data:

proc g_signal_connect*(app: GtkSelectionModel, signal: cstring, closure, data: pointer): culong =
result = g_signal_connect_data(app.pointer, signal, closure, data, nil, G_CONNECT_AFTER)

proc g_signal_connect*(adj: GtkAdjustment, signal: cstring, closure, data: pointer): culong =
result = g_signal_connect_data(adj.pointer, signal, closure, data, nil, G_CONNECT_AFTER)
{.pop.}

template withCArgs(argc, argv, body: untyped) =
Expand Down
109 changes: 108 additions & 1 deletion owlkettle/widgets.nim
Original file line number Diff line number Diff line change
Expand Up @@ -3715,7 +3715,113 @@ renderable Scale of BaseWidget:
echo "New value is ", newValue
app.value = newValue

proc newAdjustment*(
value = 0.0,
lower = 0.0,
upper = 0.0,
stepIncrement = 0.0,
pageIncrement = 0.0,
pageSize: float = 0.0
): GtkAdjustment =
gtk_adjustment_new(value.cdouble, lower.cdouble, upper.cdouble, stepIncrement.cdouble, pageIncrement.cdouble, pageSize.cdouble)

proc value*(adjustment: GtkAdjustment): float =
gtk_adjustment_get_value(adjustment).float
proc lower*(adjustment: GtkAdjustment): float =
gtk_adjustment_get_lower(adjustment).float
proc upper*(adjustment: GtkAdjustment): float =
gtk_adjustment_get_upper(adjustment).float
proc stepIncrement*(adjustment: GtkAdjustment): float =
gtk_adjustment_get_step_increment(adjustment).float
proc pageIncrement*(adjustment: GtkAdjustment): float =
gtk_adjustment_get_page_increment(adjustment).float
proc pageSize*(adjustment: GtkAdjustment): float =
gtk_adjustment_get_page_size(adjustment).float
proc minimumIncrement*(adjustment: GtkAdjustment): float =
gtk_adjustment_get_minimum_increment(adjustment).float
proc `value=`*(adjustment: GtkAdjustment, newValue: float) =
gtk_adjustment_set_value(adjustment, newValue.cdouble)
proc `lower=`*(adjustment: GtkAdjustment, newValue: float) =
gtk_adjustment_set_lower(adjustment, newValue.cdouble)
proc `upper=`*(adjustment: GtkAdjustment, newValue: float) =
gtk_adjustment_set_upper(adjustment, newValue.cdouble)
proc `stepIncrement=`*(adjustment: GtkAdjustment, newValue: float) =
gtk_adjustment_set_step_increment(adjustment, newValue.cdouble)
proc `pageIncrement=`*(adjustment: GtkAdjustment, newValue: float) =
gtk_adjustment_set_page_increment(adjustment, newValue.cdouble)
proc `pageSize=`*(adjustment: GtkAdjustment, newValue: float) =
gtk_adjustment_set_page_size(adjustment, newValue.cdouble)
proc configure*(adjustment: GtkAdjustment, value: float, lower: float, upper: float, step_increment: float, page_increment: float, page_siz: float) =
gtk_adjustment_configure(adjustment, value.cdouble, lower.cdouble, upper.cdouble, step_increment.cdouble, page_increment.cdouble, page_siz.cdouble)

# proc `valueChangedHandler=`*(adjustment: GtkAdjustment, handler: proc(newValue: float)) =
# proc valueChangedCallback(adjustment: GtkAdjustment, data: ptr EventObj[proc (newValue: float)]) {.cdecl.} =
# let event = unwrapSharedCell(data)
# event.callback(adjustment.value)

# let event = EventObj[proc(newValue: float)]()
# let data = allocSharedCell(event)
# data.callback = handler
# data.handler = g_signal_connect(adjustment, "value-changed".cstring, valueChangedCallback, data)

# proc `adjustmentChangedHandler=`*(adjustment: GtkAdjustment, handler: proc()) =
# proc adjustmentChangedCallback(adjustment: GtkAdjustment, data: ptr EventObj[proc()]) {.cdecl.} =
# let event = unwrapSharedCell(data)
# event.callback()

# let event = EventObj[proc()]()
# let data = allocSharedCell(event)
# data.callback = handler
# data.handler = g_signal_connect(adjustment, "changed".cstring, adjustmentChangedCallback, data)

# TODO: Add hooks

proc `==`*(x, y: GtkAdjustment): bool = x.pointer == y.pointer

renderable Scrollbar of BaseWidget:
## A scrollbar widget whose properties can be customized via `GtkAdjustment`.
## Use `newAdjustment` to create a GtkAdjustment.
## `GtkAdjustment` has the following properties that can be assigned to:
## - value: The position of the scrollbar within the upper and lower bounds. If e.g. value/upper = 0.5, the scrollbar will be at 50% height.
## - lower: The lower bound for the value of the scrollbar. If `value` == `lower`, then the scrollbar is at the top of the page.
## - upper: The upper bound for the value of the scrollbar. If `value` == `upper`, then the scrollbar is at the bottom of the page.
## - stepIncrement
## - pageIncrement
## - pageSize: The size of the Scrollbar, also relative to the upper and lower bounds. If e.g. `pageSize` / (`upper`- `lower`) = 0.2, then the scrollbar will take up 20% of the total available height.
orient: Orient = OrientY
adjustment: GtkAdjustment = nil.GtkAdjustment

proc scrolled(newValue: float)
proc adjustmentChanged()

hooks:
beforeBuild:
state.internalWidget = gtk_scrollbar_new(state.orient.toGtk(), nil.GtkAdjustment)
connectEvents:
proc adjustmentCallback(adjustment: GtkAdjustment, data: ptr EventObj[proc()]){.cdecl.} =
echo "CB triggered?"
data[].callback()
data[].redraw()

proc scrollCallback(adjustment: GtkAdjustment, data: ptr EventObj[proc(newValue: float)]){.cdecl.} =
echo "CB triggered?"
data[].callback(adjustment.value)
data[].redraw()

state.adjustment.connect(state.scrolled, "changed", adjustmentCallback)
state.adjustment.connect(state.adjustmentChanged, "changed", scrollCallback)
disconnectEvents:
state.adjustment.disconnect(state.scrolled)
state.adjustment.disconnect(state.adjustmentChanged)

hooks orient:
property:
gtk_orientable_set_orientation(state.internalWidget, state.orient.toGtk())

hooks adjustment:
property:
gtk_scrollbar_set_adjustment(state.internalWidget, state.adjustment)

# See TODO at comment of PixbufObj regarding why we wrap GtkMediaStream with MediaStreamObj
type
MediaStreamObj = object
Expand Down Expand Up @@ -4264,4 +4370,5 @@ export EditableLabel
export PasswordEntry
export CenterBox
export ListView
export ActionBar
export ScrollBar
export ActionBar
15 changes: 15 additions & 0 deletions owlkettle/widgetutils.nim
Original file line number Diff line number Diff line change
Expand Up @@ -56,13 +56,28 @@ proc connect*[T](renderable: Renderable,
event.widget = renderable
event.handler = g_signal_connect(renderable.internalWidget, name, eventCallback, event[].addr)

proc connect*[T](adjustment: GtkAdjustment,
event: Event[T],
name: cstring,
eventCallback: pointer) =
if not event.isNil:
event.widget = nil
event.handler = g_signal_connect(adjustment, name, eventCallback, event[].addr)

proc disconnect*[T](widget: GtkWidget, event: Event[T]) =
if not event.isNil:
assert event.handler > 0
g_signal_handler_disconnect(pointer(widget), event.handler)
event.handler = 0
event.widget = nil

proc disconnect*[T](adj: GtkAdjustment, event: Event[T]) =
if not event.isNil:
assert event.handler > 0
g_signal_handler_disconnect(pointer(adj), event.handler)
event.handler = 0
event.widget = nil

proc updateStyle*[State, Widget](state: State, widget: Widget) {.inline.} =
mixin classes
if widget.hasPrivateStyle:
Expand Down