Open can-lehmann opened 9 months ago
Started tinkering around with this one.
I'm getting Segfaults when the input is handled.
That is explicitly within inputEventCallback
due to nil access when casting it to SpinRowState.
renderable SpinRow of ActionRow:
climbRate: float = 0.0
digits: uint = 0
numeric: bool = false
min: float = 0.0
max: float = 100.0
snapToTicks: bool = false
updatePolicy: SpinButtonUpdatePolicy = SpinButtonUpdateAlways
value: float = 0.0
wrap: bool = false
proc input(newValue: float)
hooks:
beforeBuild:
let climbRate: float = if widget.hasClimbRate: widget.valClimbRate else: 0.0
let digits: uint = if widget.hasDigits: widget.valDigits else: 0.uint
state.internalWidget = adw_spin_row_new(GtkAdjustment(nil), climbRate.cdouble, digits.cuint)
connectEvents:
proc inputEventCallback(
widget: GtkWidget,
newValue: cdouble,
data: ptr EventObj[proc(newValue: float)]
): cint {.cdecl.} =
let x = data[].widget
echo data == nil # false
echo data[].widget == nil # false
echo SpinRowState(data[].widget) == nil ## Line 740, this line segfaults according to the stacktrace
SpinRowState(data[].widget).value = newValue.float
data[].callback(newValue)
data[].redraw()
result = true.cint
state.connect(state.input, "input", inputEventCallback)
... tons of property hooks that all seem to work fine even though I don't quite get what some of the properties do...
stacktrace:
false
false
Traceback (most recent call last)
/home/philipp/dev/owlkettle/examples/widgets/adw/spin_row.nim(63) spin_row
/home/philipp/dev/owlkettle/owlkettle/adw.nim(912) brew
/home/philipp/dev/owlkettle/owlkettle/mainloop.nim(96) runMainloop
/home/philipp/dev/owlkettle/owlkettle/adw.nim(740) inputEventCallback
/home/philipp/.choosenim/toolchains/nim-2.0.0/lib/system/arc.nim(238) isObjDisplayCheck
SIGSEGV: Illegal storage access. (Attempt to read from nil?)
I don't quite see why this blows up on me. I'm trying to cross reference with stuff from my Scale Example and Spinbutton but nothing really jumps out to me.
Honestly I don't even quite get the GTK Explanation of what that signal is good for:
Emitted to convert the user’s input into a double value.
Like what? The main reason I wrap this because I assume this is what gets called if you change the value in general on this widget.
Any idea what I could look into to try and solve this? I'm not even sure where to look next as the fact that it only blows up inside of inputEventCallback
tells me that inputEventCallback
itself is perfectly fine, just somehow the casting to SpinRowState
kills me despite the widget on data not being nil.
I'm a bit flummoxed.
(Edit: On an unrelated sidenote: That PR to be able to deal with different adwaita versions paid of so fast it made my head spin. Literally merged the PR and the next issue I stare at requires me to check for Adwaita version 1.4 :laughing: )
One thing I noticed is that your signal handler takes a newValue: cdouble
, but GTK passes gdouble*
.
The idea behind input is that you can use a custom parsing function. (idk, maybe parse inputs with units like 10cm
to 0.1m
)
One thing I noticed is that your signal handler takes a
newValue: cdouble
, but GTK passesgdouble*
.
is that not the same? Wait does that star make this a ptr cdouble?
I officially do not know where the actual value I type into the field comes from or is supposed to come from.
It's not newValue
because even unref'd whatever is in there is nowhere close whatever I type into the spinButton.
It's neither adw_spin_row_get_value(widget).float
because somehow that always returns 0.
If I type in 90 into that value field, it'll do a lot but not return me 90 anywhere. There's also some really weird behaviour in that it sometimes defaults back to 0 and sometimes it sticks with the value, which I assume is because the event-callback isn't getting called because it's somehow bricked for a reason I can't discern.
Hand --> Face
Seriously? The spinbutton wants you to use gtk_editable_get_text
to fetch the userinput as text. Okay I tend to just not understand gtk sometimes, this should do it.
I know that's what was written directly in the docs, it just seemed so out of pocket that I assumed it was for something else entirely and discarded that sentence.
Yeah I'm as stuck as I was yesterday. I updated the example on the branch a bit as well as the widget, but basically I can't get this to work properly.
Any value you input does not stick.
A change to the value for some reason does not trigger the property hook that would update the gtk widget.
Even if you update the gtk-widget inside of the custom eventCallback the value doesn't stick.
If you update a value 3-5 times it somehow crashes the widget because suddenly SpinRowState(data[].widget)
becomes an invalid object conversion.
Updating the newValueHolder: ptr cdouble
parameter doesn't do anything anywhere that I can see.
I'm pretty much stuck. I guess the nice thing is that either way I already did most of the typing work ?
Below the code on the widget to provide cursory glances:
when AdwVersion >= (1, 4):
renderable SpinRow of ActionRow:
climbRate: float = 0.0
digits: uint = 0
numeric: bool = false
min: float = 0.0
max: float = 100.0
snapToTicks: bool = false
updatePolicy: SpinButtonUpdatePolicy = SpinButtonUpdateAlways
value: float = 0.0
wrap: bool = false
parseToFloat: (proc(input: string): float) = parseFloat
proc input(newValue: float)
hooks:
beforeBuild:
let climbRate: float = if widget.hasClimbRate: widget.valClimbRate else: 0.0
let digits: uint = if widget.hasDigits: widget.valDigits else: 0.uint
state.internalWidget = adw_spin_row_new(GtkAdjustment(nil), climbRate.cdouble, digits.cuint)
connectEvents:
proc inputEventCallback(
widget: GtkWidget,
newValueHolder: ptr cdouble,
data: ptr EventObj[proc(newValue: float)]
): cint {.cdecl.} =
var newValue: float
try:
newValue = SpinRowState(data[].widget).parseToFloat($gtk_editable_get_text(widget))
except ValueError as e:
return GtkInputError
echo "Input event callback: ", newValue
echo "\n"
newValueHolder[] = newValue.cdouble
SpinRowState(data[].widget).value = newValue
data[].callback(newValue)
data[].redraw()
return true.cint
state.connect(state.input, "input", inputEventCallback)
# state.connect(state.output, "output", eventCallback)
# state.connect(state.wrapped, "wrapped", eventCallback)
hooks climbRate:
property:
adw_spin_row_set_climb_rate(state.internalWidget, state.climbRate.cdouble)
hooks digits:
property:
adw_spin_row_set_digits(state.internalWidget, state.digits.cuint)
hooks numeric:
property:
adw_spin_row_set_numeric(state.internalWidget, state.numeric.cbool)
hooks min:
property:
adw_spin_row_set_range(state.internalWidget, state.min.cdouble, state.max.cdouble)
hooks max:
property:
adw_spin_row_set_range(state.internalWidget, state.min.cdouble, state.max.cdouble)
hooks snapToTicks:
property:
adw_spin_row_set_snap_to_ticks(state.internalWidget, state.snapToTicks.cbool)
hooks updatePolicy:
property:
adw_spin_row_set_update_policy(state.internalWidget, state.updatePolicy)
hooks value:
property:
echo "Value was updated to: ", state.value
adw_spin_row_set_value(state.internalWidget, state.value.cdouble)
hooks wrap:
property:
adw_spin_row_set_wrap(state.internalWidget, state.wrap.cbool)
export SpinRow
A change to the value for some reason does not trigger the property hook that would update the gtk widget.
You currently don't have a changed/valueChanged/... callback (not sure what it is called for SpinRow), that makes a 2-way binding possible. input
should only be used for parsing as far as I know.
A change to the value for some reason does not trigger the property hook that would update the gtk widget.
You currently don't have a changed/valueChanged/... callback (not sure what it is called for SpinRow), that makes a 2-way binding possible.
input
should only be used for parsing as far as I know.
Tried that just now by trying to start off simple and only doing changed.
You still have to fetch the value via parseFloat($gtk_editable_get_text(widget))
because proc adw_spin_row_get_value(self: GtkWidget): cdouble
nets you the old value 100% of the time after.
So with this approach, I have a widget. However that widget has the following bugs:
max
is somehow allowed and is not immediately limited to max0
counts for some reason) you get ObjectConversionDefects and those occuring repeatedly it causes a segfault at these lines: SpinRowState(data[].widget).value = newValue
because somehow data[].widget
turns into nil (?)Here the ObjectConversionStackTrace echo'd out:
ref Exception(parent: nil, name: "ObjectConversionDefect", msg: "invalid object conversion", trace:
@[
StackTraceEntry(procname: "spin_row", line: 61, filename: "/home/philipp/dev/owlkettle/examples/widgets/adw/spin_row.nim"),
StackTraceEntry(procname: "brew", line: 920, filename: "/home/philipp/dev/owlkettle/owlkettle/adw.nim"),
StackTraceEntry(procname: "runMainloop", line: 96, filename: "/home/philipp/dev/owlkettle/owlkettle/mainloop.nim"),
StackTraceEntry(procname: "changedCallback", line: 748, filename: "/home/philipp/dev/owlkettle/owlkettle/adw.nim"),
StackTraceEntry(procname: "sysFatal", line: 53, filename: "/home/philipp/.choosenim/toolchains/nim-2.0.0/lib/system/fatal.nim")
],
up: nil)
I just can't explain why 0 somehow is a value that leads to ObjectConversionDefects. Nor why catching those exception still enables Segfaults elsewhere.
Below the current still completely busted approach:
when AdwVersion >= (1, 4):
renderable SpinRow of ActionRow:
climbRate: float = 0.0
digits: uint = 0
numeric: bool = false
min: float = 0.0
max: float = 100.0
snapToTicks: bool = false
updatePolicy: SpinButtonUpdatePolicy = SpinButtonUpdateAlways
value: float = 0
wrap: bool = false
proc changed(newValue: float)
hooks:
beforeBuild:
let climbRate: float = if widget.hasClimbRate: widget.valClimbRate else: 0.0
let digits: uint = if widget.hasDigits: widget.valDigits else: 0.uint
state.internalWidget = adw_spin_row_new(GtkAdjustment(nil), climbRate.cdouble, digits.cuint)
connectEvents:
proc changedCallback(widget: GtkWidget, data: ptr EventObj[proc (newValue: float)]) {.cdecl.} =
echo "Changed Signal"
var newValue: float
try:
let valueString = $gtk_editable_get_text(widget)
echo "The value: ", valueString
newValue = parseFloat(valueString)
except Exception as e:
echo "Borked: ", e.repr
newValue = 0.0
echo data.isNil
echo data[].widget.isNil
SpinRowState(data[].widget).value = newValue
data[].callback(newValue)
data[].redraw()
state.connect(state.changed, "changed", changedCallback)
# state.connect(state.output, "output", eventCallback)
# state.connect(state.wrapped, "wrapped", eventCallback)
hooks climbRate:
property:
adw_spin_row_set_climb_rate(state.internalWidget, state.climbRate.cdouble)
hooks digits:
property:
adw_spin_row_set_digits(state.internalWidget, state.digits.cuint)
hooks numeric:
property:
adw_spin_row_set_numeric(state.internalWidget, state.numeric.cbool)
hooks min:
property:
adw_spin_row_set_range(state.internalWidget, state.min.cdouble, state.max.cdouble)
hooks max:
property:
adw_spin_row_set_range(state.internalWidget, state.min.cdouble, state.max.cdouble)
hooks snapToTicks:
property:
adw_spin_row_set_snap_to_ticks(state.internalWidget, state.snapToTicks.cbool)
hooks updatePolicy:
property:
adw_spin_row_set_update_policy(state.internalWidget, state.updatePolicy)
hooks value:
property:
echo "Value was updated to: ", state.value
adw_spin_row_set_value(state.internalWidget, state.value.cdouble)
hooks wrap:
property:
adw_spin_row_set_wrap(state.internalWidget, state.wrap.cbool)
export SpinRow
https://gnome.pages.gitlab.gnome.org/libadwaita/doc/main/class.SpinRow.html