From 061d659bb1f48478321ab9535cb6863bb90c0e1a Mon Sep 17 00:00:00 2001 From: John Beard Date: Tue, 14 Feb 2017 18:03:08 +0800 Subject: [PATCH] Add a class to govern an "incrementing" text control This class, INCREMENTAL_TEXT_CTRL, is used to provide a frameworks for classes that want to control a text control that can be incremented according to some scheme. Also provided is a wxWidgets implementation with spin buttons for control, as well as mousewheel support. --- common/CMakeLists.txt | 1 + common/incremental_text_ctrl.cpp | 184 +++++++++++++++++++++ include/incremental_text_ctrl.h | 188 ++++++++++++++++++++++ pcbnew/dialogs/dialog_display_options.cpp | 1 + pcbnew/dialogs/dialog_display_options.h | 2 +- 5 files changed, 375 insertions(+), 1 deletion(-) create mode 100644 common/incremental_text_ctrl.cpp create mode 100644 include/incremental_text_ctrl.h diff --git a/common/CMakeLists.txt b/common/CMakeLists.txt index d686fb39af..4fce3a5d55 100644 --- a/common/CMakeLists.txt +++ b/common/CMakeLists.txt @@ -230,6 +230,7 @@ set( COMMON_SRCS gr_basic.cpp hotkeys_basic.cpp html_messagebox.cpp + incremental_text_ctrl.cpp kiface_i.cpp kiway.cpp kiway_express.cpp diff --git a/common/incremental_text_ctrl.cpp b/common/incremental_text_ctrl.cpp new file mode 100644 index 0000000000..270c2dd987 --- /dev/null +++ b/common/incremental_text_ctrl.cpp @@ -0,0 +1,184 @@ +/* + * This program source code file is part of KiCad, a free EDA CAD application. + * + * Copyright (C) 2015 Jean-Pierre Charras, jean-pierre.charras at wanadoo.fr + * Copyright (C) 1992-2016 KiCad Developers, see AUTHORS.txt for contributors. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, you may find one here: + * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html + * or you may search the http://www.gnu.org website for the version 2 license, + * or you may write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA + */ + +#include + +/** + * Check that a string looks like a floating point number that can + * be dealt with. + */ +static bool validateFloatField( const wxString& aStr ) +{ + // Skip empty fields + if( aStr.size() == 0 ) + return false; + + // a single . or , doesn't count as number, although valid in a float + if( aStr.size() == 1 ) + { + if( (aStr.compare( "." ) == 0) || + (aStr.compare( "," ) == 0) ) + return false; + } + + return true; +} + + +INCREMENTAL_TEXT_CTRL::INCREMENTAL_TEXT_CTRL() : + m_minVal( 0.0 ), + m_maxVal( 1.0 ), + m_currentValue( 0.0 ), + m_precision( 4 ) +{} + + +void INCREMENTAL_TEXT_CTRL::SetStep( double aMin, double aMax, + STEP_FUNCTION aStepFunc ) +{ + wxASSERT( aMin <= aMax ); + + m_minVal = std::min( aMin, aMax ); + m_maxVal = std::max( aMin, aMax ); + m_stepFunc = aStepFunc; + + // finally, clamp the current value and re-display + updateTextValue(); +} + + +void INCREMENTAL_TEXT_CTRL::updateTextValue() +{ + if( m_currentValue > m_maxVal ) + m_currentValue = m_maxVal; + + if( m_currentValue < m_minVal ) + m_currentValue = m_minVal; + + wxString fmt = wxString::Format( "%%.%df", m_precision ); + setTextCtrl( wxString::Format( fmt, m_currentValue ) ); +} + + +void INCREMENTAL_TEXT_CTRL::incrementCtrlBy( double aInc ) +{ + const wxString txt = getCtrlText(); + if( !validateFloatField( txt ) ) + return; + + txt.ToDouble( &m_currentValue ); + m_currentValue += aInc; + + updateTextValue(); +} + + +void INCREMENTAL_TEXT_CTRL::incrementCtrl( bool aUp ) +{ + incrementCtrlBy( m_stepFunc( aUp, m_currentValue ) ); +} + + +void INCREMENTAL_TEXT_CTRL::SetPrecision( int aPrecision ) +{ + m_precision = aPrecision; +} + + +void INCREMENTAL_TEXT_CTRL::SetValue( double aValue ) +{ + m_currentValue = aValue; + updateTextValue(); +} + + +double INCREMENTAL_TEXT_CTRL::GetValue() +{ + // sanitise before handing the value - if the user did something + // like close a window with outstanding text changes, we need + // to clamp the value and re-interpret the text + incrementCtrlBy( 0.0 ); + + return m_currentValue; +} + + +SPIN_INCREMENTAL_TEXT_CTRL::SPIN_INCREMENTAL_TEXT_CTRL( wxSpinButton& aSpinBtn, + wxTextCtrl& aTextCtrl ): + m_spinBtn( aSpinBtn ), + m_textCtrl( aTextCtrl ) +{ + // set always enabled, otherwise it's very hard to keep in sync + aSpinBtn.SetRange( -INT_MAX, INT_MAX ); + + auto spinUpHandler = [this] ( wxSpinEvent& event ) + { + incrementCtrl( true ); + }; + + // spin up/down if a single step of the field + auto spinDownHandler = [this] ( wxSpinEvent& event ) + { + incrementCtrl( false ); + }; + + auto mouseWheelHandler = [this] ( wxMouseEvent& aEvent ) + { + incrementCtrl( aEvent.GetWheelRotation() >= 0 ); + }; + + aSpinBtn.Bind( wxEVT_SPIN_UP, spinUpHandler ); + aSpinBtn.Bind( wxEVT_SPIN_DOWN, spinDownHandler ); + + m_textCtrl.Bind( wxEVT_MOUSEWHEEL, mouseWheelHandler ); + + m_textCtrl.Bind( wxEVT_KILL_FOCUS, &SPIN_INCREMENTAL_TEXT_CTRL::onFocusLoss, this ); +} + +SPIN_INCREMENTAL_TEXT_CTRL::~SPIN_INCREMENTAL_TEXT_CTRL() +{ + // this must be unbound, as kill focus can arrive after the + // text control is gone + m_textCtrl.Unbind( wxEVT_KILL_FOCUS, &SPIN_INCREMENTAL_TEXT_CTRL::onFocusLoss, this ); +} + + +void SPIN_INCREMENTAL_TEXT_CTRL::onFocusLoss( wxFocusEvent& aEvent ) +{ + // re-read the input and sanitize any user changes + incrementCtrlBy( 0.0 ); +} + + +void SPIN_INCREMENTAL_TEXT_CTRL::setTextCtrl( const wxString& val ) +{ + m_textCtrl.SetValue( val ); +} + + +wxString SPIN_INCREMENTAL_TEXT_CTRL::getCtrlText() const +{ + return m_textCtrl.GetValue(); +} + diff --git a/include/incremental_text_ctrl.h b/include/incremental_text_ctrl.h new file mode 100644 index 0000000000..994839c578 --- /dev/null +++ b/include/incremental_text_ctrl.h @@ -0,0 +1,188 @@ +/* + * This program source code file is part of KiCad, a free EDA CAD application. + * + * Copyright (C) 2013 CERN + * @author Maciej Suminski + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, you may find one here: + * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html + * or you may search the http://www.gnu.org website for the version 2 license, + * or you may write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA + */ + +#ifndef INCREMENTAL_TEXT_CTRL__H_ +#define INCREMENTAL_TEXT_CTRL__H_ + +#include +#include + +#include + +/** + * Class that governs a textual control holding a number that can + * be incremented/decremented according to some scheme (often just + * a constant step). + */ +class INCREMENTAL_TEXT_CTRL +{ +public: + + /** + * A callable object type that can be used to provide a step + * value. Client can provide one of these to use for implementing + * non-linear stepping, or stepping based on external parameters, + * such as unit selection. + * + * @param aUp true if the next step is upwards + * @param aCurrVal the current value of the control + */ + using STEP_FUNCTION = std::function; + + INCREMENTAL_TEXT_CTRL(); + + virtual ~INCREMENTAL_TEXT_CTRL() {} + + /** + * Set the value of the text control, but obey the limits + * currently set. + * + * @param aValue the control value to set + */ + void SetValue( double aValue ); + + /** + * Get the current value of the control + */ + double GetValue(); + + /** + * Function SetStep() + * + * Set the stepping parameters of the control. The range is + * enforced by not allowing the scroll to exceed it, and on + * loss of focus, the control is also clamped to the range. + * + * @param aMin the minium value allowed + * @param aMax the maximum value allows + * @param aNewfunc the step function used to calculate the next step + */ + void SetStep( double aMin, double aMax, STEP_FUNCTION aNewFunc ); + + /** + * Function SetStep() + * + * Shortcut method to set step parameters when the step is constant + * + * @param aMin the minium value allowed + * @param aMax the maximum value allows + * @param aStep the constant step size + */ + void SetStep( double aMin, double aMax, double aStep ) + { + SetStep( aMin, aMax, + [aStep] ( bool aUp, double aCurrent ) { return aUp ? aStep : -aStep; } ); + } + + /** + * Set the number of decimal places to display + */ + void SetPrecision( int aPrecision ); + +protected: + + /** + * Increment the control by the given amount + */ + void incrementCtrlBy( double aInc); + + /** + * Single increment up or down by one step + */ + void incrementCtrl( bool aUp ); + + /** + * Update the text control value with the current value, + * clamping to limits as needed + */ + void updateTextValue(); + + /* + * Implementation-specific interfaces + */ + + /** + * Set the text control string value after an increment. + */ + virtual void setTextCtrl( const wxString& aVal ) = 0; + + /** + * @return the current string value of the text control + */ + virtual wxString getCtrlText() const = 0; + +private: + + double m_minVal; + double m_maxVal; + + ///< Current value of the control + double m_currentValue; + + ///< Precision to display + int m_precision; + + ///< The function used to determine the step + STEP_FUNCTION m_stepFunc; +}; + + +/** + * Class SPIN_INCREMENTING_TEXT_CTRL + * + * An incrementable text control, with WX spin buttons for clickable + * control. + */ +class SPIN_INCREMENTAL_TEXT_CTRL: public INCREMENTAL_TEXT_CTRL +{ +public: + + /** + * Constructor + * + * @param aSpinBtn the spin button to control the value + * @param aTextCtrl the text control that will display the value + */ + SPIN_INCREMENTAL_TEXT_CTRL( wxSpinButton& aSpinBtn, + wxTextCtrl& aTextCtrl ); + + ~SPIN_INCREMENTAL_TEXT_CTRL(); + +protected: + + ///> @copydoc INCREMENTAL_TEXT_CTRL::setTextCtrl() + void setTextCtrl( const wxString& val ) override; + + ///> @copydoc INCREMENTAL_TEXT_CTRL::getCtrlText() + wxString getCtrlText() const override; + +private: + + void onFocusLoss( wxFocusEvent& aEvent ); + + wxSpinButton& m_spinBtn; + wxTextCtrl& m_textCtrl; +}; + +#endif /* INCREMENTAL_TEXT_CTRL__H_ */ diff --git a/pcbnew/dialogs/dialog_display_options.cpp b/pcbnew/dialogs/dialog_display_options.cpp index 89cf6ee068..74cc93caa1 100644 --- a/pcbnew/dialogs/dialog_display_options.cpp +++ b/pcbnew/dialogs/dialog_display_options.cpp @@ -205,6 +205,7 @@ DIALOG_DISPLAY_OPTIONS::DIALOG_DISPLAY_OPTIONS( PCB_EDIT_FRAME* parent ) : FinishDialogSettings(); } + void DIALOG_DISPLAY_OPTIONS::init() { SetFocus(); diff --git a/pcbnew/dialogs/dialog_display_options.h b/pcbnew/dialogs/dialog_display_options.h index 108e7180ad..f02c889b78 100644 --- a/pcbnew/dialogs/dialog_display_options.h +++ b/pcbnew/dialogs/dialog_display_options.h @@ -36,7 +36,7 @@ private: public: DIALOG_DISPLAY_OPTIONS( PCB_EDIT_FRAME* parent ); - ~DIALOG_DISPLAY_OPTIONS( ) { }; + ~DIALOG_DISPLAY_OPTIONS() {}; void OnOkClick( wxCommandEvent& event ) override; void OnCancelClick( wxCommandEvent& event ) override; };