From 1bcd74fc4e8d066f7d69fbefb06469f32f110f72 Mon Sep 17 00:00:00 2001 From: Martin Corino Date: Tue, 14 Nov 2023 16:47:32 +0100 Subject: [PATCH 01/21] support Wx::Sizer#horz_border --- rakelib/lib/director/sizer.rb | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/rakelib/lib/director/sizer.rb b/rakelib/lib/director/sizer.rb index fe1f7b09..c5c4583d 100644 --- a/rakelib/lib/director/sizer.rb +++ b/rakelib/lib/director/sizer.rb @@ -18,6 +18,11 @@ def setup when 'wxSizer' spec.items << 'wxSizerFlags' spec.gc_as_untracked('wxSizerFlags') + if Config.instance.wx_version < '3.3.0' + # missing from docs + spec.extend_interface 'wxSizerFlags', + 'wxSizerFlags& HorzBorder()' + end spec.make_abstract('wxSizer') spec.ignore %w[wxSizer::IsShown wxSizer::SetVirtualSizeHints] # cannot use these with wxRuby From c3b337c03ae0fab21da72f9e14e964e7cb8c1866 Mon Sep 17 00:00:00 2001 From: Martin Corino Date: Sat, 18 Nov 2023 11:52:28 +0100 Subject: [PATCH 02/21] fix missing commas --- lib/wx/core/evthandler.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/wx/core/evthandler.rb b/lib/wx/core/evthandler.rb index 726d62ac..1417aa94 100644 --- a/lib/wx/core/evthandler.rb +++ b/lib/wx/core/evthandler.rb @@ -182,7 +182,7 @@ def acquire_handler(meth, block) # check arity <= 1 if h_meth.arity>1 Kernel.raise ArgumentError, - "Event handler should not accept more than at most a single argument" + "Event handler should not accept more than at most a single argument", caller end # wrap method without any argument in anonymous proc to prevent strict argument checking @@ -193,7 +193,7 @@ def acquire_handler(meth, block) end else Kernel.raise ArgumentError, - "Specify event handler with a method, name, proc OR block" + "Specify event handler with a method, name, proc OR block", caller end end From 2a5a02c3fa229d8a64a334a285053516d2c66bd1 Mon Sep 17 00:00:00 2001 From: Martin Corino Date: Sat, 18 Nov 2023 11:53:34 +0100 Subject: [PATCH 03/21] make Swig::DirectorRubyException globally available --- ext/wxruby3/swig/custom/director.swg | 8 ++++++++ rakelib/lib/core/include/funcall.inc | 2 +- 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/ext/wxruby3/swig/custom/director.swg b/ext/wxruby3/swig/custom/director.swg index 8355a452..250408f8 100644 --- a/ext/wxruby3/swig/custom/director.swg +++ b/ext/wxruby3/swig/custom/director.swg @@ -195,6 +195,14 @@ namespace Swig { } }; + /* wxRuby customization */ + class WXRUBY_EXPORT DirectorRubyException : public DirectorException + { + public: + DirectorRubyException(VALUE error, VALUE rcvr, ID fn_id); + + }; + /* Simple thread abstraction for pthreads on win32 */ #ifdef __THREAD__ # define __PTHREAD__ diff --git a/rakelib/lib/core/include/funcall.inc b/rakelib/lib/core/include/funcall.inc index 5527be0f..3581e35e 100644 --- a/rakelib/lib/core/include/funcall.inc +++ b/rakelib/lib/core/include/funcall.inc @@ -16,7 +16,7 @@ VALUE rb_get_backtrace(VALUE); namespace Swig { - class DirectorRubyException : public DirectorException + class WXRB_EXPORT_FLAG DirectorRubyException : public DirectorException { public: DirectorRubyException(VALUE error, VALUE rcvr, ID fn_id) From c74c2a10c881520b95574b7be919383510224b1a Mon Sep 17 00:00:00 2001 From: Martin Corino Date: Sat, 18 Nov 2023 11:54:06 +0100 Subject: [PATCH 04/21] fix missing suppression decls with inline interface code --- rakelib/lib/core/spec_helper.rb | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/rakelib/lib/core/spec_helper.rb b/rakelib/lib/core/spec_helper.rb index 42abb72c..98770d6a 100644 --- a/rakelib/lib/core/spec_helper.rb +++ b/rakelib/lib/core/spec_helper.rb @@ -382,7 +382,13 @@ def init_code def interface_code if ifspec.interface_code && !ifspec.interface_code.empty? - ifspec.interface_code.join("\n") + if warn_filters.empty? + '' + else + "\n" + warn_filters.collect do |warn, decls| + decls.collect { |decl| "%warnfilter(#{warn}) #{decl};" } + end.flatten.join("\n") + end + ifspec.interface_code.join("\n") else %Q{%include "#{interface_include}"\n} end From 1502aa802d0943688ab794cfbb0828789ff9a632 Mon Sep 17 00:00:00 2001 From: Martin Corino Date: Sat, 18 Nov 2023 11:55:27 +0100 Subject: [PATCH 05/21] improve support for custom implementation class - copy ctors - upcall type casts --- rakelib/lib/swig_runner.rb | 64 ++++++++++++++++++++++++++++++++------ 1 file changed, 55 insertions(+), 9 deletions(-) diff --git a/rakelib/lib/swig_runner.rb b/rakelib/lib/swig_runner.rb index 88731a7f..1426c772 100644 --- a/rakelib/lib/swig_runner.rb +++ b/rakelib/lib/swig_runner.rb @@ -404,29 +404,63 @@ def run dir_cls_re_txt = class_list.select { |clsdef| has_proxy?(clsdef) }.collect { |cd| cd.name }.join('|') # create regexp for Director constructors (may not exist if no proxies are enabled) dir_ctor_re = /SwigDirector_\w+::SwigDirector_\w+\(.*\)\s*:\s*(#{dir_cls_re_txt})\(.*\)\s*,\s*Swig::Director.*{/ + # create regexp for method wrappers other than 'initialize' wrappers + wrap_mtd_re = /_wrap_(#{cls_re_txt})_(\w+)\(.*\)/ end found_new = false cpp_class = nil cpp_new_re = nil + found_wrap_mtd = false + wrap_mtd_name = nil + wrap_mtd_upcall_re = nil update_source do |line| if found_new # inside 'initialize' wrapper? if cpp_new_re =~ line # at C++ allocation of class instance? - # replace with the registered implementation class - line.sub!(/new\s+#{cpp_class}\(/, "new #{class_implementation(cpp_class)}(") - found_new = false # only 1 line will match per wrapper function so stop matching + if $1 # director allocation for derived class + # in case of copy ctor replace type of argument + # for director copy ctor + line.sub!(/\((\w+),\s*\(#{cpp_class}\s+const\s+\&\)/, "(\\1,(#{class_implementation(cpp_class)} const &)") + else # allocation for actual class + # replace with the registered implementation class + line.sub!(/new\s+#{cpp_class}\(/, "new #{class_implementation(cpp_class)}(") + # in case of copy ctor also replace type of argument + # for class copy ctor + line.sub!(/\(\(#{cpp_class}\s+const\s+\&\)/, "((#{class_implementation(cpp_class)} const &)") + end elsif /\A}/ =~ line # end of wrapper function? # stop matching (in case of overloads there will be one matching wrapper function # that does no actual allocation but just acts as a front for the overload wrappers) found_new = false end + elsif found_wrap_mtd + if wrap_mtd_upcall_re =~ line # at upcall for possibly proxied wrapper? + line.gsub!(cpp_class, class_implementation(cpp_class)) + # if the upcall does not yet cast the receiver instance to the correct implementation class + if /\((\w+)\)->/ =~ line + # add required cast + line.sub!(/\((\w+)\)->/, "((#{class_implementation(cpp_class)} *)\\1)->") + end + found_wrap_mtd = false # upcall found + elsif /\A}/ =~ line + found_wrap_mtd = false # end of wrapper method + end elsif new_re =~ line # are we at an 'initialize' wrapper? found_new = true cpp_class = $1 - cpp_new_re = /new\s+#{cpp_class}\(.*\)/ # regexp for C++ new expression for this specific class - elsif proxies_enabled && dir_ctor_re =~ line # at director ctor? - # replace base class name by implementation name - cpp_class = $1 - line.sub!(/:\s*#{cpp_class}\(/, ": #{class_implementation(cpp_class)}(") + cpp_new_re = /new\s+(SwigDirector_)?#{cpp_class}\(.*\)/ # regexp for C++ new expression for this specific class + elsif proxies_enabled + if dir_ctor_re =~ line # at director ctor? + # replace base class name by implementation name + cpp_class = $1 + line.sub!(/:\s*#{cpp_class}\(/, ": #{class_implementation(cpp_class)}(") + # in case of copy ctor also replace type of argument + line.sub!(/\(VALUE\s+self,\s*#{cpp_class}\s+const\s+\&(\w+)\)/, "(VALUE self, #{class_implementation(cpp_class)} const &\\1)") + elsif wrap_mtd_re =~ line # at wrapper method other than 'initialize' wrapper + cpp_class = $1 + wrap_mtd_name = $2 + wrap_mtd_upcall_re = /-\>#{cpp_class}::#{wrap_mtd_name}\(/ + found_wrap_mtd = true + end end line end @@ -435,11 +469,23 @@ def run # if so, we also need to update the header code (Director class declaration) # create regexp for 'initialize' wrappers (due to overloads this could be more than one per class) dir_re = /class\s+SwigDirector_\w+\s*:\s*public\s+(#{dir_cls_re_txt})\s*,\s*public\s+Swig::Director\s*{/ + found_dir = false + copy_ctor_re = nil update_header do |line| - if dir_re =~ line # at Director class declaration? + if found_dir + if copy_ctor_re =~ line + # replace copy ctor arg type + arg = $1 + line.sub!(/#{cpp_class}\s+const\s+&#{arg}/, "#{class_implementation(cpp_class)} const &#{arg}") + elsif /\A};/ =~ line + found_dir = false + end + elsif dir_re =~ line # at Director class declaration? # replace base class name by implementation name cpp_class = $1 line.sub!(/public\s+#{cpp_class}/, "public #{class_implementation(cpp_class)}") + copy_ctor_re = /SwigDirector_#{cpp_class}\(VALUE\s+self,\s*#{cpp_class}\s+const\s+&(\w+)\);/ + found_dir = true end line end From 47c388edf24b30b4e0a2225e49285aa04d67a0be Mon Sep 17 00:00:00 2001 From: Martin Corino Date: Sat, 18 Nov 2023 11:56:23 +0100 Subject: [PATCH 06/21] expose data transfer methods --- rakelib/lib/director/window.rb | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/rakelib/lib/director/window.rb b/rakelib/lib/director/window.rb index cf660260..e7464c8f 100644 --- a/rakelib/lib/director/window.rb +++ b/rakelib/lib/director/window.rb @@ -66,8 +66,6 @@ def setup __CODE end spec.ignore [ - 'wxWindow::TransferDataFromWindow', - 'wxWindow::TransferDataToWindow', 'wxWindow::PopEventHandler', 'wxWindow::SetConstraints', 'wxWindow::GetHandle', @@ -295,7 +293,9 @@ def process(gendoc: false) "#{spec.class_name(citem)}::EnableTouchEvents", "#{spec.class_name(citem)}::WarpPointer", "#{spec.class_name(citem)}::AdjustForLayoutDirection", - "#{spec.class_name(citem)}::IsTransparentBackgroundSupported") + "#{spec.class_name(citem)}::IsTransparentBackgroundSupported", + "#{spec.class_name(citem)}::TransferDataFromWindow", + "#{spec.class_name(citem)}::TransferDataToWindow") end end if spec.module_name == 'wxWindow' From 3b6c3507bf9d61fe95349afe61a052b5d5882ea6 Mon Sep 17 00:00:00 2001 From: Martin Corino Date: Sat, 18 Nov 2023 11:57:31 +0100 Subject: [PATCH 07/21] improve Wx::Validator - add data binding support - improve custom derivative support --- ext/wxruby3/include/wxruby-Validator.h | 37 ++++ ext/wxruby3/include/wxruby-ValidatorBinding.h | 53 ++++++ lib/wx/core/validator.rb | 70 ++++++- lib/wx/doc/validator.rb | 93 ++++++++++ rakelib/lib/director/validator.rb | 173 +++++++++++++++--- 5 files changed, 400 insertions(+), 26 deletions(-) create mode 100644 ext/wxruby3/include/wxruby-Validator.h create mode 100644 ext/wxruby3/include/wxruby-ValidatorBinding.h create mode 100644 lib/wx/doc/validator.rb diff --git a/ext/wxruby3/include/wxruby-Validator.h b/ext/wxruby3/include/wxruby-Validator.h new file mode 100644 index 00000000..4133f4d3 --- /dev/null +++ b/ext/wxruby3/include/wxruby-Validator.h @@ -0,0 +1,37 @@ +// Copyright (c) 2023 M.J.N. Corino, The Netherlands +// +// This software is released under the MIT license. + +/* + * WxRuby3 wxRubyValidator class + */ + +#ifndef _WXRUBY_VALIDATOR_H +#define _WXRUBY_VALIDATOR_H + +#include "wxruby-ValidatorBinding.h" + +class WXRUBY_EXPORT wxRubyValidator : public wxValidator, public wxRubyValidatorBinding +{ +public: + wxRubyValidator (); + wxRubyValidator (const wxRubyValidator&); + virtual ~wxRubyValidator (); + + virtual wxObject* Clone() const override; + + virtual void SetWindow(wxWindow *win) override; + + virtual bool TransferFromWindow () override; + virtual bool TransferToWindow () override; + +protected: + static WxRuby_ID do_transfer_from_window_id; + static WxRuby_ID do_transfer_to_window_id; + static WxRuby_ID clone_id; + + virtual VALUE DoTransferFromWindow(); + virtual bool DoTransferToWindow(VALUE data); +}; + +#endif /* _WXRUBY_VALIDATOR_HASH_H */ diff --git a/ext/wxruby3/include/wxruby-ValidatorBinding.h b/ext/wxruby3/include/wxruby-ValidatorBinding.h new file mode 100644 index 00000000..5c7fefb6 --- /dev/null +++ b/ext/wxruby3/include/wxruby-ValidatorBinding.h @@ -0,0 +1,53 @@ +// Copyright (c) 2023 M.J.N. Corino, The Netherlands +// +// This software is released under the MIT license. + +/* + * WxRuby3 wxRubyValidatorBinding class + */ + +#ifndef _WXRUBY_VALIDATOR_BINDING_H +#define _WXRUBY_VALIDATOR_BINDING_H + +class WXRUBY_EXPORT wxRubyValidatorBinding +{ +public: + wxRubyValidatorBinding () + : on_transfer_from_win_proc_(Qnil) + , on_transfer_to_win_proc_(Qnil) + {} + + void SetOnTransferFromWindow(VALUE proc) + { + this->on_transfer_from_win_proc_ = proc; + } + void SetOnTransferToWindow(VALUE proc) + { + this->on_transfer_to_win_proc_ = proc; + } + + bool DoOnTransferFromWindow(VALUE data); + VALUE DoOnTransferToWindow(); + + void GC_Mark() + { + rb_gc_mark(this->on_transfer_from_win_proc_); + rb_gc_mark(this->on_transfer_to_win_proc_); + } + +protected: + static WxRuby_ID call_id; + + wxRubyValidatorBinding (const wxRubyValidatorBinding& vb) + : on_transfer_from_win_proc_(vb.on_transfer_from_win_proc_) + , on_transfer_to_win_proc_(vb.on_transfer_to_win_proc_) + {} + + void CopyBindings(const wxRubyValidatorBinding* val_bind); + +private: + VALUE on_transfer_from_win_proc_; + VALUE on_transfer_to_win_proc_; +}; + +#endif /* #define _WXRUBY_VALIDATOR_BINDING_H */ diff --git a/lib/wx/core/validator.rb b/lib/wx/core/validator.rb index c835670a..ceaf4951 100644 --- a/lib/wx/core/validator.rb +++ b/lib/wx/core/validator.rb @@ -1,15 +1,75 @@ # Copyright (c) 2023 M.J.N. Corino, The Netherlands # # This software is released under the MIT license. -# -# Some parts are -# Copyright 2004-2007, wxRuby development team -# released under the MIT-like wxRuby2 license class Wx::Validator + # Default implementation of clone, may need to be over-ridden if # custom subclasses should state variables that need to be copied + # NOTE: There is no way to copy the bindings here so do NOT def clone - self.class.new + begin + self.class.new(self) + rescue + p $! + raise + end + end + + # overload for customized functionality + def do_transfer_from_window + nil # by default just return nil + end + + # overload for customized functionality + def do_transfer_to_window(_data) + true # by default just return true end + + module Binding + def self.included(base) + wx_on_transfer_from_window = base.instance_method :on_transfer_from_window + base.define_method :on_transfer_from_window do |meth = nil, &block| + proc = if block and not meth + block + elsif meth and not block + h_meth = case meth + when Symbol, String then self.method(meth) + when Proc then meth + when Method then meth + end + # check arity == 1 + if h_meth.arity != 1 + Kernel.raise ArgumentError, + "on_transfer_from_window handler should accept a single argument" + end + h_meth + end + wx_on_transfer_from_window.bind(self).call(proc) + end + + wx_on_transfer_to_window = base.instance_method :on_transfer_to_window + base.define_method :on_transfer_to_window do |meth = nil, &block| + proc = if block and not meth + block + elsif meth and not block + h_meth = case meth + when Symbol, String then self.method(meth) + when Proc then meth + when Method then meth + end + # check arity == 0 + if h_meth.arity != 0 + Kernel.raise ArgumentError, + "on_transfer_to_window handler should not accept any argument" + end + h_meth + end + wx_on_transfer_to_window.bind(self).call(proc) + end + end + end + + include Binding + end diff --git a/lib/wx/doc/validator.rb b/lib/wx/doc/validator.rb new file mode 100644 index 00000000..7439a9a5 --- /dev/null +++ b/lib/wx/doc/validator.rb @@ -0,0 +1,93 @@ +# :stopdoc: +# Copyright (c) 2023 M.J.N. Corino, The Netherlands +# +# This software is released under the MIT license. +# :startdoc: + + +module Wx + + class Validator < EvtHandler + + # @overload initialize() + # Constructor. + # @return [Wx::Validator] + # @overload initialize(other) + # Copy constructor. + # @param [Wx::Validator] other + # @return [Wx::Validator] + def initialize(*arg) end + + # Method called when transferring data from window. + # Should retrieve and return the data from the associated window (#get_window). + # By default returns nil. + # Overload for customized functionality. + # @return [Object] retrieved data from window + def do_transfer_from_window; end + + # Method called when transferring data to window. + # Should transfer the given data to the associated window and return true if successful. + # By default does nothing and just returns true. + # Overload for customized functionality. + def do_transfer_to_window(data) end + + # Mixin module providing data binding options for validators. + module Binding + + # Installs a callback handler to capture the data retrieved from the associated window. + # The callback handler can be specified as a (name of a) method, a Proc or a block. + # + # @example This can be used to implement data binding like: + # class MyValidator < Wx::Validator + # def initialize(data_store) + # @data_store = data_store + # self.on_transfer_from_window :store_data + # self.on_transfer_to_window :load_data + # end + # protected + # def store_data(data) + # @data_store.save_the_data(data) + # end + # def load_data + # @data_store.get_the_data + # end + # end + # val = MyValidator.new(a_data_store) + # win.set_validator(val) + # + # @param [String,Symbol,Method,Proc] meth (name of) method or event handling proc; to be supplied when no block is given + # @yieldparam [Object] data the data retrieved from the window + def on_transfer_from_window(meth=nil, &block) end + + # Installs a callback handler to provide the data to transfer to the associated window. + # The callback handler can be specified as a (name of a) method, a Proc or a block. + # + # @example This can be used to implement data binding like: + # class MyValidator < Wx::Validator + # def initialize(data_store) + # @data_store = data_store + # self.on_transfer_from_window :store_data + # self.on_transfer_to_window :load_data + # end + # protected + # def store_data(data) + # @data_store.save_the_data(data) + # end + # def load_data + # @data_store.get_the_data + # end + # end + # val = MyValidator.new(a_data_store) + # win.set_validator(val) + # + # @param [String,Symbol,Method,Proc] meth (name of) method or event handling proc; to be supplied when no block is given + # @yieldreturn [Object] the data to transfer to the window + def on_transfer_to_window(meth=nil, &block) end + + end + + include Binding + + end + +end diff --git a/rakelib/lib/director/validator.rb b/rakelib/lib/director/validator.rb index d02a46a6..43d383a7 100644 --- a/rakelib/lib/director/validator.rb +++ b/rakelib/lib/director/validator.rb @@ -16,29 +16,160 @@ class Validator < EvtHandler def setup super - # make Ruby director and wrappers use custom implementation - spec.use_class_implementation('wxValidator', 'wxRubyValidator') - # provide custom wxRuby derivative of validator - spec.add_header_code <<~__HEREDOC - class wxRubyValidator : public wxValidator - { - public: - wxRubyValidator () : wxValidator () {} - virtual ~wxRubyValidator () + if spec.module_name == 'wxValidator' + # make Ruby director and wrappers use custom implementation + spec.use_class_implementation('wxValidator', 'wxRubyValidator') + # provide custom wxRuby derivative of validator + spec.add_header_code <<~__HEREDOC + #include "wxruby-Validator.h" + + WxRuby_ID wxRubyValidator::do_transfer_from_window_id("do_transfer_from_window"); + WxRuby_ID wxRubyValidator::do_transfer_to_window_id("do_transfer_to_window"); + WxRuby_ID wxRubyValidator::clone_id("clone"); + + wxRubyValidator::wxRubyValidator () + : wxValidator () + , wxRubyValidatorBinding () + {} + wxRubyValidator::wxRubyValidator (const wxRubyValidator& v) + : wxValidator (v) + , wxRubyValidatorBinding (v) + {} + wxRubyValidator::~wxRubyValidator () { wxRuby_ReleaseEvtHandlerProcs(this); - } - - // these two methods are noops in wxRuby (since we do not support C++ data transfer there) - // but we want them to always return true to prevent wxWidgets from complaining - bool TransferFromWindow () override { return true; } - bool TransferToWindow () override { return true; } - }; - __HEREDOC - # will be provided as a pure Ruby method - spec.ignore 'wxValidator::Clone' - # not provided in Ruby - spec.ignore %w[wxValidator::TransferFromWindow wxValidator::TransferToWindow] + } + + wxObject* wxRubyValidator::Clone() const + { + bool ex_caught = false; + VALUE self = SWIG_RubyInstanceFor(const_cast (this)); + VALUE rc = wxRuby_Funcall(ex_caught, self, clone_id(), 0); + if (ex_caught) + { + throw Swig::DirectorRubyException(rc, self, clone_id()); + } + void *ptr; + int res = SWIG_ConvertPtr(rc, &ptr, SWIGTYPE_p_wxValidator, 0); + if (!SWIG_IsOK(res)) + { + Swig::DirectorTypeMismatchException::raise(self, "clone", SWIG_ErrorType(SWIG_ArgError(res)), "in output value of type '""Wx::Validator *""'"); + } + return reinterpret_cast< wxValidator * >(ptr); + } + + void wxRubyValidator::SetWindow(wxWindow *win) + { + this->wxValidator::SetWindow(win); + VALUE self = SWIG_RubyInstanceFor(this); + // make sure Ruby does not own this validator instance anymore + RDATA(self)->dfree = SWIG_RubyRemoveTracking; + } + + bool wxRubyValidator::TransferFromWindow() + { + return this->DoOnTransferFromWindow(this->DoTransferFromWindow()); + } + bool wxRubyValidator::TransferToWindow() + { + return this->DoTransferToWindow(this->DoOnTransferToWindow()); + } + + VALUE wxRubyValidator::DoTransferFromWindow() + { + bool ex_caught = false; + VALUE self = SWIG_RubyInstanceFor(this); + VALUE rc = wxRuby_Funcall(ex_caught, self, do_transfer_from_window_id(), 0); + if (ex_caught) + { + throw Swig::DirectorRubyException(rc, self, do_transfer_from_window_id()); + } + return rc; + } + bool wxRubyValidator::DoTransferToWindow(VALUE data) + { + bool ex_caught = false; + VALUE self = SWIG_RubyInstanceFor(this); + VALUE rc = wxRuby_Funcall(ex_caught, self, do_transfer_to_window_id(), 1, data); + if (ex_caught) + { + throw Swig::DirectorRubyException(rc, self, do_transfer_to_window_id()); + } + return (rc == Qtrue); + } + + WxRuby_ID wxRubyValidatorBinding::call_id("call"); + + bool wxRubyValidatorBinding::DoOnTransferFromWindow(VALUE data) + { + if (!NIL_P(this->on_transfer_from_win_proc_)) + { + bool ex_caught = false; + VALUE rc = wxRuby_Funcall(ex_caught, this->on_transfer_from_win_proc_, call_id(), 1, data); + if (ex_caught) + { + throw Swig::DirectorRubyException(rc, this->on_transfer_from_win_proc_, call_id()); + } + } + return true; + } + VALUE wxRubyValidatorBinding::DoOnTransferToWindow() + { + if (!NIL_P(this->on_transfer_to_win_proc_)) + { + bool ex_caught = false; + VALUE rc = wxRuby_Funcall(ex_caught, this->on_transfer_to_win_proc_, call_id(), 0); + if (ex_caught) + { + throw Swig::DirectorRubyException(rc, this->on_transfer_to_win_proc_, call_id()); + } + return rc; + } + return Qnil; + } + + void wxRubyValidatorBinding::CopyBindings(const wxRubyValidatorBinding* val_bind) + { + if (val_bind) + { + this->on_transfer_from_win_proc_ = val_bind->on_transfer_from_win_proc_; + this->on_transfer_to_win_proc_ = val_bind->on_transfer_to_win_proc_; + } + } + + static void GC_mark_wxValidator(void* ptr) + { + if (ptr) + reinterpret_cast (ptr)->GC_Mark(); + } + __HEREDOC + spec.add_swig_code '%markfunc wxValidator "GC_mark_wxValidator";' + # will be provided as a pure Ruby method + spec.ignore 'wxValidator::Clone', ignore_doc: false + # add wxRuby specifics + spec.extend_interface 'wxValidator', + 'wxValidator(const wxValidator& other)' + spec.add_extend_code 'wxValidator', <<~__HEREDOC + void OnTransferFromWindow(VALUE proc) + { + dynamic_cast($self)->SetOnTransferFromWindow(proc); + } + void OnTransferToWindow(VALUE proc) + { + dynamic_cast($self)->SetOnTransferToWindow(proc); + } + __HEREDOC + # not provided in Ruby + spec.ignore %w[wxValidator::TransferFromWindow wxValidator::TransferToWindow wxValidator::SetWindow] + else + spec.add_header_code <<~__HEREDOC + #include "wxruby-ValidatorBinding.h" + __HEREDOC + end + # overrule common typemap for parent arg of Validate + spec.map 'wxWindow* parent' do + map_check code: '' + end end end # class Validator From 0132e18d648017e3008d446ca45ef4693390ce5f Mon Sep 17 00:00:00 2001 From: Martin Corino Date: Sat, 18 Nov 2023 11:58:51 +0100 Subject: [PATCH 08/21] update for Validator improvements - adds data binding support - improved derivative support --- .../director/numeric_property_validator.rb | 103 +++++++++++++++++- rakelib/lib/director/text_validator.rb | 103 +++++++++++++++--- 2 files changed, 187 insertions(+), 19 deletions(-) diff --git a/rakelib/lib/director/numeric_property_validator.rb b/rakelib/lib/director/numeric_property_validator.rb index a330350d..fd7ba9c9 100644 --- a/rakelib/lib/director/numeric_property_validator.rb +++ b/rakelib/lib/director/numeric_property_validator.rb @@ -6,31 +6,122 @@ # wxRuby3 wxWidgets interface director ### -require_relative './event_handler' +require_relative './validator' module WXRuby3 class Director - class NumericPropertyValidator < EvtHandler + class NumericPropertyValidator < Validator def setup super # need a custom implementation to handle event handler proc cleanup spec.add_header_code <<~__HEREDOC - class WXRubyNumericPropertyValidator : public wxNumericPropertyValidator + class WXRubyNumericPropertyValidator : public wxNumericPropertyValidator, public wxRubyValidatorBinding { public: WXRubyNumericPropertyValidator(NumericType numericType, int base=10) - : wxNumericPropertyValidator(numericType, base) {} + : wxNumericPropertyValidator(numericType, base) + { + m_stringValue = &m_valueCache; + } + WXRubyNumericPropertyValidator(const WXRubyNumericPropertyValidator& other) + : wxNumericPropertyValidator(other) + , wxRubyValidatorBinding(other) + { + m_stringValue = &m_valueCache; + } virtual ~WXRubyNumericPropertyValidator() { wxRuby_ReleaseEvtHandlerProcs(this); } + + virtual wxObject* Clone() const override + { + WXRubyNumericPropertyValidator *clone = new WXRubyNumericPropertyValidator(*this); + clone->m_valueCache = this->m_valueCache; + return clone; + } + + virtual void SetWindow(wxWindow *win) override + { + this->wxNumericPropertyValidator::SetWindow(win); + VALUE self = get_self(); + // make sure Ruby does not own this validator instance anymore + RDATA(self)->dfree = SWIG_RubyRemoveTracking; + } + + virtual bool TransferFromWindow () override + { + // call super + if (this->wxNumericPropertyValidator::TransferFromWindow()) + { + // ok, data is retrieved from window and cached + // now allow any defined binding handler to pass on the data + return this->DoOnTransferFromWindow(WXSTR_TO_RSTR(m_valueCache)); + } + return false; + } + virtual bool TransferToWindow () override + { + // collect data from any defined binding handler + VALUE data = this->DoOnTransferToWindow(); + if (NIL_P(data)) + { + m_valueCache.clear(); + } + else + { + m_valueCache = RSTR_TO_WXSTR(data); + } + // now allow standard functionality to transfer to window + return this->wxNumericPropertyValidator::TransferToWindow(); + } + + private: + static VALUE c_NumericPropertyValidator; + + VALUE get_self() + { + VALUE self = SWIG_RubyInstanceFor(this); + // if this is a C++ created clone (wxWidgets clones validators that are set) it's not tracked yet + if (NIL_P(self)) + { + if (NIL_P(c_NumericPropertyValidator)) + { + c_NumericPropertyValidator = rb_eval_string("Wx::PG::NumericPropertyValidator"); + } + swig_type_info* swig_type = wxRuby_GetSwigTypeForClass(c_NumericPropertyValidator); + self = SWIG_NewPointerObj(this, swig_type, 0); // wrap but don't make Ruby own it + } + return self; + } + + wxString m_valueCache; }; - __HEREDOC + + VALUE WXRubyNumericPropertyValidator::c_NumericPropertyValidator = Qnil; + + WXRUBY_EXPORT void GC_mark_wxNumericPropertyValidator(void* ptr) + { + if (ptr) + reinterpret_cast (ptr)->GC_Mark(); + } + __HEREDOC + spec.add_swig_code '%markfunc wxValidator "GC_mark_wxValidator";' + # make Ruby director and wrappers use custom implementation spec.use_class_implementation 'wxNumericPropertyValidator', 'WXRubyNumericPropertyValidator' - spec.no_proxy 'wxNumericPropertyValidator::Clone' + spec.new_object 'wxNumericPropertyValidator::Clone' + # handle clone mapping + spec.map 'wxObject *' => 'Wx::PG::NumericPropertyValidator' do + map_out code: <<~__CODE + $result = SWIG_NewPointerObj(SWIG_as_voidptr($1), SWIGTYPE_p_wxTextValidator, SWIG_POINTER_OWN); + __CODE + end + spec.suppress_warning(473, 'wxNumericPropertyValidator::Clone') + spec.extend_interface 'wxNumericPropertyValidator', + 'wxNumericPropertyValidator(const wxNumericPropertyValidator &other)' spec.do_not_generate :variables, :defines, :enums, :functions end diff --git a/rakelib/lib/director/text_validator.rb b/rakelib/lib/director/text_validator.rb index ebcbee4a..9c997d8b 100644 --- a/rakelib/lib/director/text_validator.rb +++ b/rakelib/lib/director/text_validator.rb @@ -6,48 +6,125 @@ # wxRuby3 wxWidgets interface director ### -require_relative './event_handler' +require_relative './validator' module WXRuby3 class Director - class TextValidator < EvtHandler + class TextValidator < Validator def setup super # need a custom implementation to handle event handler proc cleanup spec.add_header_code <<~__HEREDOC - class WXRubyTextValidator : public wxTextValidator + class WXRubyTextValidator : public wxTextValidator, public wxRubyValidatorBinding { public: - WXRubyTextValidator(const wxTextValidator& v) - : wxTextValidator(v) {} WXRubyTextValidator(long style=wxFILTER_NONE) - : wxTextValidator(style) {} + : wxTextValidator(style, &m_valueCache) + { + } + WXRubyTextValidator(const WXRubyTextValidator& other) + : wxTextValidator(other.GetStyle(), &m_valueCache) + , wxRubyValidatorBinding(other) + { + } virtual ~WXRubyTextValidator() { wxRuby_ReleaseEvtHandlerProcs(this); - } + } + + virtual wxObject* Clone() const override + { + WXRubyTextValidator* clone = new WXRubyTextValidator(*this); + clone->m_valueCache = this->m_valueCache; + return clone; + } + + virtual void SetWindow(wxWindow *win) override + { + this->wxTextValidator::SetWindow(win); + VALUE self = get_self(); + // make sure Ruby does not own this validator instance anymore + RDATA(self)->dfree = SWIG_RubyRemoveTracking; + } + + virtual bool TransferFromWindow () override + { + // call super + if (this->wxTextValidator::TransferFromWindow()) + { + // ok, data is retrieved from window and cached + // now allow any defined binding handler to pass on the data + return this->DoOnTransferFromWindow(WXSTR_TO_RSTR(m_valueCache)); + } + return false; + } + virtual bool TransferToWindow () override + { + // collect data from any defined binding handler + VALUE data = this->DoOnTransferToWindow(); + if (NIL_P(data)) + { + m_valueCache.clear(); + } + else + { + m_valueCache = RSTR_TO_WXSTR(data); + } + // now allow standard functionality to transfer to window + return this->wxTextValidator::TransferToWindow(); + } + + private: + static VALUE c_TextValidator; + + VALUE get_self() + { + VALUE self = SWIG_RubyInstanceFor(this); + // if this is a C++ created clone (wxWidgets clones validators that are set) it's not tracked yet + if (NIL_P(self)) + { + if (NIL_P(c_TextValidator)) + { + c_TextValidator = rb_const_get(mWxCore, rb_intern("TextValidator")); + } + swig_type_info* swig_type = wxRuby_GetSwigTypeForClass(c_TextValidator); + self = SWIG_NewPointerObj(this, swig_type, 0); // wrap but don't make Ruby own it + } + return self; + } + + wxString m_valueCache; }; - __HEREDOC + + VALUE WXRubyTextValidator::c_TextValidator = Qnil; + + WXRUBY_EXPORT void GC_mark_wxTextValidator(void* ptr) + { + if (ptr) + reinterpret_cast (ptr)->GC_Mark(); + } + + __HEREDOC + spec.add_swig_code '%markfunc wxTextValidator "GC_mark_wxTextValidator";' + # make Ruby director and wrappers use custom implementation spec.use_class_implementation 'wxTextValidator', 'WXRubyTextValidator' - # ignore copy ctor doc - spec.regard 'wxTextValidator::wxTextValidator(const wxTextValidator&)', regard_doc: false # ignore this ctor spec.ignore 'wxTextValidator::wxTextValidator(long, wxString*)' # add alternative spec.extend_interface 'wxTextValidator', 'wxTextValidator(long style=wxFILTER_NONE)' - # ignore non-virtual standard handler (not really useful in Ruby) + # ignore non-virtual standard handler (not useful in Ruby) spec.ignore 'wxTextValidator::OnChar' - spec.no_proxy 'wxTextValidator::Clone' spec.new_object 'wxTextValidator::Clone' # handle clone mapping spec.map 'wxObject *' => 'Wx::TextValidator' do map_out code: <<~__CODE - $result = SWIG_NewPointerObj(SWIG_as_voidptr($1), SWIGTYPE_p_wxTextValidator, SWIG_POINTER_OWN | 0 ); + $result = SWIG_NewPointerObj(SWIG_as_voidptr($1), SWIGTYPE_p_wxTextValidator, SWIG_POINTER_OWN); __CODE end + spec.suppress_warning(473, 'wxTextValidator::Clone') # not provided in Ruby spec.ignore %w[wxTextValidator::TransferFromWindow wxTextValidator::TransferToWindow] end From 75e5d80626c9ba3afef618d83db256939763a5ac Mon Sep 17 00:00:00 2001 From: Martin Corino Date: Sat, 18 Nov 2023 12:02:11 +0100 Subject: [PATCH 09/21] update for Validator improvements - adds data binding support - improved derivative support --- lib/wx/doc/pg/numeric_property_validator.rb | 23 +++++++++++++++++++++ 1 file changed, 23 insertions(+) create mode 100644 lib/wx/doc/pg/numeric_property_validator.rb diff --git a/lib/wx/doc/pg/numeric_property_validator.rb b/lib/wx/doc/pg/numeric_property_validator.rb new file mode 100644 index 00000000..26beed5c --- /dev/null +++ b/lib/wx/doc/pg/numeric_property_validator.rb @@ -0,0 +1,23 @@ +# :stopdoc: +# This file is automatically generated by the WXRuby3 documentation +# generator. Do not alter this file. +# :startdoc: + + +module Wx::PG + + class NumericPropertyValidator < TextValidator + + # @overload initialize(validator) + # Copy constructor. + # @param [Wx::wxNumericPropertyValidator] validator validator to copy + # @overload initialize(numericType, base=10) + # @param numericType [Wx::wxNumericPropertyValidator::NumericType] + # @param base [Integer] + # @return [Wx::PG::NumericPropertyValidator] + def initialize(*args) end + + end # NumericPropertyValidator + + +end From eca6614bc09e0d1e2473edc943944482390ca033 Mon Sep 17 00:00:00 2001 From: Martin Corino Date: Sat, 18 Nov 2023 12:02:32 +0100 Subject: [PATCH 10/21] add numeric validators (incl. docs) --- lib/wx/doc/num_validator.rb | 357 ++++++++++++++++++++++ rakelib/lib/director/num_validator.rb | 411 ++++++++++++++++++++++++++ rakelib/lib/specs/interfaces.rb | 1 + 3 files changed, 769 insertions(+) create mode 100644 lib/wx/doc/num_validator.rb create mode 100644 rakelib/lib/director/num_validator.rb diff --git a/lib/wx/doc/num_validator.rb b/lib/wx/doc/num_validator.rb new file mode 100644 index 00000000..f09ac07e --- /dev/null +++ b/lib/wx/doc/num_validator.rb @@ -0,0 +1,357 @@ +# :stopdoc: +# Copyright (c) 2023 M.J.N. Corino, The Netherlands +# +# This software is released under the MIT license. +# :startdoc: + + +module Wx + + # Validator for text entries used for integer entry. + # + # This validator can be used with Wx::TextCtrl or Wx::ComboBox (and potentially + # any other class implementing Wx::TextEntry interface) to check that only + # valid integer values can be entered into them. + # + # By default this validator accepts any signed integer values in the 64 bit range. + # This range can be restricted further by calling #set_min and #set_max or #set_range + # methods inherited from the base class. + # + # When the validator displays integers with thousands separators, the + # character used for the separators (usually "." or ",") depends on the locale + # set with Wx::Locale (note that you shouldn't change locale with `setlocale()` + # as this can result in a mismatch between the thousands separator used by + # Wx::Locale and the one used by the run-time library). + # + # @example A simple example of using this class: + # class MyDialog < Wx::Dialog + # + # def initialize + # super(...) + # ... + # # Allow integers and display them with thousands + # # separators. + # @value = 0 + # val = Wx::IntegerValidator.new(Wx::NUM_VAL_THOUSANDS_SEPARATOR) + # val.on_transfer_to_window { @value } + # val.on_transfer_from_window { |val| @value = val } + # + # # If the would like to accept only positive integers we could + # # call val.set_min(0) or alternatively use {Wx::UnsignedValidator}. + # + # # Associate it with the text control: + # Wx::TextCtrl.new(self, ..., val) + # end + # + # end + # + # @see Wx::Validator + # @see Wx::GenericValidator + # @see Wx::TextValidator + # @see Wx::UnsignedValidator + # @see Wx::FloatValidator + # @wxrb_require USE_VALIDATORS + class IntegerValidator < Validator + + # @overload initialize(style=Wx::NumValidatorStyle::NUM_VAL_DEFAULT) + # Constructor. + # @param [Wx::NumValidatorStyle] style A combination of Wx::NumValidatorStyle enum values with the exception of Wx::NUM_VAL_NO_TRAILING_ZEROES which can't be used here. + # @return [Wx::IntegerValidator] + # @overload initialize(min, max, style=Wx::NumValidatorStyle::NUM_VAL_DEFAULT) + # Constructor with specified range. + # @param [Integer] min The minimum value accepted by the validator. + # @param [Integer] max The maximum value accepted by the validator. + # @param [Wx::NumValidatorStyle] style A combination of Wx::NumValidatorStyle enum values with the exception of Wx::NUM_VAL_NO_TRAILING_ZEROES which can't be used here. + # @return [Wx::IntegerValidator] + # @overload initialize(other) + # Copy constructor. + # @param [Wx::IntegerValidator] other + # @return [Wx::IntegerValidator] + def initialize(*arg) end + + # Sets the minimal value accepted by the validator. + # + # This value is inclusive, i.e. the value equal to min is accepted. + # @param [Integer] min + def set_min(min) end + alias :min= :set_min + + # Gets the minimal value accepted by the validator. + # @return [Integer] + def get_min; end + alias :min :get_min + + # Sets the maximal value accepted by the validator. + # + # This value is inclusive, i.e. the value equal to max is accepted. + # @param [Integer] max + def set_max(max) end + alias :max= :set_max + + # Gets the maximum value accepted by the validator. + # @return [Integer] + def get_max; end + alias :max :get_max + + # Sets both minimal and maximal values accepted by the validator. + # + # Calling this is equivalent to calling both #set_min and #set_max. + # @param [Integer] min + # @param [Integer] max + def set_range(min, max) end + alias :range= :set_range + + # Gets both minimal and maximal values accepted by the validator. + # + # Returns an array with `[min, max]` + # @return [Array] + def get_range; end + alias :range :get_range + + # Change the validator style. + # + # Can be used to change the style of the validator after its creation. + # @param [Wx::NumValidatorStyle] style A combination of Wx::NumValidatorStyle enum values with the exception of Wx::NUM_VAL_NO_TRAILING_ZEROES which can't be used here. + def set_style(style) end + alias :style= :set_style + + end + + # Validator for text entries used for unsigned integer entry. + # + # This validator can be used with Wx::TextCtrl or Wx::ComboBox (and potentially + # any other class implementing Wx::TextEntry interface) to check that only + # valid integer values can be entered into them. + # + # By default this validator accepts any unsigned integer values in the 64 bit range. + # This range can be restricted further by calling #set_min and #set_max or #set_range + # methods inherited from the base class. + # + # When the validator displays integers with thousands separators, the + # character used for the separators (usually "." or ",") depends on the locale + # set with Wx::Locale (note that you shouldn't change locale with `setlocale()` + # as this can result in a mismatch between the thousands separator used by + # Wx::Locale and the one used by the run-time library). + # + # @example A simple example of using this class: + # class MyDialog < Wx::Dialog + # + # def initialize + # super(...) + # ... + # # Allow unsigned integers and display them with thousands + # # separators. + # @value = 0 + # val = Wx::UnsignedValidator.new(Wx::NUM_VAL_THOUSANDS_SEPARATOR) + # val.on_transfer_to_window { @value } + # val.on_transfer_from_window { |val| @value = val } + # + # # Associate it with the text control: + # Wx::TextCtrl.new(self, ..., val) + # end + # + # end + # + # @see Wx::Validator + # @see Wx::GenericValidator + # @see Wx::TextValidator + # @see Wx::IntegerValidator + # @see Wx::FloatValidator + # @wxrb_require USE_VALIDATORS + class UnsignedValidator < Validator + + # @overload initialize(style=Wx::NumValidatorStyle::NUM_VAL_DEFAULT) + # Constructor. + # @param [Wx::NumValidatorStyle] style A combination of Wx::NumValidatorStyle enum values with the exception of Wx::NUM_VAL_NO_TRAILING_ZEROES which can't be used here. + # @return [Wx::UnsignedValidator] + # @overload initialize(min, max, style=Wx::NumValidatorStyle::NUM_VAL_DEFAULT) + # Constructor with specified range. + # @param [Integer] min The minimum value accepted by the validator. + # @param [Integer] max The maximum value accepted by the validator. + # @param [Wx::NumValidatorStyle] style A combination of Wx::NumValidatorStyle enum values with the exception of Wx::NUM_VAL_NO_TRAILING_ZEROES which can't be used here. + # @return [Wx::UnsignedValidator] + # @overload initialize(other) + # Copy constructor. + # @param [Wx::UnsignedValidator] other + # @return [Wx::UnsignedValidator] + def initialize(*arg) end + + # Sets the minimal value accepted by the validator. + # + # This value is inclusive, i.e. the value equal to min is accepted. + # @param [Integer] min + def set_min(min) end + alias :min= :set_min + + # Gets the minimal value accepted by the validator. + # @return [Integer] + def get_min; end + alias :min :get_min + + # Sets the maximal value accepted by the validator. + # + # This value is inclusive, i.e. the value equal to max is accepted. + # @param [Integer] max + def set_max(max) end + alias :max= :set_max + + # Gets the maximum value accepted by the validator. + # @return [Integer] + def get_max; end + alias :max :get_max + + # Sets both minimal and maximal values accepted by the validator. + # + # Calling this is equivalent to calling both #set_min and #set_max. + # @param [Integer] min + # @param [Integer] max + def set_range(min, max) end + alias :range= :set_range + + # Gets both minimal and maximal values accepted by the validator. + # + # Returns an array with `[min, max]` + # @return [Array] + def get_range; end + alias :range :get_range + + # Change the validator style. + # + # Can be used to change the style of the validator after its creation. + # @param [Wx::NumValidatorStyle] style A combination of Wx::NumValidatorStyle enum values with the exception of Wx::NUM_VAL_NO_TRAILING_ZEROES which can't be used here. + def set_style(style) end + alias :style= :set_style + + end + + # Validator for text entries used for floating point numbers entry. + # + # This validator can be used with Wx::TextCtrl or Wx::ComboBox (and potentially + # any other class implementing Wx::TextEntry interface) to check that only + # valid floating point values can be entered into them. Currently only fixed + # format is supported on input, i.e. scientific format with mantissa and + # exponent is not supported. + # + # Similarly to Wx::IntegerValidator, the range for the accepted values is by + # default set appropriately for the type (`double`). Additionally, this validator allows + # to specify the maximum number of digits that can be entered after the + # decimal separator. By default this is also set appropriately for the type + # used, e.g. 15 for `double` on a typical IEEE-754-based + # implementation. As with the range, the precision can be restricted after + # the validator creation if necessary. + # + # When the validator displays numbers with decimal or thousands separators, + # the characters used for the separators (usually "." or ",") depend on the + # locale set with Wx::Locale (note that you shouldn't change locale with + # `setlocale()` as this can result in a mismatch between the separators used by + # Wx::Locale and the one used by the run-time library). + # + # @example A simple example of using this class: + # class MyDialog < Wx::Dialog + # def initialize + # super(..) + # ... + # # Allow floating point numbers from 0 to 100 with 2 decimal + # # digits only and handle empty string as 0 by default. + # val = Wx::FloatValidator.new(2, Wx::NUM_VAL_ZERO_AS_BLANK) + # val.set_range(0, 100) + # + # # Associate it with the text control: + # Wx::TextCtrl.new(this, ..., val) + # end + # + # end + # + # @see Wx::Validator + # @see Wx::GenericValidator + # @see Wx::TextValidator + # @see Wx::IntegerValidator + # @see Wx::UnsignedValidator + # @wxrb_require USE_VALIDATORS + class FloatValidator < Validator + + # @overload initialize(style=Wx::NumValidatorStyle::NUM_VAL_DEFAULT) + # Constructor. + # @param [Wx::NumValidatorStyle] style A combination of Wx::NumValidatorStyle enum values. + # @return [Wx::FloatValidator] + # @overload initialize(precision, style) + # Constructor for validator specifying the precision. + # @param [Integer] precision The number of decimal digits after the decimal separator to show and accept. + # @param [Wx::NumValidatorStyle] style A combination of Wx::NumValidatorStyle enum values. + # @return [Wx::FloatValidator] + # @overload initialize(other) + # Copy constructor. + # @param [Wx::FloatValidator] other + # @return [Wx::FloatValidator] + def initialize(*arg) end + + # Sets the minimal value accepted by the validator. + # + # This value is inclusive, i.e. the value equal to min is accepted. + # @param [Float] min + def set_min(min) end + alias :min= :set_min + + # Gets the minimal value accepted by the validator. + # @return [Float] + def get_min; end + alias :min :get_min + + # Sets the maximal value accepted by the validator. + # + # This value is inclusive, i.e. the value equal to max is accepted. + # @param [Float] max + def set_max(max) end + alias :max= :set_max + + # Gets the maximum value accepted by the validator. + # @return [Float] + def get_max; end + alias :max :get_max + + # Sets both minimal and maximal values accepted by the validator. + # + # Calling this is equivalent to calling both #set_min and #set_max. + # @param [Float] min + # @param [Float] max + def set_range(min, max) end + alias :range= :set_range + + # Gets both minimal and maximal values accepted by the validator. + # + # Returns an array with `[min, max]` + # @return [Array] + def get_range; end + alias :range :get_range + + # Change the validator style. + # + # Can be used to change the style of the validator after its creation. + # @param [Wx::NumValidatorStyle] style A combination of Wx::NumValidatorStyle enum values. + def set_style(style) end + alias :style= :set_style + + # Set precision. + # + # Precision is the number of digits shown (and accepted on input) + # after the decimal point. By default this is set to the maximal + # precision supported by the type handled by the validator in its + # constructor. + # @param [Integer] precision + def set_precision(precision) end + alias :precision= :set_precision + + # Set factor used for displaying the value. + # + # The value associated with the validator is multiplied by the factor + # before displaying it and divided by it when retrieving its value from + # the control. By default, the factor is 1, so the actual value is not + # affected by it, but it can be set to, for example, 100, to display the + # value in percents while still storing it as absolute value. + # @param [Float] factor + def set_factor(factor) end + alias :factor= :set_factor + + end + +end diff --git a/rakelib/lib/director/num_validator.rb b/rakelib/lib/director/num_validator.rb new file mode 100644 index 00000000..434f9f20 --- /dev/null +++ b/rakelib/lib/director/num_validator.rb @@ -0,0 +1,411 @@ +# Copyright (c) 2023 M.J.N. Corino, The Netherlands +# +# This software is released under the MIT license. + +### +# wxRuby3 wxWidgets interface director +### + +require_relative './validator' + +module WXRuby3 + + class Director + + class NumValidator < Validator + + def setup + spec.items.replace ['valnum.h'] # enum from XML but only manually defined custom wxRuby classes + super + spec.initialize_at_end = true # no inheritance/XML info to analyze + spec.ignore 'wxMakeIntegerValidator', 'wxMakeFloatingPointValidator' + # need to explicitly declare this here as we do not have any XML extracted items + # so the post processing of the EvtHandler director does not work + spec.add_header_code <<~__HEREDOC + static WxRuby_ID __wxrb_try_before_id("try_before"); + static WxRuby_ID __wxrb_try_after_id("try_after"); + __HEREDOC + # provide custom wxRuby numeric validator classes + spec.add_header_code <<~__HEREDOC + #include + + #ifdef wxLongLong_t + typedef wxLongLong_t LongestValueType; + typedef wxULongLong_t ULongestValueType; + #else + typedef long LongestValueType; + typedef unsigned long ULongestValueType; + #endif + class WXIntegerValidator : public wxIntegerValidator, public wxRubyValidatorBinding + { + public: + WXIntegerValidator(const WXIntegerValidator& v) + : wxIntegerValidator(v) + , wxRubyValidatorBinding(v) + { + // this is horrible but why they needed to explicitly declare this as a const member is beyond me + *reinterpret_cast ((void*)&m_value) = &m_valueCache; + } + WXIntegerValidator(long style=wxNUM_VAL_DEFAULT) + : wxIntegerValidator(&m_valueCache, style) + {} + WXIntegerValidator(LongestValueType min, LongestValueType max, long style=wxFILTER_NONE) + : wxIntegerValidator(&m_valueCache, min, max, style) + {} + virtual ~WXIntegerValidator() + { + wxRuby_ReleaseEvtHandlerProcs(this); + } + + virtual wxObject* Clone() const override + { + WXIntegerValidator* clone = new WXIntegerValidator(*this); + clone->m_valueCache = this->m_valueCache; + return clone; + } + + virtual void SetWindow(wxWindow *win) override + { + this->wxIntegerValidator::SetWindow(win); + VALUE self = get_self(); + // make sure Ruby does not own this validator instance anymore + RDATA(self)->dfree = SWIG_RubyRemoveTracking; + } + + virtual bool TransferFromWindow () override + { + // call super + if (this->wxIntegerValidator::TransferFromWindow()) + { + // ok, data is retrieved from window and cached + // now allow any defined binding handler to pass on the data + return this->DoOnTransferFromWindow(LL2NUM(m_valueCache)); + } + return false; + } + virtual bool TransferToWindow () override + { + // collect data from any defined binding handler + VALUE data = this->DoOnTransferToWindow(); + if (NIL_P(data)) + { + m_valueCache = 0; + } + else + { + m_valueCache = NUM2LL(data); + } + // now allow standard functionality to transfer to window + return this->wxIntegerValidator::TransferToWindow(); + } + + protected: + static VALUE c_IntegerValidator; + + VALUE get_self() + { + VALUE self = SWIG_RubyInstanceFor(this); + // if this is a C++ created clone (wxWidgets clones validators that are set) it's not tracked yet + if (NIL_P(self)) + { + if (NIL_P(c_IntegerValidator)) + { + c_IntegerValidator = rb_const_get(mWxCore, rb_intern("IntegerValidator")); + } + swig_type_info* swig_type = wxRuby_GetSwigTypeForClass(c_IntegerValidator); + self = SWIG_NewPointerObj(this, swig_type, 0); // wrap but don't make Ruby own it + } + return self; + } + + LongestValueType m_valueCache {}; + }; + + VALUE WXIntegerValidator::c_IntegerValidator = Qnil; + + WXRUBY_EXPORT void GC_mark_wxIntegerValidator(void* ptr) + { + if (ptr) + reinterpret_cast (ptr)->GC_Mark(); + } + + class WXUnsignedValidator : public wxIntegerValidator, public wxRubyValidatorBinding + { + public: + WXUnsignedValidator(const WXUnsignedValidator& v) + : wxIntegerValidator(v) + , wxRubyValidatorBinding(v) + { + // this is horrible but why they needed to explicitly declare this as a const member is beyond me + *reinterpret_cast ((void*)&m_value) = &m_valueCache; + } + WXUnsignedValidator(long style=wxFILTER_NONE) + : wxIntegerValidator(&m_valueCache, style) + {} + WXUnsignedValidator(LongestValueType min, LongestValueType max, long style=wxFILTER_NONE) + : wxIntegerValidator(&m_valueCache, min, max, style) + {} + virtual ~WXUnsignedValidator() + { + wxRuby_ReleaseEvtHandlerProcs(this); + } + + virtual wxObject* Clone() const override + { + WXUnsignedValidator* clone = new WXUnsignedValidator(*this); + clone->m_valueCache = this->m_valueCache; + return clone; + } + + virtual void SetWindow(wxWindow *win) override + { + this->wxIntegerValidator::SetWindow(win); + VALUE self = get_self(); + // make sure Ruby does not own this validator instance anymore + RDATA(self)->dfree = SWIG_RubyRemoveTracking; + } + + virtual bool TransferFromWindow () override + { + // call super + if (this->wxIntegerValidator::TransferFromWindow()) + { + // ok, data is retrieved from window and cached + // now allow any defined binding handler to pass on the data + return this->DoOnTransferFromWindow(ULL2NUM(m_valueCache)); + } + return false; + } + virtual bool TransferToWindow () override + { + // collect data from any defined binding handler + VALUE data = this->DoOnTransferToWindow(); + if (NIL_P(data)) + { + m_valueCache = 0; + } + else + { + m_valueCache = NUM2ULL(data); + } + // now allow standard functionality to transfer to window + return this->wxIntegerValidator::TransferToWindow(); + } + + protected: + static VALUE c_UnsignedValidator; + + VALUE get_self() + { + VALUE self = SWIG_RubyInstanceFor(this); + // if this is a C++ created clone (wxWidgets clones validators that are set) it's not tracked yet + if (NIL_P(self)) + { + if (NIL_P(c_UnsignedValidator)) + { + c_UnsignedValidator = rb_const_get(mWxCore, rb_intern("UnsignedValidator")); + } + swig_type_info* swig_type = wxRuby_GetSwigTypeForClass(c_UnsignedValidator); + self = SWIG_NewPointerObj(this, swig_type, 0); // wrap but don't make Ruby own it + } + return self; + } + + ULongestValueType m_valueCache {}; + }; + + VALUE WXUnsignedValidator::c_UnsignedValidator = Qnil; + + WXRUBY_EXPORT void GC_mark_wxUnsignedValidator(void* ptr) + { + if (ptr) + reinterpret_cast (ptr)->GC_Mark(); + } + + class WXFloatValidator : public wxFloatingPointValidator, public wxRubyValidatorBinding + { + public: + WXFloatValidator(const WXFloatValidator& v) + : wxFloatingPointValidator(v) + , wxRubyValidatorBinding(v) + { + // this is horrible but why they needed to explicitly declare this as a const member is beyond me + *reinterpret_cast ((void*)&m_value) = &m_valueCache; + } + WXFloatValidator(long style=wxFILTER_NONE) + : wxFloatingPointValidator(&m_valueCache, style) + {} + WXFloatValidator(int precision, long style=wxFILTER_NONE) + : wxFloatingPointValidator(precision, &m_valueCache, style) + {} + virtual ~WXFloatValidator() + { + wxRuby_ReleaseEvtHandlerProcs(this); + } + + virtual wxObject* Clone() const override + { + return new WXFloatValidator(*this); + } + + virtual void SetWindow(wxWindow *win) override + { + this->wxFloatingPointValidator::SetWindow(win); + VALUE self = get_self(); + // make sure Ruby does not own this validator instance anymore + RDATA(self)->dfree = SWIG_RubyRemoveTracking; + } + + virtual bool TransferFromWindow () override + { + // call super + if (this->wxFloatingPointValidator::TransferFromWindow()) + { + // ok, data is retrieved from window and cached + // now allow any defined binding handler to pass on the data + return this->DoOnTransferFromWindow(DBL2NUM(m_valueCache)); + } + return false; + } + virtual bool TransferToWindow () override + { + // collect data from any defined binding handler + VALUE data = this->DoOnTransferToWindow(); + if (NIL_P(data)) + { + m_valueCache = 0.0; + } + else + { + m_valueCache = NUM2DBL(data); + } + // now allow standard functionality to transfer to window + return this->wxFloatingPointValidator::TransferToWindow(); + } + + protected: + static VALUE c_FloatValidator; + + VALUE get_self() + { + VALUE self = SWIG_RubyInstanceFor(this); + // if this is a C++ created clone (wxWidgets clones validators that are set) it's not tracked yet + if (NIL_P(self)) + { + if (NIL_P(c_FloatValidator)) + { + c_FloatValidator = rb_const_get(mWxCore, rb_intern("FloatValidator")); + } + swig_type_info* swig_type = wxRuby_GetSwigTypeForClass(c_FloatValidator); + self = SWIG_NewPointerObj(this, swig_type, 0); // wrap but don't make Ruby own it + } + return self; + } + + double m_valueCache {}; + }; + + VALUE WXFloatValidator::c_FloatValidator = Qnil; + + WXRUBY_EXPORT void GC_mark_wxFloatValidator(void* ptr) + { + if (ptr) + reinterpret_cast (ptr)->GC_Mark(); + } + __HEREDOC + spec.add_swig_code 'GC_MANAGE_AS_OBJECT(WXIntegerValidator);', + '%markfunc WXIntegerValidator "GC_mark_wxIntegerValidator";', + 'GC_MANAGE_AS_OBJECT(WXUnsignedValidator);', + '%markfunc WXUnsignedValidator "GC_mark_wxUnsignedValidator";', + 'GC_MANAGE_AS_OBJECT(WXFloatValidator);', + '%markfunc WXFloatValidator "GC_mark_wxFloatValidator";' + spec.new_object 'WXIntegerValidator::Clone', + 'WXUnsignedValidator::Clone', + 'WXFloatValidator::Clone' + spec.suppress_warning(473, + 'WXIntegerValidator::Clone', + 'WXUnsignedValidator::Clone', + 'WXFloatValidator::Clone') + spec.map_apply 'long long * OUTPUT' => ['wxLongLong_t& min', 'wxLongLong_t& max'] + spec.map_apply 'unsigned long long * OUTPUT' => ['wxULongLong_t& min', 'wxULongLong_t& max'] + spec.map_apply 'double * OUTPUT' => ['double& min', 'double& max'] + spec.swig_import 'swig/classes/include/wxObject.h' + spec.swig_import 'swig/classes/include/wxEvtHandler.h' + spec.swig_import 'swig/classes/include/wxValidator.h' + # hardcoded interface declarations + spec.add_interface_code <<~__HEREDOC + // Bit masks used for numeric validator styles. + enum wxNumValidatorStyle + { + wxNUM_VAL_DEFAULT = 0x0, + wxNUM_VAL_THOUSANDS_SEPARATOR = 0x1, + wxNUM_VAL_ZERO_AS_BLANK = 0x2, + wxNUM_VAL_NO_TRAILING_ZEROES = 0x4 + }; + + class WXIntegerValidator : public wxValidator + { + public: + WXIntegerValidator(const WXIntegerValidator& v); + WXIntegerValidator(long style=wxNUM_VAL_DEFAULT); + WXIntegerValidator(wxLongLong_t min, wxLongLong_t max, long style=wxFILTER_NONE); + virtual ~WXIntegerValidator(); + + virtual WXIntegerValidator* Clone() const; + + void SetMin(wxLongLong_t min); + wxLongLong_t GetMin() const; + void SetMax(wxLongLong_t max); + wxLongLong_t GetMax() const; + void SetRange(wxLongLong_t min, wxLongLong_t max); + void GetRange(wxLongLong_t& min, wxLongLong_t& max) const; + void SetStyle(int style); + }; + + class WXUnsignedValidator : public wxValidator + { + public: + WXUnsignedValidator(const WXUnsignedValidator& v); + WXUnsignedValidator(long style=wxNUM_VAL_DEFAULT); + WXUnsignedValidator(wxULongLong_t min, wxULongLong_t max, long style=wxFILTER_NONE); + virtual ~WXUnsignedValidator(); + + virtual WXUnsignedValidator* Clone() const; + + void SetMin(wxULongLong_t min); + wxULongLong_t GetMin() const; + void SetMax(wxULongLong_t max); + wxULongLong_t GetMax() const; + void SetRange(wxULongLong_t min, wxULongLong_t max); + void GetRange(wxULongLong_t& min, wxULongLong_t& max) const; + void SetStyle(int style); + }; + + class WXFloatValidator : public wxValidator + { + public: + WXFloatValidator(const WXFloatValidator& v); + WXFloatValidator(long style=wxNUM_VAL_DEFAULT); + WXFloatValidator(int precision, long style); + virtual ~WXFloatValidator(); + + virtual WXFloatValidator* Clone() const; + + void SetMin(double min); + double GetMin() const; + void SetMax(double max); + double GetMax() const; + void SetRange(double min, double max); + void GetRange(double& min, double& max) const; + void SetStyle(int style); + + void SetPrecision(unsigned precision); + void SetFactor(double factor); + }; + __HEREDOC + end + end # class NumValidator + + end # class Director + +end # module WXRuby3 diff --git a/rakelib/lib/specs/interfaces.rb b/rakelib/lib/specs/interfaces.rb index dd6e712a..46702ade 100644 --- a/rakelib/lib/specs/interfaces.rb +++ b/rakelib/lib/specs/interfaces.rb @@ -56,6 +56,7 @@ module WXRuby3 Director.Spec(pkg, 'wxCaret') Director.Spec(pkg, 'wxValidator', requirements: %[USE_VALIDATORS]) Director.Spec(pkg, 'wxTextValidator', requirements: %[USE_VALIDATORS]) + Director.Spec(pkg, 'wxNumValidator', requirements: %[USE_VALIDATORS]) Director.Spec(pkg, 'wxAccelerator', requirements: %w[USE_ACCEL]) Director.Spec(pkg, 'wxMenuItem', requirements: %w[USE_MENUS]) Director.Spec(pkg, 'wxMenuBar', requirements: %w[USE_MENUBAR]) From 7bba35665ba6ee862c94228915c54fd5561cec90 Mon Sep 17 00:00:00 2001 From: Martin Corino Date: Sat, 18 Nov 2023 12:02:51 +0100 Subject: [PATCH 11/21] add validator tests --- tests/test_validators.rb | 489 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 489 insertions(+) create mode 100644 tests/test_validators.rb diff --git a/tests/test_validators.rb b/tests/test_validators.rb new file mode 100644 index 00000000..4b665f55 --- /dev/null +++ b/tests/test_validators.rb @@ -0,0 +1,489 @@ +# Copyright (c) 2023 M.J.N. Corino, The Netherlands +# +# This software is released under the MIT license. + +require_relative './lib/wxframe_runner' + +class ValidatorTests < WxRuby::Test::GUITests + + def setup + super + @text = Wx::TextCtrl.new(frame_win, name: 'Text') + end + + def cleanup + @text.destroy if @text + @text = nil + super + end + + attr_accessor :text + + class MyTextValidator < Wx::Validator + + # overload for customized functionality + def do_transfer_from_window + get_window.get_value + end + + # overload for customized functionality + def do_transfer_to_window(val) + get_window.set_value(val) + true + end + + def validate(parent) + get_window && get_window.value != 'F*ck!' + end + + end + + def test_my_text_validator + data = 'hello' + val = MyTextValidator.new + + val.on_transfer_to_window { data } + val.on_transfer_from_window { |v| data = v } + text.set_validator(val) + + assert_true(text.empty?) + + assert_true(text.transfer_data_to_window) + + assert_equal('hello', text.value) + assert_true(frame_win.validate) + + text.value = 'F*ck!' + assert_false(frame_win.validate) + + text.value = 'OMG!' + assert_true(frame_win.validate) + + assert_true(text.transfer_data_from_window) + assert_equal('OMG!', data) + end + +end + +class TextValidatorTests < WxRuby::Test::GUITests + + def setup + super + @text = nil + end + + def cleanup + @text.destroy if @text + @text = nil + super + end + + attr_accessor :text + + def test_basic + val = Wx::TextValidator.new(Wx::TextValidatorStyle::FILTER_NONE) + + assert_empty(val.valid?("wx-90.?! @_~E+{")) + + val.set_style(Wx::TextValidatorStyle::FILTER_EMPTY) + assert_not_empty(val.valid?('')) + assert_empty(val.valid?(' ')) + + val.set_style(Wx::TextValidatorStyle::FILTER_ASCII) + assert_empty(val.valid?("wx-90.?! @_~E+{")) + + val.set_style(Wx::TextValidatorStyle::FILTER_ALPHA) + + assert_empty(val.valid?("wx")) + assert_not_empty(val.valid?("wx_")) # _ is not alpha + + val.set_style(Wx::TextValidatorStyle::FILTER_ALPHANUMERIC) + + assert_empty(val.valid?("wx01")) + assert_not_empty(val.valid?("wx 01")) # 'space' is not alphanumeric + + val.set_style(Wx::TextValidatorStyle::FILTER_DIGITS) + + assert_empty(val.valid?("97")) + assert_not_empty(val.valid?("9.7")) # . is not digit + + val.set_style(Wx::TextValidatorStyle::FILTER_XDIGITS) + + assert_empty(val.valid?("90AEF")) + assert_not_empty(val.valid?("90GEF")) # G is not xdigit + + val.set_style(Wx::TextValidatorStyle::FILTER_NUMERIC) + + assert_empty(val.valid?("+90.e-2")) + assert_not_empty(val.valid?("-8.5#0")) # # is not numeric + + val.set_style(Wx::TextValidatorStyle::FILTER_INCLUDE_LIST) + + val.set_includes(%w[wxMSW wxGTK wxOSX]) + + assert_empty(val.valid?("wxGTK")) + assert_not_empty(val.valid?("wxQT")) # wxQT is not included + + val.set_excludes(%w[wxGTK]) + + assert_empty(val.valid?("wxOSX")) + assert_not_empty(val.valid?("wxGTK")) # wxGTK now excluded + + val.set_style(Wx::TextValidatorStyle::FILTER_EXCLUDE_LIST) + + val.set_excludes(%w[wxMSW wxGTK wxOSX]) + + assert_empty(val.valid?("wxQT & wxUNIV")) + assert_not_empty(val.valid?("wxMSW")) # wxMSW is excluded + + val.set_includes(%w[wxMSW]) # exclusion takes priority over inclusion. + + assert_empty(val.valid?("wxUNIV")) + assert_not_empty(val.valid?("wxMSW")) # wxMSW still excluded + + val.set_style(Wx::TextValidatorStyle::FILTER_INCLUDE_CHAR_LIST) + val.set_char_includes("tuvwxyz.012+-") + + assert_empty(val.valid?("0.2t+z-1")) + assert_not_empty(val.valid?("x*y")); # * is not included + + val.add_char_includes("*") + + assert_empty(val.valid?("x*y")) # * now included + assert_not_empty(val.valid?("x%y")) # % is not included + + val.add_char_excludes("*") # exclusion takes priority over inclusion. + + assert_not_empty(val.valid?("x*y")) # * now excluded + + val.set_style(Wx::TextValidatorStyle::FILTER_EXCLUDE_CHAR_LIST) + val.set_char_excludes("tuvwxyz.012+-") + + assert_empty(val.valid?("A*B=?")) + assert_not_empty(val.valid?("0.6/t")) # t is excluded + + val.add_char_includes("t") # exclusion takes priority over inclusion. + + assert_not_empty(val.valid?("0.6/t")) # t still excluded + end + + def test_text_ctrl_validate + self.text = Wx::TextCtrl.new(frame_win, name: 'Text') + + data = 'wxwidgets' + val = Wx::TextValidator.new(Wx::TextValidatorStyle::FILTER_ALPHA) + val.on_transfer_to_window { data } + val.on_transfer_from_window { |v| data = v } + text.set_validator(val) + + assert_true(text.empty?) + + assert_true(text.transfer_data_to_window) + + assert_equal('wxwidgets', text.value) + + assert_equal('wxwidgets', data) + text.value = 'wxRuby' + assert_true(text.transfer_data_from_window) + assert_equal('wxRuby', data) + end + + class CustomTextValidator < Wx::TextValidator + + def initialize(arg) + if arg.is_a?(self.class) + super(arg) + @value_owner = arg.value_owner + else + super(Wx::TextValidatorStyle::FILTER_NONE) + @value_owner = arg + self.on_transfer_to_window :handle_get_data + self.on_transfer_from_window :handle_set_data + end + end + + attr_reader :value_owner + + def clone + self.class.new(self) + end + + protected + + def handle_set_data(data) + @value_owner.value = data + end + + def handle_get_data + @value_owner.value + end + + end + + class ModelData + + def initialize(val = '123') + @value = val + end + + attr_accessor :value + + end + + def test_custom_validator + self.text = Wx::TextCtrl.new(frame_win, name: 'Text') + + mod = ModelData.new + + assert_equal('123', mod.value) + + val = CustomTextValidator.new(mod) + text.set_validator(val) + + assert_true(text.empty?) + + assert_true(text.transfer_data_to_window) + + assert_equal('123', text.value) + + text.value = '456' + + assert_true(text.transfer_data_from_window) + + assert_equal('456', mod.value) + end + +end + +class IntegerValidatorTests < WxRuby::Test::GUITests + + def setup + super + @text = Wx::TextCtrl.new(frame_win, name: 'Text') + end + + def cleanup + @text.destroy if @text + @text = nil + super + end + + attr_accessor :text + + def test_transfer + data = 0 + valInt = Wx::IntegerValidator.new + valInt.on_transfer_to_window { data } + valInt.on_transfer_from_window { |v| data = v } + text.validator = valInt + + assert_true(text.transfer_data_to_window) + assert_equal('0', text.value) + + data = 17 + assert_true(text.transfer_data_to_window) + assert_equal('17', text.value) + + text.change_value("foobar") + assert_false(text.transfer_data_from_window) + + text.change_value('-234') + assert_true(text.transfer_data_from_window) + assert_equal(-234, data) + + text.change_value('9223372036854775808') # == LLONG_MAX + 1 + assert_false(text.transfer_data_from_window) + + text.clear + assert_false(text.transfer_data_from_window) + end + + def test_transfer_range + data = 0 + valInt = Wx::IntegerValidator.new(-20, 20, Wx::NumValidatorStyle::NUM_VAL_ZERO_AS_BLANK) + valInt.on_transfer_to_window { data } + valInt.on_transfer_from_window { |v| data = v } + text.validator = valInt + + assert_true(text.transfer_data_to_window) + assert_equal('', text.value) # Wx::NumValidatorStyle::NUM_VAL_ZERO_AS_BLANK + + data = 17 + assert_true(text.transfer_data_to_window) + assert_equal('17', text.value) + + text.change_value('-234') + assert_false(text.transfer_data_from_window) + + text.change_value('-20') + assert_true(text.transfer_data_from_window) + assert_equal(-20, data) + + text.change_value('21') # == max + 1 + assert_false(text.transfer_data_from_window) + + text.clear + assert_true(text.transfer_data_from_window) + assert_equal(0, data) # Wx::NumValidatorStyle::NUM_VAL_ZERO_AS_BLANK + end + +end + +class UnsignedValidatorTests < WxRuby::Test::GUITests + + def setup + super + @text = Wx::TextCtrl.new(frame_win, name: 'Text') + end + + def cleanup + @text.destroy if @text + @text = nil + super + end + + attr_accessor :text + + def test_transfer + data = 0 + valInt = Wx::UnsignedValidator.new + valInt.on_transfer_to_window { data } + valInt.on_transfer_from_window { |v| data = v } + text.validator = valInt + + assert_true(text.transfer_data_to_window) + assert_equal('0', text.value) + + data = 17 + assert_true(text.transfer_data_to_window) + assert_equal('17', text.value) + + text.change_value('-1') + assert_false(text.transfer_data_from_window) + + text.change_value('234') + assert_true(text.transfer_data_from_window) + assert_equal(234, data) + + text.change_value((2*64).to_s) # == ULLONG_MAX + assert_true(text.transfer_data_from_window) + + text.clear + assert_false(text.transfer_data_from_window) + end + + def test_transfer_range + data = 1 + valInt = Wx::UnsignedValidator.new(1, 20) + valInt.on_transfer_to_window { data } + valInt.on_transfer_from_window { |v| data = v } + text.validator = valInt + + assert_true(text.transfer_data_to_window) + assert_equal('1', text.value) + + data = 17 + assert_true(text.transfer_data_to_window) + assert_equal('17', text.value) + + text.change_value('0') + assert_false(text.transfer_data_from_window) + + text.change_value('234') + assert_false(text.transfer_data_from_window) + + text.change_value('20') + assert_true(text.transfer_data_from_window) + assert_equal(20, data) + + text.change_value('1') + assert_true(text.transfer_data_from_window) + assert_equal(1, data) + + text.clear + assert_false(text.transfer_data_from_window) + end + +end + +class FloatValidatorTests < WxRuby::Test::GUITests + + def setup + super + @text = Wx::TextCtrl.new(frame_win, name: 'Text') + end + + def cleanup + @text.destroy if @text + @text = nil + super + end + + attr_accessor :text + + def test_transfer + dpt = Wx::Locale.get_info(Wx::LocaleInfo::LOCALE_DECIMAL_POINT) + data = 0.0 + valFlt = Wx::FloatValidator.new(3, Wx::NumValidatorStyle::NUM_VAL_DEFAULT) + valFlt.on_transfer_to_window { data } + valFlt.on_transfer_from_window { |v| data = v } + text.validator = valFlt + + assert_true(text.transfer_data_to_window) + assert_equal("0#{dpt}000", text.value) + + text.validator.style = Wx::NumValidatorStyle::NUM_VAL_NO_TRAILING_ZEROES + + assert_true(text.transfer_data_to_window) + assert_equal('0', text.value) + + data = 17.123 + assert_true(text.transfer_data_to_window) + assert_equal("17#{dpt}123", text.value) + + data = 17.1236 + assert_true(text.transfer_data_to_window) + assert_equal("17#{dpt}124", text.value) + + text.change_value("foobar") + assert_false(text.transfer_data_from_window) + + text.change_value('-234') + assert_true(text.transfer_data_from_window) + assert_equal(-234, data) + + text.clear + assert_false(text.transfer_data_from_window) + end + + def test_transfer_range + dpt = Wx::Locale.get_info(Wx::LocaleInfo::LOCALE_DECIMAL_POINT) + data = 0 + valFlt = Wx::FloatValidator.new(3, Wx::NumValidatorStyle::NUM_VAL_NO_TRAILING_ZEROES) + valFlt.set_range(-0.500, 0.500) + valFlt.on_transfer_to_window { data } + valFlt.on_transfer_from_window { |v| data = v } + text.validator = valFlt + + assert_true(text.transfer_data_to_window) + assert_equal('0', text.value) + + data = 0.123 + assert_true(text.transfer_data_to_window) + assert_equal("0#{dpt}123", text.value) + + text.change_value('-0.734') + assert_false(text.transfer_data_from_window) + + text.change_value('-0.234') + assert_true(text.transfer_data_from_window) + assert_equal(-0.234, data) + + text.change_value('0.501') # == max + 0.001 + assert_false(text.transfer_data_from_window) + + text.clear + assert_false(text.transfer_data_from_window) + end + +end From ae9960a311c570dfcddc81b66af962e5531ecea4 Mon Sep 17 00:00:00 2001 From: Martin Corino Date: Sat, 18 Nov 2023 12:21:48 +0100 Subject: [PATCH 12/21] add Wx::BitmapToggleButton --- lib/wx/keyword_defs.rb | 7 ++++++- rakelib/lib/director/toggle_button.rb | 1 + 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/lib/wx/keyword_defs.rb b/lib/wx/keyword_defs.rb index 2ff96a8c..179dd6e0 100644 --- a/lib/wx/keyword_defs.rb +++ b/lib/wx/keyword_defs.rb @@ -290,7 +290,12 @@ # A button which stays pressed when clicked by user. Wx::define_keyword_ctors(Wx::ToggleButton) do wx_ctor_params :id, :label, :pos, :size, :style - wx_ctor_params :validator, :name => 'checkBox' + wx_ctor_params :validator, :name => 'toggleButton' +end + +Wx::define_keyword_ctors(Wx::BitmapToggleButton) do + wx_ctor_params :id, :label, :pos, :size, :style + wx_ctor_params :validator, :name => 'bitmapToggleButton' end # Control showing an entire calendar month diff --git a/rakelib/lib/director/toggle_button.rb b/rakelib/lib/director/toggle_button.rb index d13217db..d503a670 100644 --- a/rakelib/lib/director/toggle_button.rb +++ b/rakelib/lib/director/toggle_button.rb @@ -15,6 +15,7 @@ class Director class ToggleButton < Window def setup + spec.items << 'wxBitmapToggleButton' spec.include('wx/tglbtn.h') super end From 03f9ab5851c02ea50b77c250c16f7eabccb804b4 Mon Sep 17 00:00:00 2001 From: Martin Corino Date: Sat, 18 Nov 2023 17:35:42 +0100 Subject: [PATCH 13/21] updates to enable Wx::GenericValidator creation --- ext/wxruby3/include/wxruby-Validator.h | 2 + ext/wxruby3/include/wxruby-ValidatorBinding.h | 17 +++++- lib/wx/core/validator.rb | 5 ++ lib/wx/doc/validator.rb | 20 +++++++ rakelib/lib/director/num_validator.rb | 59 +++++++++++-------- .../director/numeric_property_validator.rb | 21 ++++--- rakelib/lib/director/text_validator.rb | 23 ++++---- rakelib/lib/director/validator.rb | 56 +++++++++++++++--- 8 files changed, 147 insertions(+), 56 deletions(-) diff --git a/ext/wxruby3/include/wxruby-Validator.h b/ext/wxruby3/include/wxruby-Validator.h index 4133f4d3..3f28ec53 100644 --- a/ext/wxruby3/include/wxruby-Validator.h +++ b/ext/wxruby3/include/wxruby-Validator.h @@ -32,6 +32,8 @@ class WXRUBY_EXPORT wxRubyValidator : public wxValidator, public wxRubyValidator virtual VALUE DoTransferFromWindow(); virtual bool DoTransferToWindow(VALUE data); + + virtual VALUE get_self() override; }; #endif /* _WXRUBY_VALIDATOR_HASH_H */ diff --git a/ext/wxruby3/include/wxruby-ValidatorBinding.h b/ext/wxruby3/include/wxruby-ValidatorBinding.h index 5c7fefb6..3721467b 100644 --- a/ext/wxruby3/include/wxruby-ValidatorBinding.h +++ b/ext/wxruby3/include/wxruby-ValidatorBinding.h @@ -13,7 +13,8 @@ class WXRUBY_EXPORT wxRubyValidatorBinding { public: wxRubyValidatorBinding () - : on_transfer_from_win_proc_(Qnil) + : self_(Qnil) + , on_transfer_from_win_proc_(Qnil) , on_transfer_to_win_proc_(Qnil) {} @@ -26,8 +27,11 @@ class WXRUBY_EXPORT wxRubyValidatorBinding this->on_transfer_to_win_proc_ = proc; } - bool DoOnTransferFromWindow(VALUE data); VALUE DoOnTransferToWindow(); + bool DoOnTransferFromWindow(VALUE data); + + bool OnTransferFromWindow(VALUE data); + VALUE OnTransferToWindow(); void GC_Mark() { @@ -36,15 +40,22 @@ class WXRUBY_EXPORT wxRubyValidatorBinding } protected: + static WxRuby_ID do_on_transfer_from_window_id; + static WxRuby_ID do_on_transfer_to_window_id; static WxRuby_ID call_id; wxRubyValidatorBinding (const wxRubyValidatorBinding& vb) - : on_transfer_from_win_proc_(vb.on_transfer_from_win_proc_) + : self_(Qnil) + , on_transfer_from_win_proc_(vb.on_transfer_from_win_proc_) , on_transfer_to_win_proc_(vb.on_transfer_to_win_proc_) {} void CopyBindings(const wxRubyValidatorBinding* val_bind); + virtual VALUE get_self() = 0; + + VALUE self_; + private: VALUE on_transfer_from_win_proc_; VALUE on_transfer_to_win_proc_; diff --git a/lib/wx/core/validator.rb b/lib/wx/core/validator.rb index ceaf4951..398effe2 100644 --- a/lib/wx/core/validator.rb +++ b/lib/wx/core/validator.rb @@ -20,11 +20,16 @@ def clone def do_transfer_from_window nil # by default just return nil end + protected :do_transfer_from_window # overload for customized functionality def do_transfer_to_window(_data) true # by default just return true end + protected :do_transfer_to_window + + protected :do_on_transfer_from_window + protected :do_on_transfer_to_window module Binding def self.included(base) diff --git a/lib/wx/doc/validator.rb b/lib/wx/doc/validator.rb index 7439a9a5..92029505 100644 --- a/lib/wx/doc/validator.rb +++ b/lib/wx/doc/validator.rb @@ -24,12 +24,16 @@ def initialize(*arg) end # Overload for customized functionality. # @return [Object] retrieved data from window def do_transfer_from_window; end + protected :do_transfer_from_window # Method called when transferring data to window. # Should transfer the given data to the associated window and return true if successful. # By default does nothing and just returns true. # Overload for customized functionality. + # @param [Object] data + # @return [Boolean] def do_transfer_to_window(data) end + protected :do_transfer_to_window # Mixin module providing data binding options for validators. module Binding @@ -84,6 +88,22 @@ def on_transfer_from_window(meth=nil, &block) end # @yieldreturn [Object] the data to transfer to the window def on_transfer_to_window(meth=nil, &block) end + # Method called with data transferred from window. + # By default will call the on_transfer_from_window handler if defined. + # Returns true if successful or none defined. + # @param [Object] data + # @return [Boolean] + def do_on_transfer_from_window(data) end + protected :do_on_transfer_from_window + + # Method called to get data to transfer to window. + # By default will call the on_transfer_to_window handler if defined. + # Returns the handler's result if successful. + # Otherwise returns nil. + # @return [Object] + def do_on_transfer_to_window; end + protected :do_on_transfer_to_window + end include Binding diff --git a/rakelib/lib/director/num_validator.rb b/rakelib/lib/director/num_validator.rb index 434f9f20..e6bd1474 100644 --- a/rakelib/lib/director/num_validator.rb +++ b/rakelib/lib/director/num_validator.rb @@ -102,22 +102,25 @@ class WXIntegerValidator : public wxIntegerValidator, public w protected: static VALUE c_IntegerValidator; - VALUE get_self() + virtual VALUE get_self() override { - VALUE self = SWIG_RubyInstanceFor(this); - // if this is a C++ created clone (wxWidgets clones validators that are set) it's not tracked yet - if (NIL_P(self)) + if (NIL_P(this->self_)) { + this->self_ = SWIG_RubyInstanceFor(this); + // if this is a C++ created clone (wxWidgets clones validators that are set) it's not tracked yet + if (NIL_P(this->self_)) + { if (NIL_P(c_IntegerValidator)) { c_IntegerValidator = rb_const_get(mWxCore, rb_intern("IntegerValidator")); } swig_type_info* swig_type = wxRuby_GetSwigTypeForClass(c_IntegerValidator); - self = SWIG_NewPointerObj(this, swig_type, 0); // wrap but don't make Ruby own it + this->self_ = SWIG_NewPointerObj(this, swig_type, 0); // wrap but don't make Ruby own it + } } - return self; + return this->self_; } - + LongestValueType m_valueCache {}; }; @@ -195,20 +198,23 @@ class WXUnsignedValidator : public wxIntegerValidator, public protected: static VALUE c_UnsignedValidator; - VALUE get_self() + virtual VALUE get_self() override { - VALUE self = SWIG_RubyInstanceFor(this); - // if this is a C++ created clone (wxWidgets clones validators that are set) it's not tracked yet - if (NIL_P(self)) + if (NIL_P(this->self_)) { - if (NIL_P(c_UnsignedValidator)) + this->self_ = SWIG_RubyInstanceFor(this); + // if this is a C++ created clone (wxWidgets clones validators that are set) it's not tracked yet + if (NIL_P(this->self_)) { - c_UnsignedValidator = rb_const_get(mWxCore, rb_intern("UnsignedValidator")); + if (NIL_P(c_UnsignedValidator)) + { + c_UnsignedValidator = rb_const_get(mWxCore, rb_intern("UnsignedValidator")); + } + swig_type_info* swig_type = wxRuby_GetSwigTypeForClass(c_UnsignedValidator); + this->self_ = SWIG_NewPointerObj(this, swig_type, 0); // wrap but don't make Ruby own it } - swig_type_info* swig_type = wxRuby_GetSwigTypeForClass(c_UnsignedValidator); - self = SWIG_NewPointerObj(this, swig_type, 0); // wrap but don't make Ruby own it } - return self; + return this->self_; } ULongestValueType m_valueCache {}; @@ -286,20 +292,23 @@ class WXFloatValidator : public wxFloatingPointValidator, public wxRubyV protected: static VALUE c_FloatValidator; - VALUE get_self() + virtual VALUE get_self() override { - VALUE self = SWIG_RubyInstanceFor(this); - // if this is a C++ created clone (wxWidgets clones validators that are set) it's not tracked yet - if (NIL_P(self)) + if (NIL_P(this->self_)) { - if (NIL_P(c_FloatValidator)) + this->self_ = SWIG_RubyInstanceFor(this); + // if this is a C++ created clone (wxWidgets clones validators that are set) it's not tracked yet + if (NIL_P(this->self_)) { - c_FloatValidator = rb_const_get(mWxCore, rb_intern("FloatValidator")); + if (NIL_P(c_FloatValidator)) + { + c_FloatValidator = rb_const_get(mWxCore, rb_intern("FloatValidator")); + } + swig_type_info* swig_type = wxRuby_GetSwigTypeForClass(c_FloatValidator); + this->self_ = SWIG_NewPointerObj(this, swig_type, 0); // wrap but don't make Ruby own it } - swig_type_info* swig_type = wxRuby_GetSwigTypeForClass(c_FloatValidator); - self = SWIG_NewPointerObj(this, swig_type, 0); // wrap but don't make Ruby own it } - return self; + return this->self_; } double m_valueCache {}; diff --git a/rakelib/lib/director/numeric_property_validator.rb b/rakelib/lib/director/numeric_property_validator.rb index fd7ba9c9..04443f8f 100644 --- a/rakelib/lib/director/numeric_property_validator.rb +++ b/rakelib/lib/director/numeric_property_validator.rb @@ -82,20 +82,23 @@ class WXRubyNumericPropertyValidator : public wxNumericPropertyValidator, public private: static VALUE c_NumericPropertyValidator; - VALUE get_self() + virtual VALUE get_self() override { - VALUE self = SWIG_RubyInstanceFor(this); - // if this is a C++ created clone (wxWidgets clones validators that are set) it's not tracked yet - if (NIL_P(self)) + if (NIL_P(this->self_)) { - if (NIL_P(c_NumericPropertyValidator)) + this->self_ = SWIG_RubyInstanceFor(this); + // if this is a C++ created clone (wxWidgets clones validators that are set) it's not tracked yet + if (NIL_P(this->self_)) { - c_NumericPropertyValidator = rb_eval_string("Wx::PG::NumericPropertyValidator"); + if (NIL_P(c_NumericPropertyValidator)) + { + c_NumericPropertyValidator = rb_eval_string("Wx::PG::NumericPropertyValidator"); + } + swig_type_info* swig_type = wxRuby_GetSwigTypeForClass(c_NumericPropertyValidator); + this->self_ = SWIG_NewPointerObj(this, swig_type, 0); // wrap but don't make Ruby own it } - swig_type_info* swig_type = wxRuby_GetSwigTypeForClass(c_NumericPropertyValidator); - self = SWIG_NewPointerObj(this, swig_type, 0); // wrap but don't make Ruby own it } - return self; + return this->self_; } wxString m_valueCache; diff --git a/rakelib/lib/director/text_validator.rb b/rakelib/lib/director/text_validator.rb index 9c997d8b..4762f14e 100644 --- a/rakelib/lib/director/text_validator.rb +++ b/rakelib/lib/director/text_validator.rb @@ -45,7 +45,7 @@ class WXRubyTextValidator : public wxTextValidator, public wxRubyValidatorBindin virtual void SetWindow(wxWindow *win) override { this->wxTextValidator::SetWindow(win); - VALUE self = get_self(); + VALUE self = this->get_self(); // make sure Ruby does not own this validator instance anymore RDATA(self)->dfree = SWIG_RubyRemoveTracking; } @@ -80,20 +80,23 @@ class WXRubyTextValidator : public wxTextValidator, public wxRubyValidatorBindin private: static VALUE c_TextValidator; - VALUE get_self() + virtual VALUE get_self() override { - VALUE self = SWIG_RubyInstanceFor(this); - // if this is a C++ created clone (wxWidgets clones validators that are set) it's not tracked yet - if (NIL_P(self)) + if (NIL_P(this->self_)) { - if (NIL_P(c_TextValidator)) + this->self_ = SWIG_RubyInstanceFor(this); + // if this is a C++ created clone (wxWidgets clones validators that are set) it's not tracked yet + if (NIL_P(this->self_)) { - c_TextValidator = rb_const_get(mWxCore, rb_intern("TextValidator")); + if (NIL_P(c_TextValidator)) + { + c_TextValidator = rb_const_get(mWxCore, rb_intern("TextValidator")); + } + swig_type_info* swig_type = wxRuby_GetSwigTypeForClass(c_TextValidator); + this->self_ = SWIG_NewPointerObj(this, swig_type, 0); // wrap but don't make Ruby own it } - swig_type_info* swig_type = wxRuby_GetSwigTypeForClass(c_TextValidator); - self = SWIG_NewPointerObj(this, swig_type, 0); // wrap but don't make Ruby own it } - return self; + return this->self_; } wxString m_valueCache; diff --git a/rakelib/lib/director/validator.rb b/rakelib/lib/director/validator.rb index 43d383a7..c90e31fa 100644 --- a/rakelib/lib/director/validator.rb +++ b/rakelib/lib/director/validator.rb @@ -43,7 +43,7 @@ def setup wxObject* wxRubyValidator::Clone() const { bool ex_caught = false; - VALUE self = SWIG_RubyInstanceFor(const_cast (this)); + VALUE self = const_cast (this)->get_self(); VALUE rc = wxRuby_Funcall(ex_caught, self, clone_id(), 0); if (ex_caught) { @@ -61,7 +61,7 @@ def setup void wxRubyValidator::SetWindow(wxWindow *win) { this->wxValidator::SetWindow(win); - VALUE self = SWIG_RubyInstanceFor(this); + VALUE self = this->get_self(); // make sure Ruby does not own this validator instance anymore RDATA(self)->dfree = SWIG_RubyRemoveTracking; } @@ -78,29 +78,59 @@ def setup VALUE wxRubyValidator::DoTransferFromWindow() { bool ex_caught = false; - VALUE self = SWIG_RubyInstanceFor(this); - VALUE rc = wxRuby_Funcall(ex_caught, self, do_transfer_from_window_id(), 0); + VALUE rc = wxRuby_Funcall(ex_caught, this->get_self(), do_transfer_from_window_id(), 0); if (ex_caught) { - throw Swig::DirectorRubyException(rc, self, do_transfer_from_window_id()); + throw Swig::DirectorRubyException(rc, this->get_self(), do_transfer_from_window_id()); } return rc; } bool wxRubyValidator::DoTransferToWindow(VALUE data) { bool ex_caught = false; - VALUE self = SWIG_RubyInstanceFor(this); - VALUE rc = wxRuby_Funcall(ex_caught, self, do_transfer_to_window_id(), 1, data); + VALUE rc = wxRuby_Funcall(ex_caught, this->get_self(), do_transfer_to_window_id(), 1, data); if (ex_caught) { - throw Swig::DirectorRubyException(rc, self, do_transfer_to_window_id()); + throw Swig::DirectorRubyException(rc, this->get_self(), do_transfer_to_window_id()); } return (rc == Qtrue); } + VALUE wxRubyValidator::get_self() + { + if (NIL_P(this->self_)) + { + this->self_ = SWIG_RubyInstanceFor(this); + } + return this->self_; + } + + WxRuby_ID wxRubyValidatorBinding::do_on_transfer_from_window_id("do_on_transfer_from_window"); + WxRuby_ID wxRubyValidatorBinding::do_on_transfer_to_window_id("do_on_transfer_to_window"); WxRuby_ID wxRubyValidatorBinding::call_id("call"); bool wxRubyValidatorBinding::DoOnTransferFromWindow(VALUE data) + { + bool ex_caught = false; + VALUE rc = wxRuby_Funcall(ex_caught, this->get_self(), do_on_transfer_from_window_id(), 1, data); + if (ex_caught) + { + throw Swig::DirectorRubyException(rc, this->get_self(), do_on_transfer_from_window_id()); + } + return (rc == Qtrue); + } + VALUE wxRubyValidatorBinding::DoOnTransferToWindow() + { + bool ex_caught = false; + VALUE rc = wxRuby_Funcall(ex_caught, this->get_self(), do_on_transfer_to_window_id(), 0); + if (ex_caught) + { + throw Swig::DirectorRubyException(rc, this->get_self(), do_on_transfer_to_window_id()); + } + return rc; + } + + bool wxRubyValidatorBinding::OnTransferFromWindow(VALUE data) { if (!NIL_P(this->on_transfer_from_win_proc_)) { @@ -113,7 +143,7 @@ def setup } return true; } - VALUE wxRubyValidatorBinding::DoOnTransferToWindow() + VALUE wxRubyValidatorBinding::OnTransferToWindow() { if (!NIL_P(this->on_transfer_to_win_proc_)) { @@ -158,6 +188,14 @@ def setup { dynamic_cast($self)->SetOnTransferToWindow(proc); } + bool DoOnTransferFromWindow(VALUE data) + { + return dynamic_cast($self)->OnTransferFromWindow(data); + } + VALUE DoOnTransferToWindow() + { + return dynamic_cast($self)->OnTransferToWindow(); + } __HEREDOC # not provided in Ruby spec.ignore %w[wxValidator::TransferFromWindow wxValidator::TransferToWindow wxValidator::SetWindow] From 0818840e0d7a29230b4722f05c7ad33a7b928233 Mon Sep 17 00:00:00 2001 From: Martin Corino Date: Sat, 18 Nov 2023 17:36:01 +0100 Subject: [PATCH 14/21] add Wx::GenericValidator --- lib/wx/core/generic_validator.rb | 310 +++++++++++++++++++++++++++++++ lib/wx/doc/generic_validator.rb | 95 ++++++++++ 2 files changed, 405 insertions(+) create mode 100644 lib/wx/core/generic_validator.rb create mode 100644 lib/wx/doc/generic_validator.rb diff --git a/lib/wx/core/generic_validator.rb b/lib/wx/core/generic_validator.rb new file mode 100644 index 00000000..39ef3cb8 --- /dev/null +++ b/lib/wx/core/generic_validator.rb @@ -0,0 +1,310 @@ +# Copyright (c) 2023 M.J.N. Corino, The Netherlands +# +# This software is released under the MIT license. + +module Wx + + if has_feature?(:USE_VALIDATORS) + + class GenericValidator < Wx::Validator + + class << self + def handlers + @handlers ||= ::Hash.new(Proc.new { |win, *rest| raise NotImplementedError, "#{win.class} not supported" }) + end + + def define_handler(klass, meth=nil, &block) + raise ArgumentError, 'Expected a Class for arg 1' unless klass.is_a?(::Class) + h = if block and not meth + block + else + case meth + when Proc, Method + meth + else + raise ArgumentError, 'Expected a Proc or Method for arg 2' + end + end + handlers[klass] = h + end + end + + def initialize(*arg) + super + @value = arg.empty? ? nil : arg.first.value + @handler = nil + @klass = nil + end + + attr_accessor :value + + private + + def do_transfer_from_window + get_handler.call(get_window) + end + + def do_transfer_to_window(val) + get_handler.call(get_window, val) unless val.nil? + true + end + + def do_on_transfer_from_window(data) + super(@value = data) + end + + def do_on_transfer_to_window + @value = super || @value + end + + def get_handler + if @handler && @klass == get_window.class + @handler + else + @klass = get_window.class + @handler = GenericValidator.handlers[@klass] || GenericValidator.handlers.each_key.detect { |k| k > klass } + @handler || ::Kernel.raise(NotImplementedError, "#{@klass} not supported") + end + end + + end # GenericValidator + + # define standard handlers + + # boolean controls + if Wx.has_feature?(:USE_CHECKBOX) + + GenericValidator.define_handler(Wx::CheckBox) do |win, *val| + if val.empty? + !!win.get_value + else + win.set_value(!!val.shift) + end + end + + end + if Wx.has_feature?(:USE_RADIOBTN) + + GenericValidator.define_handler(Wx::RadioButton) do |win, *val| + if val.empty? + !!win.get_value + else + win.set_value(!!val.shift) + end + end + + end + if Wx.has_feature?(:USE_TOGGLEBTN) + # also covers Wx::BitmapToggleButton + GenericValidator.define_handler(Wx::ToggleButton) do |win, *val| + if val.empty? + !!win.get_value + else + win.set_value(!!val.shift) + end + end + + end + + # integer controls + if Wx.has_feature?(:USE_GAUGE) + + GenericValidator.define_handler(Wx::Gauge) do |win, *val| + if val.empty? + win.get_value + else + win.set_value(val.shift) + end + end + + end + if Wx.has_feature?(:USE_RADIOBOX) + + GenericValidator.define_handler(Wx::RadioBox) do |win, *val| + if val.empty? + win.get_selection + else + win.set_selection(val.shift) + end + end + + end + if Wx.has_feature?(:USE_SCROLLBAR) + + GenericValidator.define_handler(Wx::ScrollBar) do |win, *val| + if val.empty? + win.get_thumb_position + else + win.set_thumb_position(val.shift) + end + end + + end + if Wx.has_feature?(:USE_SPINCTRL) + + GenericValidator.define_handler(Wx::SpinCtrl) do |win, *val| + if val.empty? + win.get_value + else + win.set_value(val.shift) + end + end + + end + if Wx.has_feature?(:USE_SPINBTN) + + GenericValidator.define_handler(Wx::SpinButton) do |win, *val| + if val.empty? + win.get_value + else + win.set_value(val.shift) + end + end + + end + if Wx.has_feature?(:USE_SLIDER) + + GenericValidator.define_handler(Wx::Slider) do |win, *val| + if val.empty? + win.get_value + else + win.set_value(val.shift) + end + end + + end + if Wx.has_feature?(:USE_CHOICE) + + GenericValidator.define_handler(Wx::Choice) do |win, *val| + if val.empty? + win.get_selection + else + win.set_selection(val.shift) + end + end + + end + if Wx.has_feature?(:USE_COMBOBOX) + + GenericValidator.define_handler(Wx::ComboBox) do |win, *val| + if val.empty? + if (win.get_window_style & Wx::CB_READONLY) == Wx::CB_READONLY + win.get_selection + else + win.get_string_selection + end + else + if (win.get_window_style & Wx::CB_READONLY) == Wx::CB_READONLY + win.set_selection(val.shift) + else + win.set_string_selection(val.shift) + end + end + end + + end + + # date/time controls + if Wx.has_feature?(:USE_DATEPICKCTRL) + + GenericValidator.define_handler(Wx::DatePickerCtrl) do |win, *val| + if val.empty? + win.get_value + else + win.set_value(val.shift) + end + end + + end + if Wx.has_feature?(:USE_TIMEPICKCTRL) + + GenericValidator.define_handler(Wx::TimePickerCtrl) do |win, *val| + if val.empty? + win.get_value + else + win.set_value(val.shift) + end + end + + end + + # string controls + if Wx.has_feature?(:USE_BUTTON) + + GenericValidator.define_handler(Wx::Button) do |win, *val| + if val.empty? + win.get_label + else + win.set_label(val.shift) + end + end + + end + if Wx.has_feature?(:USE_STATTEXT) + + GenericValidator.define_handler(Wx::StaticText) do |win, *val| + if val.empty? + win.get_label + else + win.set_label(val.shift) + end + end + + end + if Wx.has_feature?(:USE_TEXTCTRL) + + GenericValidator.define_handler(Wx::TextCtrl) do |win, *val| + if val.empty? + win.get_value + else + win.set_value(val.shift) + end + end + + end + + # array controls + if Wx.has_feature?(:USE_CHECKLISTBOX) + + GenericValidator.define_handler(Wx::CheckListBox) do |win, *val| + if val.empty? + if (win.get_window_style & Wx::LB_SINGLE) == Wx::LB_SINGLE + win.get_checked_items.first + else + win.get_checked_items + end + else + if (win.get_window_style & Wx::LB_SINGLE) == Wx::LB_SINGLE + win.check(val.shift, true) + else + win.get_count.times { |i| win.check(i, false) } + [val].flatten.each { |i| win.check(i, true) } + end + end + end + + end + if Wx.has_feature?(:USE_LISTBOX) + + GenericValidator.define_handler(Wx::ListBox) do |win, *val| + if val.empty? + if (win.get_window_style & Wx::LB_SINGLE) == Wx::LB_SINGLE + win.get_selection + else + win.get_selections + end + else + if (win.get_window_style & Wx::LB_SINGLE) == Wx::LB_SINGLE + win.set_selection(val.shift) + else + win.get_count.times { |i| win.deselect(i) } + [val].flatten.each { |i| win.set_selection(i) } + end + end + end + + end + + end # if USE_VALIDATORS + +end diff --git a/lib/wx/doc/generic_validator.rb b/lib/wx/doc/generic_validator.rb new file mode 100644 index 00000000..09044126 --- /dev/null +++ b/lib/wx/doc/generic_validator.rb @@ -0,0 +1,95 @@ +# :stopdoc: +# Copyright (c) 2023 M.J.N. Corino, The Netherlands +# +# This software is released under the MIT license. +# :startdoc: + + +module Wx + + # Wx::GenericValidator performs data transfer (but not validation or filtering) + # for many type of controls. + # + # Wx::GenericValidator supports: + # - boolean type controls + # - Wx::CheckBox, Wx::RadioButton, Wx::ToggleButton, Wx::BitmapToggleButton + # - string type controls + # - Wx::Button, Wx::StaticText, Wx::TextCtrl + # - Wx::ComboBox in case it does not have style Wx::CB_READONLY + # - integer type controls + # - Wx::RadioBox, Wx::SpinButton, Wx::SpinCtrl, Wx::Gauge, Wx::Slider, Wx::ScrollBar, Wx::Choice + # - Wx::ComboBox in case it has style Wx::CB_READONLY + # - Wx::ListBox and Wx::CheckListBox in case of style Wx::LB_SINGLE + # - integer array type controls + # - Wx::ListBox and Wx::CheckListBox in case of style Wx::LB_MULTIPLE or Wx::LB_EXTENDED + # - date/time type controls + # - Wx::DatePickerCtrl, Wx::TimePickerCtrl + # + # It checks the type of the window and uses an appropriate type for it. + # For example, Wx::Button and Wx::TextCtrl transfer data to and from a + # String variable; Wx::ListBox uses an Array of Integer (in case of multiple + # selection list) or an Integer (in case of a single choice list); Wx::CheckBox + # uses a boolean. + # + # In wxRuby this is a pure Ruby implementation derived from Wx::Validator and + # **not** a wrapper for the C++ wxGenericValidator class although the functionality + # is virtually identical. + # + # @see Wx::Validator + # @see Wx::TextValidator + # @see Wx::IntegerValidator + # @see Wx::UnsignedValidator + # @see Wx::FloatValidator + # + # @wxrb_require USE_VALIDATORS + class GenericValidator < Wx::Validator + + # Returns a Hash of handlers for the various control types this class supports. + # User defined extension (or re-definition) of these handlers is possible. + # @see GenericValidator.define_handler + # @return [Hash] + def self.handlers; end + + # Defines a new handler for a control type (class). + # + # When called it should be supplied the control Class and either a Proc or Method + # instance or a block which should accept a single Wx::Window argument and an optional + # second argument. + # The handler will be called when transferring data from or to the associated window. + # The associated window is always passed as the first argument. + # In case of a transfer from the associated window that is the only argument and the + # handler should retrieve and return the data typical for the type of control window. + # In case of a transfer to the associated window the second argument will be the data + # to transfer to the associated control window. + # + # @example Definition of Wx::TextCtrl handler + # GenericValidator.define_handler(Wx::TextCtrl) do |win, *val| + # if val.empty? + # win.get_value + # else + # win.set_value(val.shift) + # end + # end + # + # @param [Class] klass control window class + # @param [Proc,Method,nil] meth + # @return [void] + def self.define_handler(klass, meth=nil, &block) end + + # @overload initialize + # Default constructor. + # @return [Wx::GenericValidator] + # @overload initialize(other) + # Copy constructor. + # @param [Wx::GenericValidator] other + # @return [Wx::GenericValidator] + def initialize(*arg)end + + # The value store attribute. Initially nil. When set the type should + # be appropriate for the associated control type. + # No forced conversions will be applied. + attr_accessor :value + + end + +end From cd620df0906a1f32f009d683b0ed5b8f4b2ae0dd Mon Sep 17 00:00:00 2001 From: Martin Corino Date: Sat, 18 Nov 2023 17:36:07 +0100 Subject: [PATCH 15/21] add Wx::GenericValidator tests --- tests/test_validators.rb | 203 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 203 insertions(+) diff --git a/tests/test_validators.rb b/tests/test_validators.rb index 4b665f55..54a09351 100644 --- a/tests/test_validators.rb +++ b/tests/test_validators.rb @@ -17,6 +17,12 @@ def cleanup super end + def teardown + @text.destroy if @text + @text = nil + super + end + attr_accessor :text class MyTextValidator < Wx::Validator @@ -78,6 +84,12 @@ def cleanup super end + def teardown + @text.destroy if @text + @text = nil + super + end + attr_accessor :text def test_basic @@ -268,6 +280,12 @@ def cleanup super end + def teardown + @text.destroy if @text + @text = nil + super + end + attr_accessor :text def test_transfer @@ -342,6 +360,12 @@ def cleanup super end + def teardown + @text.destroy if @text + @text = nil + super + end + attr_accessor :text def test_transfer @@ -419,6 +443,12 @@ def cleanup super end + def teardown + @text.destroy if @text + @text = nil + super + end + attr_accessor :text def test_transfer @@ -487,3 +517,176 @@ def test_transfer_range end end + +class GenericValidatorTests < WxRuby::Test::GUITests + + def setup + super + @control = nil + end + + def cleanup + @control.destroy if @control + @control = nil + super + end + + def teardown + @control.destroy if @control + @control = nil + super + end + + attr_accessor :control + + def test_text_ctrl + self.control = Wx::TextCtrl.new(frame_win, name: 'Text', validator: Wx::GenericValidator.new) + + assert_equal('', control.value) + assert_nil(control.validator.value) + + control.validator.value = 'Hello' + assert_true(control.transfer_data_to_window) + assert_equal('Hello', control.value) + + control.change_value('Bye') + assert_true(control.transfer_data_from_window) + assert_equal('Bye', control.validator.value) + end + + def test_boolean_ctrl + self.control = Wx::CheckBox.new(frame_win, name: 'Check', validator: Wx::GenericValidator.new) + + assert_false(control.checked?) + assert_nil(control.validator.value) + + control.validator.value = true + assert_true(control.transfer_data_to_window) + assert_true(control.checked?) + + control.set_value(false) + assert_true(control.transfer_data_from_window) + assert_false(control.validator.value) + end + + def test_integer_ctrl + self.control = Wx::Gauge.new(frame_win, range: 100, name: 'Gauge', validator: Wx::GenericValidator.new) + + assert_equal(0, control.value) + assert_nil(control.validator.value) + + control.validator.value = 10 + assert_true(control.transfer_data_to_window) + assert_equal(10, control.value) + + control.set_value(75) + assert_true(control.transfer_data_from_window) + assert_equal(75, control.validator.value) + end + + def test_integer_ctrl_binding + integer_store = 0 + self.control = Wx::Gauge.new(frame_win, range: 100, name: 'Gauge', validator: Wx::GenericValidator.new) + control.validator.on_transfer_to_window { integer_store } + control.validator.on_transfer_from_window { |data| integer_store = data } + + assert_equal(0, control.value) + assert_nil(control.validator.value) + + integer_store = 10 + assert_true(control.transfer_data_to_window) + assert_equal(10, control.value) + assert_equal(10, control.validator.value) + + control.set_value(75) + assert_true(control.transfer_data_from_window) + assert_equal(75, control.validator.value) + assert_equal(75, integer_store) + end + + def test_single_list_ctrl + self.control = Wx::ListBox.new(frame_win, choices: %w[First Second Third Fourth Fifth], name: 'List', validator: Wx::GenericValidator.new) + + assert_equal(0, control.selections.size) + assert_nil(control.validator.value) + + control.validator.value = 1 + assert_true(control.transfer_data_to_window) + assert_equal(1, control.selection) + + control.deselect(1) + control.set_selection(3) + assert_true(control.transfer_data_from_window) + assert_equal(3, control.validator.value) + end + + def test_array_ctrl + self.control = Wx::ListBox.new(frame_win, choices: %w[First Second Third Fourth Fifth], style: Wx::LB_MULTIPLE, name: 'List', validator: Wx::GenericValidator.new) + + assert_equal(0, control.selections.size) + assert_nil(control.validator.value) + + control.validator.value = [1, 3] + assert_true(control.transfer_data_to_window) + assert_equal([1,3], control.selections) + + control.count.times { |i| control.deselect(i) } + [0, 2, 4].each { |i| control.set_selection(i) } + assert_true(control.transfer_data_from_window) + assert_equal([0, 2, 4], control.validator.value) + end + + class Model + + def initialize + @value = nil + end + + attr_accessor :value + + def load + @value + end + + def store(v) + @value = v + end + + end + + class GenericModelValidator < Wx::GenericValidator + + def initialize(arg) + if arg.is_a?(self.class) + super + @model = arg.model + else + super() + @model = arg + on_transfer_to_window { @model.load } + on_transfer_from_window { |data| @model.store(data) } + end + end + + attr_reader :model + + end + + def test_custom_array_ctrl_validate + model = Model.new + self.control = Wx::ListBox.new(frame_win, choices: %w[First Second Third Fourth Fifth], style: Wx::LB_MULTIPLE, name: 'List', validator: GenericModelValidator.new(model)) + + assert_equal(0, control.selections.size) + assert_nil(model.value) + + model.value = [1, 3] + assert_true(control.transfer_data_to_window) + assert_equal([1,3], control.selections) + + control.count.times { |i| control.deselect(i) } + [0, 2, 4].each { |i| control.set_selection(i) } + assert_true(control.transfer_data_from_window) + assert_equal([0, 2, 4], model.value) + end + +end From 06a9603ee42be24837e62c0ebe1a521690f8e6ec Mon Sep 17 00:00:00 2001 From: Martin Corino Date: Sun, 19 Nov 2023 13:11:40 +0100 Subject: [PATCH 16/21] protect against marking non-customized validators --- rakelib/lib/director/num_validator.rb | 24 ++++++++++++++++--- .../director/numeric_property_validator.rb | 8 ++++++- rakelib/lib/director/text_validator.rb | 8 ++++++- rakelib/lib/director/validator.rb | 8 ++++++- 4 files changed, 42 insertions(+), 6 deletions(-) diff --git a/rakelib/lib/director/num_validator.rb b/rakelib/lib/director/num_validator.rb index e6bd1474..5a1961a1 100644 --- a/rakelib/lib/director/num_validator.rb +++ b/rakelib/lib/director/num_validator.rb @@ -129,7 +129,13 @@ class WXIntegerValidator : public wxIntegerValidator, public w WXRUBY_EXPORT void GC_mark_wxIntegerValidator(void* ptr) { if (ptr) - reinterpret_cast (ptr)->GC_Mark(); + { + wxValidator* vp = reinterpret_cast (ptr); + WXIntegerValidator* rbvp = dynamic_cast (vp); + // This might be a pointer to a non-customized validator (or clone thereof) created internally + // by wxWidgets C++ code + if (rbvp) rbvp->GC_Mark(); + } } class WXUnsignedValidator : public wxIntegerValidator, public wxRubyValidatorBinding @@ -225,7 +231,13 @@ class WXUnsignedValidator : public wxIntegerValidator, public WXRUBY_EXPORT void GC_mark_wxUnsignedValidator(void* ptr) { if (ptr) - reinterpret_cast (ptr)->GC_Mark(); + { + wxValidator* vp = reinterpret_cast (ptr); + WXUnsignedValidator* rbvp = dynamic_cast (vp); + // This might be a pointer to a non-customized validator (or clone thereof) created internally + // by wxWidgets C++ code + if (rbvp) rbvp->GC_Mark(); + } } class WXFloatValidator : public wxFloatingPointValidator, public wxRubyValidatorBinding @@ -319,7 +331,13 @@ class WXFloatValidator : public wxFloatingPointValidator, public wxRubyV WXRUBY_EXPORT void GC_mark_wxFloatValidator(void* ptr) { if (ptr) - reinterpret_cast (ptr)->GC_Mark(); + { + wxValidator* vp = reinterpret_cast (ptr); + WXFloatValidator* rbvp = dynamic_cast (vp); + // This might be a pointer to a non-customized validator (or clone thereof) created internally + // by wxWidgets C++ code + if (rbvp) rbvp->GC_Mark(); + } } __HEREDOC spec.add_swig_code 'GC_MANAGE_AS_OBJECT(WXIntegerValidator);', diff --git a/rakelib/lib/director/numeric_property_validator.rb b/rakelib/lib/director/numeric_property_validator.rb index 04443f8f..492ba353 100644 --- a/rakelib/lib/director/numeric_property_validator.rb +++ b/rakelib/lib/director/numeric_property_validator.rb @@ -109,7 +109,13 @@ class WXRubyNumericPropertyValidator : public wxNumericPropertyValidator, public WXRUBY_EXPORT void GC_mark_wxNumericPropertyValidator(void* ptr) { if (ptr) - reinterpret_cast (ptr)->GC_Mark(); + { + wxValidator* vp = reinterpret_cast (ptr); + WXRubyNumericPropertyValidator* rbvp = dynamic_cast (vp); + // This might be a pointer to a non-customized validator (or clone thereof) created internally + // by wxWidgets C++ code + if (rbvp) rbvp->GC_Mark(); + } } __HEREDOC spec.add_swig_code '%markfunc wxValidator "GC_mark_wxValidator";' diff --git a/rakelib/lib/director/text_validator.rb b/rakelib/lib/director/text_validator.rb index 4762f14e..df29fd95 100644 --- a/rakelib/lib/director/text_validator.rb +++ b/rakelib/lib/director/text_validator.rb @@ -107,7 +107,13 @@ class WXRubyTextValidator : public wxTextValidator, public wxRubyValidatorBindin WXRUBY_EXPORT void GC_mark_wxTextValidator(void* ptr) { if (ptr) - reinterpret_cast (ptr)->GC_Mark(); + { + wxValidator* vp = reinterpret_cast (ptr); + WXRubyTextValidator* rbvp = dynamic_cast (vp); + // This might be a pointer to a non-customized validator (or clone thereof) created internally + // by wxWidgets C++ code + if (rbvp) rbvp->GC_Mark(); + } } __HEREDOC diff --git a/rakelib/lib/director/validator.rb b/rakelib/lib/director/validator.rb index c90e31fa..84bd7ff4 100644 --- a/rakelib/lib/director/validator.rb +++ b/rakelib/lib/director/validator.rb @@ -170,7 +170,13 @@ def setup static void GC_mark_wxValidator(void* ptr) { if (ptr) - reinterpret_cast (ptr)->GC_Mark(); + { + wxValidator* vp = reinterpret_cast (ptr); + wxRubyValidator* rbvp = dynamic_cast (vp); + // This might be a pointer to the global constant wxDefaultValidator or one of it's clones + // which are not wxRubyValidator-s + if (rbvp) rbvp->GC_Mark(); + } } __HEREDOC spec.add_swig_code '%markfunc wxValidator "GC_mark_wxValidator";' From 8f1a731fd9899dee9c34d881a995f22f2a6e796a Mon Sep 17 00:00:00 2001 From: Martin Corino Date: Sun, 19 Nov 2023 14:15:00 +0100 Subject: [PATCH 17/21] fix WXOSX ListBox peculiarity --- lib/wx/core/generic_validator.rb | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/lib/wx/core/generic_validator.rb b/lib/wx/core/generic_validator.rb index 39ef3cb8..6ec41c0a 100644 --- a/lib/wx/core/generic_validator.rb +++ b/lib/wx/core/generic_validator.rb @@ -288,13 +288,15 @@ def get_handler GenericValidator.define_handler(Wx::ListBox) do |win, *val| if val.empty? - if (win.get_window_style & Wx::LB_SINGLE) == Wx::LB_SINGLE + # test this the hard since on WXOSX LB_SINGLE is default but does get set + # by default i.e. on WXOSX no selection type bit set == LB_SINGLE + if (win.get_window_style & (Wx::LB_MULTIPLE|Wx::LB_EXTENDED)) == 0 win.get_selection else win.get_selections end else - if (win.get_window_style & Wx::LB_SINGLE) == Wx::LB_SINGLE + if (win.get_window_style & (Wx::LB_MULTIPLE|Wx::LB_EXTENDED)) == 0 win.set_selection(val.shift) else win.get_count.times { |i| win.deselect(i) } From e0ba998ba0ed7fe83e965d453d134be8322a3440 Mon Sep 17 00:00:00 2001 From: Martin Corino Date: Sun, 19 Nov 2023 14:25:07 +0100 Subject: [PATCH 18/21] support new method for >= 3.3.0; remove unnecessary proxy --- rakelib/lib/director/tree_ctrl.rb | 35 +++++++++++++++++++++++++++++++ 1 file changed, 35 insertions(+) diff --git a/rakelib/lib/director/tree_ctrl.rb b/rakelib/lib/director/tree_ctrl.rb index d1800aba..c51ff6c5 100644 --- a/rakelib/lib/director/tree_ctrl.rb +++ b/rakelib/lib/director/tree_ctrl.rb @@ -50,6 +50,41 @@ class WxRubyTreeCtrl : public wxTreeCtrl wxTreeCtrl::GetFirstChild wxTreeCtrl::GetNextChild ] + if Config.instance.wx_version >= '3.3.0' + # new method with bitmap bundle vector + spec.no_proxy 'wxTreeCtrl::SetStateImages' + spec.map 'const wxVector& images' => 'Array' do + map_in temp: 'wxVector tmp', code: <<~__CODE + if ($input != Qnil) + { + if (TYPE($input) == T_ARRAY) + { + for (int i=0; i(ptr)); + } + } + else + { + VALUE msg = rb_inspect($input); + rb_raise(rb_eArgError, "Expected Array of Wx::ImageBundle for $argnum but got %s", StringValuePtr(msg)); + } + } + $1 = &tmp; + __CODE + end + end + # don't see how supporting overloading this one in Ruby is helpful + # (also seeing SetButtonsImageList isn't virtual at all) + spec.no_proxy 'wxTreeCtrl::SetStateImageList' # these are potentially involved in GC mark phase so # we can't have them redirecting to Ruby calls spec.no_proxy %w[ From d466f64dcdf92ec39a36d273a3232569af4f5b2c Mon Sep 17 00:00:00 2001 From: Martin Corino Date: Sun, 19 Nov 2023 14:38:09 +0100 Subject: [PATCH 19/21] make test more resilient to local differences --- tests/test_validators.rb | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/tests/test_validators.rb b/tests/test_validators.rb index 54a09351..9db52ec6 100644 --- a/tests/test_validators.rb +++ b/tests/test_validators.rb @@ -452,7 +452,6 @@ def teardown attr_accessor :text def test_transfer - dpt = Wx::Locale.get_info(Wx::LocaleInfo::LOCALE_DECIMAL_POINT) data = 0.0 valFlt = Wx::FloatValidator.new(3, Wx::NumValidatorStyle::NUM_VAL_DEFAULT) valFlt.on_transfer_to_window { data } @@ -460,7 +459,7 @@ def test_transfer text.validator = valFlt assert_true(text.transfer_data_to_window) - assert_equal("0#{dpt}000", text.value) + assert_match(/\A0(\.|,)000\Z/, text.value) text.validator.style = Wx::NumValidatorStyle::NUM_VAL_NO_TRAILING_ZEROES @@ -469,11 +468,11 @@ def test_transfer data = 17.123 assert_true(text.transfer_data_to_window) - assert_equal("17#{dpt}123", text.value) + assert_match(/\A17(\.|,)123\Z/, text.value) data = 17.1236 assert_true(text.transfer_data_to_window) - assert_equal("17#{dpt}124", text.value) + assert_match(/\A17(\.|,)124\Z/, text.value) text.change_value("foobar") assert_false(text.transfer_data_from_window) @@ -487,7 +486,6 @@ def test_transfer end def test_transfer_range - dpt = Wx::Locale.get_info(Wx::LocaleInfo::LOCALE_DECIMAL_POINT) data = 0 valFlt = Wx::FloatValidator.new(3, Wx::NumValidatorStyle::NUM_VAL_NO_TRAILING_ZEROES) valFlt.set_range(-0.500, 0.500) @@ -500,7 +498,7 @@ def test_transfer_range data = 0.123 assert_true(text.transfer_data_to_window) - assert_equal("0#{dpt}123", text.value) + assert_match(/\A0(\.|,)123\Z/, text.value) text.change_value('-0.734') assert_false(text.transfer_data_from_window) From 3746c742099d017f8783bcf30e6c37f08832703f Mon Sep 17 00:00:00 2001 From: Martin Corino Date: Sun, 19 Nov 2023 16:28:03 +0100 Subject: [PATCH 20/21] fix incorrect export specs --- rakelib/lib/director/num_validator.rb | 6 +++--- rakelib/lib/director/numeric_property_validator.rb | 2 +- rakelib/lib/director/text_validator.rb | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/rakelib/lib/director/num_validator.rb b/rakelib/lib/director/num_validator.rb index 5a1961a1..a75e3c9f 100644 --- a/rakelib/lib/director/num_validator.rb +++ b/rakelib/lib/director/num_validator.rb @@ -126,7 +126,7 @@ class WXIntegerValidator : public wxIntegerValidator, public w VALUE WXIntegerValidator::c_IntegerValidator = Qnil; - WXRUBY_EXPORT void GC_mark_wxIntegerValidator(void* ptr) + static void GC_mark_wxIntegerValidator(void* ptr) { if (ptr) { @@ -228,7 +228,7 @@ class WXUnsignedValidator : public wxIntegerValidator, public VALUE WXUnsignedValidator::c_UnsignedValidator = Qnil; - WXRUBY_EXPORT void GC_mark_wxUnsignedValidator(void* ptr) + static void GC_mark_wxUnsignedValidator(void* ptr) { if (ptr) { @@ -328,7 +328,7 @@ class WXFloatValidator : public wxFloatingPointValidator, public wxRubyV VALUE WXFloatValidator::c_FloatValidator = Qnil; - WXRUBY_EXPORT void GC_mark_wxFloatValidator(void* ptr) + static void GC_mark_wxFloatValidator(void* ptr) { if (ptr) { diff --git a/rakelib/lib/director/numeric_property_validator.rb b/rakelib/lib/director/numeric_property_validator.rb index 492ba353..3c87b1b3 100644 --- a/rakelib/lib/director/numeric_property_validator.rb +++ b/rakelib/lib/director/numeric_property_validator.rb @@ -106,7 +106,7 @@ class WXRubyNumericPropertyValidator : public wxNumericPropertyValidator, public VALUE WXRubyNumericPropertyValidator::c_NumericPropertyValidator = Qnil; - WXRUBY_EXPORT void GC_mark_wxNumericPropertyValidator(void* ptr) + static void GC_mark_wxNumericPropertyValidator(void* ptr) { if (ptr) { diff --git a/rakelib/lib/director/text_validator.rb b/rakelib/lib/director/text_validator.rb index df29fd95..494f43da 100644 --- a/rakelib/lib/director/text_validator.rb +++ b/rakelib/lib/director/text_validator.rb @@ -104,7 +104,7 @@ class WXRubyTextValidator : public wxTextValidator, public wxRubyValidatorBindin VALUE WXRubyTextValidator::c_TextValidator = Qnil; - WXRUBY_EXPORT void GC_mark_wxTextValidator(void* ptr) + static void GC_mark_wxTextValidator(void* ptr) { if (ptr) { From abb40a19644080f91b73ec736e4fa52159e289e1 Mon Sep 17 00:00:00 2001 From: Martin Corino Date: Sun, 19 Nov 2023 18:32:07 +0100 Subject: [PATCH 21/21] use option file for linking on windows --- rakelib/lib/config/mingw.rb | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/rakelib/lib/config/mingw.rb b/rakelib/lib/config/mingw.rb index dc1ae870..ba0e496f 100644 --- a/rakelib/lib/config/mingw.rb +++ b/rakelib/lib/config/mingw.rb @@ -53,6 +53,17 @@ def dll_mask "{#{dll_ext},dll}" end + def do_link(pkg) + # have to use option file for objects to link on windows because command line gets too long + ftmp = Tempfile.new('object') + ftmp.puts pkg.all_obj_files.collect { |o| File.join('..', o) }.join(' ') + ftmp.close # close but do not unlink + objs = "@#{ftmp.path}" + depsh = pkg.dep_libnames.collect { |dl| "#{dl}.#{dll_ext}" }.join(' ') + sh "cd lib && #{WXRuby3.config.ld} #{WXRuby3.config.ldflags(pkg.lib_target)} #{objs} #{depsh} " + + "#{WXRuby3.config.libs} #{WXRuby3.config.link_output_flag}#{pkg.lib_target}" + end + private def wx_make