Say hello to QskTextEdit

Resolves #109
This commit is contained in:
Peter Hartmann 2024-12-06 16:50:36 +01:00
parent 4e8c3e665c
commit 1af5648965
21 changed files with 1589 additions and 21 deletions

View File

@ -8,6 +8,7 @@ list(APPEND HEADERS
)
list(APPEND PRIVATE_HEADERS
QskFluent2TextAreaSkinlet.h
QskFluent2TextFieldSkinlet.h
)
@ -15,6 +16,7 @@ list(APPEND SOURCES
QskFluent2Theme.cpp
QskFluent2Skin.cpp
QskFluent2SkinFactory.cpp
QskFluent2TextAreaSkinlet.cpp
QskFluent2TextFieldSkinlet.cpp
)

View File

@ -47,6 +47,7 @@
*/
#include "QskFluent2Skin.h"
#include "QskFluent2Theme.h"
#include "QskFluent2TextAreaSkinlet.h"
#include "QskFluent2TextFieldSkinlet.h"
#include <QskSkinHintTableEditor.h>
@ -80,6 +81,7 @@
#include <QskTabButton.h>
#include <QskTabView.h>
#include <QskTextField.h>
#include <QskTextArea.h>
#include <QskTextLabel.h>
#include <QskVirtualKeyboard.h>
@ -297,6 +299,15 @@ namespace
void setupTabViewMetrics();
void setupTabViewColors( QskAspect::Section, const QskFluent2Theme& );
template< typename Q >
void setupTextControlMetrics();
template< typename Q, typename SK >
void setupTextControlColors( QskAspect::Section section, const QskFluent2Theme& theme );
void setupTextAreaMetrics();
void setupTextAreaColors( QskAspect::Section, const QskFluent2Theme& );
void setupTextFieldMetrics();
void setupTextFieldColors( QskAspect::Section, const QskFluent2Theme& );
@ -356,6 +367,7 @@ void Editor::setupMetrics()
setupTabButtonMetrics();
setupTabBarMetrics();
setupTabViewMetrics();
setupTextAreaMetrics();
setupTextFieldMetrics();
setupTextLabelMetrics();
setupVirtualKeyboardMetrics();
@ -395,6 +407,7 @@ void Editor::setupColors( QskAspect::Section section, const QskFluent2Theme& the
setupTabButtonColors( section, theme );
setupTabBarColors( section, theme );
setupTabViewColors( section, theme );
setupTextAreaColors( section, theme );
setupTextFieldColors( section, theme );
setupTextLabelColors( section, theme );
setupVirtualKeyboardColors( section, theme );
@ -1781,10 +1794,8 @@ void Editor::setupTextLabelColors(
setColor( Q::Text | section, pal.fillColor.text.primary );
}
void Editor::setupTextFieldMetrics()
template< typename Q > void Editor::setupTextControlMetrics()
{
using Q = QskTextField;
setStrutSize( Q::TextPanel, { -1, 30_px } );
setPadding( Q::TextPanel, { 11_px, 0, 11_px, 0 } );
@ -1794,18 +1805,15 @@ void Editor::setupTextFieldMetrics()
setBoxShape( Q::TextPanel, 3_px );
setAlignment( Q::Text, Qt::AlignLeft | Qt::AlignVCenter );
setFontRole( Q::Text, Fluent2::Body );
setAlignment( Q::PlaceholderText, alignment( Q::Text ) );
setFontRole( Q::PlaceholderText, fontRole( Q::Text ) );
}
void Editor::setupTextFieldColors(
QskAspect::Section section, const QskFluent2Theme& theme )
template< typename Q, typename SK > void Editor::setupTextControlColors(
QskAspect::Section section, const QskFluent2Theme& theme)
{
using Q = QskTextField;
using SK = QskTextFieldSkinlet;
using A = QskAspect;
const auto& pal = theme.palette;
@ -1858,6 +1866,36 @@ void Editor::setupTextFieldColors(
}
}
void Editor::setupTextAreaMetrics()
{
using Q = QskTextArea;
setAlignment( Q::Text, Qt::AlignLeft | Qt::AlignTop );
setupTextControlMetrics< Q >();
}
void Editor::setupTextAreaColors(
QskAspect::Section section, const QskFluent2Theme& theme )
{
setupTextControlColors< QskTextArea, QskTextAreaSkinlet >( section, theme );
}
void Editor::setupTextFieldMetrics()
{
using Q = QskTextField;
setAlignment( Q::Text, Qt::AlignLeft | Qt::AlignVCenter );
setupTextControlMetrics< Q >();
}
void Editor::setupTextFieldColors(
QskAspect::Section section, const QskFluent2Theme& theme )
{
setupTextControlColors< QskTextField, QskTextFieldSkinlet >( section, theme );
}
void Editor::setupSwitchButtonMetrics()
{
using Q = QskSwitchButton;
@ -2042,6 +2080,7 @@ void Editor::setupVirtualKeyboardColors(
QskFluent2Skin::QskFluent2Skin( QObject* parent )
: Inherited( parent )
{
declareSkinlet< QskTextArea, QskFluent2TextAreaSkinlet >();
declareSkinlet< QskTextField, QskFluent2TextFieldSkinlet >();
setupFonts();

View File

@ -0,0 +1,32 @@
/******************************************************************************
* QSkinny - Copyright (C) The authors
* SPDX-License-Identifier: BSD-3-Clause
*****************************************************************************/
#include "QskFluent2TextAreaSkinlet.h"
#include "QskTextArea.h"
using Q = QskTextArea;
QskFluent2TextAreaSkinlet::QskFluent2TextAreaSkinlet( QskSkin* skin )
: Inherited( skin )
{
}
QskFluent2TextAreaSkinlet::~QskFluent2TextAreaSkinlet()
{
}
QRectF QskFluent2TextAreaSkinlet::subControlRect( const QskSkinnable* skinnable,
const QRectF& contentsRect, QskAspect::Subcontrol subControl ) const
{
return Inherited::subControlRect( skinnable, contentsRect, subControl );
}
QSizeF QskFluent2TextAreaSkinlet::sizeHint( const QskSkinnable* skinnable,
Qt::SizeHint which, const QSizeF& constraint ) const
{
return Inherited::sizeHint( skinnable, which, constraint );
}
#include "moc_QskFluent2TextAreaSkinlet.cpp"

View File

@ -0,0 +1,29 @@
/******************************************************************************
* QSkinny - Copyright (C) The authors
* SPDX-License-Identifier: BSD-3-Clause
*****************************************************************************/
#ifndef QSK_FLUENT2_TEXTAREA_SKINLET_H
#define QSK_FLUENT2_TEXTAREA_SKINLET_H
#include "QskFluent2Global.h"
#include "QskTextAreaSkinlet.h"
class QSK_FLUENT2_EXPORT QskFluent2TextAreaSkinlet : public QskTextAreaSkinlet
{
Q_GADGET
using Inherited = QskTextAreaSkinlet;
public:
Q_INVOKABLE QskFluent2TextAreaSkinlet( QskSkin* = nullptr );
~QskFluent2TextAreaSkinlet() override;
QRectF subControlRect( const QskSkinnable*,
const QRectF& rect, QskAspect::Subcontrol ) const override;
QSizeF sizeHint( const QskSkinnable*,
Qt::SizeHint, const QSizeF& ) const override;
};
#endif

View File

@ -42,6 +42,8 @@
#include <QskTabBar.h>
#include <QskTabButton.h>
#include <QskTabView.h>
#include <QskTextArea.h>
#include <QskTextAreaSkinlet.h>
#include <QskTextField.h>
#include <QskTextFieldSkinlet.h>
#include <QskTextLabel.h>
@ -142,6 +144,10 @@ namespace
Q_INVOKABLE void setupTabButton();
Q_INVOKABLE void setupTabBar();
Q_INVOKABLE void setupTabView();
template< typename Q, typename SK >
void setupTextControl();
Q_INVOKABLE void setupTextArea();
Q_INVOKABLE void setupTextField();
Q_INVOKABLE void setupTextLabel();
@ -383,16 +389,12 @@ void Editor::setupTextLabel()
setBoxBorderColors( Q::Panel, QskRgb::lighter( m_pal.outline, 108 ) );
}
void Editor::setupTextField()
template< typename Q, typename SK >
void Editor::setupTextControl()
{
using Q = QskTextField;
using SK = QskTextFieldSkinlet;
using A = QskAspect;
using P = QPalette;
setAlignment( Q::Text, Qt::AlignLeft | Qt::AlignVCenter );
setAlignment( Q::PlaceholderText, Qt::AlignLeft | Qt::AlignVCenter );
for ( auto state : { A::NoState, Q::Disabled } )
{
const auto colorGroup = ( state == A::NoState ) ? P::Active : P::Disabled;
@ -418,6 +420,28 @@ void Editor::setupTextField()
setPadding( Q::TextPanel, 4_px );
}
void Editor::setupTextArea()
{
using Q = QskTextArea;
using SK = QskTextAreaSkinlet;
setAlignment( Q::Text, Qt::AlignLeft | Qt::AlignTop );
setAlignment( Q::PlaceholderText, Qt::AlignLeft | Qt::AlignTop );
setupTextControl< Q, SK >();
}
void Editor::setupTextField()
{
using Q = QskTextField;
using SK = QskTextFieldSkinlet;
setAlignment( Q::Text, Qt::AlignLeft | Qt::AlignVCenter );
setAlignment( Q::PlaceholderText, Qt::AlignLeft | Qt::AlignVCenter );
setupTextControl< Q, SK >();
}
void Editor::setupProgressBar()
{
// indeterminate style is different: TODO ...

View File

@ -10,6 +10,7 @@ list(APPEND HEADERS
list(APPEND PRIVATE_HEADERS
QskMaterial3ProgressBarSkinlet.h
QskMaterial3SliderSkinlet.h
QskMaterial3TextAreaSkinlet.h
QskMaterial3TextFieldSkinlet.h
)
@ -18,6 +19,7 @@ list(APPEND SOURCES
QskMaterial3SkinFactory.cpp
QskMaterial3ProgressBarSkinlet.cpp
QskMaterial3SliderSkinlet.cpp
QskMaterial3TextAreaSkinlet.cpp
QskMaterial3TextFieldSkinlet.cpp
)

View File

@ -11,6 +11,7 @@
#include "QskMaterial3Skin.h"
#include "QskMaterial3ProgressBarSkinlet.h"
#include "QskMaterial3SliderSkinlet.h"
#include "QskMaterial3TextAreaSkinlet.h"
#include "QskMaterial3TextFieldSkinlet.h"
#include <QskSkinHintTableEditor.h>
@ -46,6 +47,7 @@
#include <QskTabBar.h>
#include <QskTabButton.h>
#include <QskTabView.h>
#include <QskTextArea.h>
#include <QskTextField.h>
#include <QskTextLabel.h>
#include <QskVirtualKeyboard.h>
@ -185,6 +187,10 @@ namespace
Q_INVOKABLE void setupTabButton();
Q_INVOKABLE void setupTabBar();
Q_INVOKABLE void setupTabView();
template< typename Q, typename SK >
void setupTextControl();
Q_INVOKABLE void setupTextArea();
Q_INVOKABLE void setupTextField();
Q_INVOKABLE void setupTextLabel();
@ -431,11 +437,9 @@ void Editor::setupTextLabel()
setPadding( Q::Panel, 5_px );
}
void Editor::setupTextField()
template< typename Q, typename SK >
void Editor::setupTextControl()
{
using Q = QskTextField;
using SK = QskTextInputSkinlet;
setStrutSize( Q::Panel, -1.0, 56_px );
setPadding( Q::Panel, { 12_px, 8_px, 12_px, 8_px } );
setGradient( Q::Panel, m_pal.surfaceVariant );
@ -457,7 +461,6 @@ void Editor::setupTextField()
setColor( Q::Text, m_pal.onSurface );
setFontRole( Q::Text, BodyLarge );
setAlignment( Q::Text, Qt::AlignLeft | Qt::AlignVCenter );
setAlignment( Q::PlaceholderText, Qt::AlignLeft | Qt::AlignVCenter );
@ -474,6 +477,26 @@ void Editor::setupTextField()
setAlignment( Q::PlaceholderText, alignment( Q::Text ) );
}
void Editor::setupTextArea()
{
using Q = QskTextArea;
using SK = QskTextEditSkinlet;
setAlignment( Q::Text, Qt::AlignLeft | Qt::AlignTop );
setupTextControl< Q, SK >();
}
void Editor::setupTextField()
{
using Q = QskTextField;
using SK = QskTextInputSkinlet;
setAlignment( Q::Text, Qt::AlignLeft | Qt::AlignVCenter );
setupTextControl< Q, SK >();
}
void Editor::setupProgressBar()
{
using A = QskAspect;
@ -1618,6 +1641,7 @@ QskMaterial3Skin::QskMaterial3Skin( QObject* parent )
{
declareSkinlet< QskProgressBar, QskMaterial3ProgressBarSkinlet >();
declareSkinlet< QskSlider, QskMaterial3SliderSkinlet >();
declareSkinlet< QskTextArea, QskMaterial3TextAreaSkinlet >();
declareSkinlet< QskTextField, QskMaterial3TextFieldSkinlet >();
}

View File

@ -0,0 +1,36 @@
/******************************************************************************
* QSkinny - Copyright (C) The authors
* SPDX-License-Identifier: BSD-3-Clause
*****************************************************************************/
#include "QskMaterial3TextAreaSkinlet.h"
#include "QskTextArea.h"
QskMaterial3TextAreaSkinlet::QskMaterial3TextAreaSkinlet( QskSkin* skin )
: Inherited( skin )
{
}
QskMaterial3TextAreaSkinlet::~QskMaterial3TextAreaSkinlet()
{
}
QRectF QskMaterial3TextAreaSkinlet::subControlRect( const QskSkinnable* skinnable,
const QRectF& contentsRect, QskAspect::Subcontrol subControl ) const
{
return Inherited::subControlRect( skinnable, contentsRect, subControl );
}
QSGNode* QskMaterial3TextAreaSkinlet::updateSubNode(
const QskSkinnable* skinnable, quint8 nodeRole, QSGNode* node ) const
{
return Inherited::updateSubNode( skinnable, nodeRole, node );
}
QSizeF QskMaterial3TextAreaSkinlet::sizeHint( const QskSkinnable* skinnable,
Qt::SizeHint which, const QSizeF& constraint ) const
{
return Inherited::sizeHint( skinnable, which, constraint );
}
#include "moc_QskMaterial3TextAreaSkinlet.cpp"

View File

@ -0,0 +1,33 @@
/******************************************************************************
* QSkinny - Copyright (C) The authors
* SPDX-License-Identifier: BSD-3-Clause
*****************************************************************************/
#ifndef QSK_MATERIAL3_TEXTAREA_SKINLET_H
#define QSK_MATERIAL3_TEXTAREA_SKINLET_H
#include "QskMaterial3Global.h"
#include "QskTextAreaSkinlet.h"
class QSK_MATERIAL3_EXPORT QskMaterial3TextAreaSkinlet : public QskTextAreaSkinlet
{
Q_GADGET
using Inherited = QskTextAreaSkinlet;
public:
Q_INVOKABLE QskMaterial3TextAreaSkinlet( QskSkin* = nullptr );
~QskMaterial3TextAreaSkinlet() override;
QRectF subControlRect( const QskSkinnable*,
const QRectF& rect, QskAspect::Subcontrol ) const override;
QSizeF sizeHint( const QskSkinnable*,
Qt::SizeHint, const QSizeF& ) const override;
protected:
QSGNode* updateSubNode( const QskSkinnable*,
quint8 nodeRole, QSGNode* ) const override;
};
#endif

View File

@ -7,6 +7,7 @@
#include <QskGridBox.h>
#include <QskSlider.h>
#include <QskTextArea.h>
#include <QskTextField.h>
#include <QskSpinBox.h>
@ -92,6 +93,22 @@ namespace
}
}
};
class TextAreaBox : public QskLinearBox
{
public:
TextAreaBox( QQuickItem* parent = nullptr )
: QskLinearBox( Qt::Horizontal, parent )
{
setSpacing( 20 );
{
auto textArea = new QskTextArea( "here enter longer text", this );
textArea->setWrapMode( QskTextOptions::Wrap );
textArea->setPlaceholderText( "placeholder text" );
}
}
};
}
InputPage::InputPage( QQuickItem* parent )
@ -123,6 +140,8 @@ InputPage::InputPage( QQuickItem* parent )
auto textInputBox = new TextInputBox();
textInputBox->setSizePolicy( Qt::Vertical, QskSizePolicy::Fixed );
auto textAreaBox = new TextAreaBox();
auto vBox = new QskLinearBox( Qt::Vertical );
vBox->setSpacing( 30 );
vBox->setExtraSpacingAt( Qt::RightEdge | Qt::BottomEdge );
@ -130,8 +149,9 @@ InputPage::InputPage( QQuickItem* parent )
vBox->addItem( sliders[0].continous );
vBox->addItem( sliders[0].discrete );
vBox->addItem( sliders[0].centered );
vBox->addItem( textInputBox );
vBox->addItem( spinBox );
vBox->addItem( textInputBox );
vBox->addItem( textAreaBox );
auto mainBox = new QskLinearBox( Qt::Horizontal, this );
mainBox->setSpacing( 30 );

View File

@ -283,6 +283,10 @@ list(APPEND HEADERS
controls/QskTabButtonSkinlet.h
controls/QskTabView.h
controls/QskTabViewSkinlet.h
controls/QskTextArea.h
controls/QskTextAreaSkinlet.h
controls/QskTextEdit.h
controls/QskTextEditSkinlet.h
controls/QskTextField.h
controls/QskTextFieldSkinlet.h
controls/QskTextInput.h
@ -391,6 +395,10 @@ list(APPEND SOURCES
controls/QskTabButtonSkinlet.cpp
controls/QskTabView.cpp
controls/QskTabViewSkinlet.cpp
controls/QskTextArea.cpp
controls/QskTextAreaSkinlet.cpp
controls/QskTextEdit.cpp
controls/QskTextEditSkinlet.cpp
controls/QskTextField.cpp
controls/QskTextFieldSkinlet.cpp
controls/QskTextInput.cpp

View File

@ -100,6 +100,12 @@
#include "QskTextLabel.h"
#include "QskTextLabelSkinlet.h"
#include "QskTextArea.h"
#include "QskTextAreaSkinlet.h"
#include "QskTextEdit.h"
#include "QskTextEditSkinlet.h"
#include "QskTextField.h"
#include "QskTextFieldSkinlet.h"
@ -211,6 +217,7 @@ QskSkin::QskSkin( QObject* parent )
declareSkinlet< QskTabButton, QskTabButtonSkinlet >();
declareSkinlet< QskTabView, QskTabViewSkinlet >();
declareSkinlet< QskTextLabel, QskTextLabelSkinlet >();
declareSkinlet< QskTextArea, QskTextAreaSkinlet >();
declareSkinlet< QskTextField, QskTextFieldSkinlet >();
declareSkinlet< QskProgressBar, QskProgressBarSkinlet >();
declareSkinlet< QskProgressRing, QskProgressRingSkinlet >();

View File

@ -0,0 +1,47 @@
/******************************************************************************
* QSkinny - Copyright (C) The authors
* SPDX-License-Identifier: BSD-3-Clause
*****************************************************************************/
#include "QskTextArea.h"
QSK_SUBCONTROL( QskTextArea, Panel )
QSK_SUBCONTROL( QskTextArea, PlaceholderText )
class QskTextArea::PrivateData
{
public:
QString placeholderText;
};
QskTextArea::QskTextArea( QQuickItem* parent )
: Inherited( parent )
, m_data( new PrivateData() )
{
}
QskTextArea::QskTextArea( const QString& text, QQuickItem* parent )
: QskTextArea( parent )
{
setText( text );
}
QskTextArea::~QskTextArea()
{
}
void QskTextArea::setPlaceholderText( const QString& text )
{
if ( m_data->placeholderText != text )
{
m_data->placeholderText = text;
Q_EMIT placeholderTextChanged( text );
}
}
QString QskTextArea::placeholderText() const
{
return m_data->placeholderText;
}
#include "moc_QskTextArea.cpp"

View File

@ -0,0 +1,39 @@
/******************************************************************************
* QSkinny - Copyright (C) The authors
* SPDX-License-Identifier: BSD-3-Clause
*****************************************************************************/
#ifndef QSK_TEXT_AREA_H
#define QSK_TEXT_AREA_H
#include "QskTextEdit.h"
class QSK_EXPORT QskTextArea : public QskTextEdit
{
Q_OBJECT
Q_PROPERTY( QString placeholderText READ placeholderText
WRITE setPlaceholderText NOTIFY placeholderTextChanged )
using Inherited = QskTextEdit;
public:
QSK_SUBCONTROLS( Panel, PlaceholderText )
QskTextArea( QQuickItem* parent = nullptr );
QskTextArea( const QString& text, QQuickItem* parent = nullptr );
~QskTextArea() override;
void setPlaceholderText( const QString& );
QString placeholderText() const;
Q_SIGNALS:
void placeholderTextChanged( const QString& );
private:
class PrivateData;
std::unique_ptr< PrivateData > m_data;
};
#endif

View File

@ -0,0 +1,103 @@
/******************************************************************************
* QSkinny - Copyright (C) The authors
* SPDX-License-Identifier: BSD-3-Clause
*****************************************************************************/
#include "QskTextAreaSkinlet.h"
#include "QskTextArea.h"
using Q = QskTextArea;
QskTextAreaSkinlet::QskTextAreaSkinlet( QskSkin* skin )
: Inherited( skin )
{
setNodeRoles( { PanelRole, TextPanelRole, PlaceholderTextRole } );
}
QskTextAreaSkinlet::~QskTextAreaSkinlet()
{
}
QRectF QskTextAreaSkinlet::subControlRect( const QskSkinnable* skinnable,
const QRectF& contentsRect, QskAspect::Subcontrol subControl ) const
{
if ( subControl == Q::Panel )
return contentsRect;
if ( subControl == Q::TextPanel )
return skinnable->subControlContentsRect( contentsRect, Q::Panel );
if ( subControl == Q::PlaceholderText )
{
const auto textArea = static_cast< const QskTextArea* >( skinnable );
if( textArea->text().isEmpty() )
return subControlRect( skinnable, contentsRect, Q::Text );
return QRectF();
}
return Inherited::subControlRect( skinnable, contentsRect, subControl );
}
QSGNode* QskTextAreaSkinlet::updateSubNode(
const QskSkinnable* skinnable, quint8 nodeRole, QSGNode* node ) const
{
const auto textArea = static_cast< const QskTextArea* >( skinnable );
switch ( nodeRole )
{
case PanelRole:
{
return updateBoxNode( skinnable, node, Q::Panel );
}
case PlaceholderTextRole:
{
const auto text = effectivePlaceholderText( textArea );
if ( text.isEmpty() )
return nullptr;
const auto subControl = Q::PlaceholderText;
QskSkinHintStatus status;
auto options = skinnable->textOptionsHint( subControl, &status );
if ( !status.isValid() )
options.setElideMode( Qt::ElideRight );
return updateTextNode( skinnable, node,
textArea->subControlRect( subControl ),
textArea->alignmentHint( subControl, Qt::AlignLeft ),
options, text, subControl );
}
}
return Inherited::updateSubNode( skinnable, nodeRole, node );
}
QSizeF QskTextAreaSkinlet::sizeHint( const QskSkinnable* skinnable,
Qt::SizeHint which, const QSizeF& constraint ) const
{
if ( which != Qt::PreferredSize )
return QSizeF();
auto hint = Inherited::sizeHint( skinnable, which, constraint );
hint = skinnable->outerBoxSize( Q::Panel, hint );
hint = hint.expandedTo( skinnable->strutSizeHint( Q::Panel ) );
return hint;
}
QString QskTextAreaSkinlet::effectivePlaceholderText(
const QskTextArea* textArea ) const
{
if ( textArea->text().isEmpty() &&
!( textArea->isReadOnly() || textArea->isEditing() ) )
{
return textArea->placeholderText();
}
return QString();
}
#include "moc_QskTextAreaSkinlet.cpp"

View File

@ -0,0 +1,44 @@
/******************************************************************************
* QSkinny - Copyright (C) The authors
* SPDX-License-Identifier: BSD-3-Clause
*****************************************************************************/
#ifndef QSK_TEXT_AREA_SKINLET_H
#define QSK_TEXT_AREA_SKINLET_H
#include "QskTextEditSkinlet.h"
class QskTextArea;
class QSK_EXPORT QskTextAreaSkinlet : public QskTextEditSkinlet
{
Q_GADGET
using Inherited = QskTextEditSkinlet;
public:
enum NodeRole : quint8
{
PanelRole = Inherited::RoleCount,
PlaceholderTextRole,
RoleCount
};
Q_INVOKABLE QskTextAreaSkinlet( QskSkin* = nullptr );
~QskTextAreaSkinlet() override;
QRectF subControlRect( const QskSkinnable*,
const QRectF& rect, QskAspect::Subcontrol ) const override;
QSizeF sizeHint( const QskSkinnable*,
Qt::SizeHint, const QSizeF& ) const override;
protected:
QSGNode* updateSubNode( const QskSkinnable*,
quint8 nodeRole, QSGNode* ) const override;
virtual QString effectivePlaceholderText( const QskTextArea* ) const;
};
#endif

View File

@ -0,0 +1,758 @@
/******************************************************************************
* QSkinny - Copyright (C) The authors
* SPDX-License-Identifier: BSD-3-Clause
*****************************************************************************/
#include "QskTextEdit.h"
#include "QskTextEditSkinlet.h"
#include "QskFontRole.h"
#include "QskInternalMacros.h"
#include "QskQuick.h"
QSK_QT_PRIVATE_BEGIN
#include <private/qquicktextedit_p.h>
#include <private/qquicktextedit_p_p.h>
#if QT_VERSION >= QT_VERSION_CHECK( 6, 0, 0 )
#include <private/qeventpoint_p.h>
#endif
QSK_QT_PRIVATE_END
QSK_SUBCONTROL( QskTextEdit, TextPanel )
QSK_SUBCONTROL( QskTextEdit, Text )
QSK_SYSTEM_STATE( QskTextEdit, ReadOnly, QskAspect::FirstSystemState << 1 )
QSK_SYSTEM_STATE( QskTextEdit, Editing, QskAspect::FirstSystemState << 2 )
QSK_SYSTEM_STATE( QskTextEdit, Error, QskAspect::FirstSystemState << 4 )
static inline void qskPropagateReadOnly( QskTextEdit* edit )
{
Q_EMIT edit->readOnlyChanged( edit->isReadOnly() );
QEvent event( QEvent::ReadOnlyChange );
QCoreApplication::sendEvent( edit, &event );
}
static inline void qskTranslateMouseEventPosition(
QMouseEvent* mouseEvent, const QPointF& offset )
{
#if QT_VERSION >= QT_VERSION_CHECK( 6, 0, 0 )
auto& point = mouseEvent->point( 0 );
QMutableEventPoint::setPosition(
point, point.position() + offset );
#else
mouseEvent->setLocalPos( mouseEvent->localPos() + offset );
#endif
}
static inline void qskBindSignals(
const QQuickTextEdit* wrappedEdit, QskTextEdit* edit )
{
QObject::connect( wrappedEdit, &QQuickTextEdit::textChanged,
edit, [ edit ] { Q_EMIT edit->textChanged( edit->text() ); } );
QObject::connect( wrappedEdit, &QQuickTextEdit::preeditTextChanged,
edit, [ edit ] { Q_EMIT edit->preeditTextChanged( edit->preeditText() ); } );
QObject::connect( wrappedEdit, &QQuickTextEdit::readOnlyChanged,
edit, [ edit ] { qskPropagateReadOnly( edit ); } );
QObject::connect( wrappedEdit, &QQuickTextEdit::overwriteModeChanged,
edit, &QskTextEdit::overwriteModeChanged );
QObject::connect( wrappedEdit, &QQuickTextEdit::wrapModeChanged,
edit, [ edit ] { Q_EMIT edit->wrapModeChanged( edit->wrapMode() ); } );
QObject::connect( wrappedEdit, &QQuickTextEdit::lineCountChanged,
[ edit ] { Q_EMIT edit->lineCountChanged( edit->lineCount() ); } );
QObject::connect( wrappedEdit, &QQuickTextEdit::textFormatChanged,
edit, [ edit ]( QQuickTextEdit::TextFormat format )
{
Q_EMIT edit->textFormatChanged( static_cast< QskTextOptions::TextFormat >( format ) );
} );
QObject::connect( wrappedEdit, &QQuickTextEdit::selectByMouseChanged,
edit, &QskTextEdit::selectByMouseChanged );
QObject::connect( wrappedEdit, &QQuickTextEdit::tabStopDistanceChanged,
edit, &QskTextEdit::tabStopDistanceChanged );
QObject::connect( wrappedEdit, &QQuickItem::implicitWidthChanged,
edit, &QskControl::resetImplicitSize );
QObject::connect( wrappedEdit, &QQuickItem::implicitHeightChanged,
edit, &QskControl::resetImplicitSize );
}
namespace
{
class QuickTextEdit final : public QQuickTextEdit
{
using Inherited = QQuickTextEdit;
public:
QuickTextEdit( QskTextEdit* );
void setEditing( bool on );
inline void setAlignment( Qt::Alignment alignment )
{
setHAlign( ( HAlignment ) ( int( alignment ) & 0x0f ) );
setVAlign( ( VAlignment ) ( int( alignment ) & 0xf0 ) );
}
void updateColors();
void updateMetrics();
inline bool handleEvent( QEvent* event )
{
bool ok;
switch( static_cast< int >( event->type() ) )
{
case QEvent::MouseButtonDblClick:
case QEvent::MouseButtonPress:
case QEvent::MouseButtonRelease:
case QEvent::MouseMove:
{
auto mouseEvent = static_cast< QMouseEvent* >( event );
/*
As the event was sent for the parent item
we have to translate the position into
our coordinate system.
*/
qskTranslateMouseEventPosition( mouseEvent, -position() );
ok = this->event( mouseEvent );
qskTranslateMouseEventPosition( mouseEvent, position() );
break;
}
default:
ok = this->event( event );
}
return ok;
}
inline bool hasSelectedText() const
{
return !selectedText().isEmpty();
}
protected:
#if QT_VERSION >= QT_VERSION_CHECK( 6, 0, 0 )
void geometryChange(
const QRectF& newGeometry, const QRectF& oldGeometry ) override
{
Inherited::geometryChange( newGeometry, oldGeometry );
updateClip();
}
#else
void geometryChanged(
const QRectF& newGeometry, const QRectF& oldGeometry ) override
{
Inherited::geometryChanged( newGeometry, oldGeometry );
updateClip();
}
#endif
void updateClip()
{
setClip( ( contentWidth() > width() ) ||
( contentHeight() > height() ) );
}
QSGNode* updatePaintNode(
QSGNode* oldNode, UpdatePaintNodeData* data ) override
{
updateColors();
return Inherited::updatePaintNode( oldNode, data );
}
};
QuickTextEdit::QuickTextEdit( QskTextEdit* textField )
: QQuickTextEdit( textField )
{
classBegin();
setActiveFocusOnTab( false );
setFlag( ItemAcceptsInputMethod, false );
setFocusOnPress( false );
setSelectByMouse( true );
componentComplete();
connect( this, &QuickTextEdit::contentSizeChanged,
this, &QuickTextEdit::updateClip );
}
void QuickTextEdit::setEditing( bool on )
{
auto d = QQuickTextEditPrivate::get( this );
if ( d->cursorVisible == on )
return;
setCursorVisible( on );
polish();
update();
}
void QuickTextEdit::updateMetrics()
{
auto textEdit = static_cast< const QskTextEdit* >( parentItem() );
setAlignment( textEdit->alignment() );
setFont( textEdit->font() );
}
void QuickTextEdit::updateColors()
{
using Q = QskTextEdit;
auto input = static_cast< const Q* >( parentItem() );
setColor( input->color( Q::Text ) );
const auto state = QskTextEditSkinlet::Selected;
setSelectionColor( input->color( Q::TextPanel | state ) );
setSelectedTextColor( input->color( Q::Text | state ) );
}
}
class QskTextEdit::PrivateData
{
public:
QuickTextEdit* wrappedEdit;
ActivationModes activationModes;
};
QskTextEdit::QskTextEdit( QQuickItem* parent )
: Inherited( parent )
, m_data( new PrivateData() )
{
m_data->activationModes = ActivationOnMouse | ActivationOnKey;
setPolishOnResize( true );
setAcceptHoverEvents( true );
setFocusPolicy( Qt::StrongFocus );
setFlag( QQuickItem::ItemAcceptsInputMethod );
/*
QQuickTextEdit is a beast of almost 3.5k lines of code, we don't
want to reimplement that - at least not now.
So this is more or less a simple wrapper making everything
conforming to qskinny.
*/
m_data->wrappedEdit = new QuickTextEdit( this );
qskBindSignals( m_data->wrappedEdit, this );
setAcceptedMouseButtons( m_data->wrappedEdit->acceptedMouseButtons() );
m_data->wrappedEdit->setAcceptedMouseButtons( Qt::NoButton );
initSizePolicy( QskSizePolicy::Expanding, QskSizePolicy::Expanding );
}
QskTextEdit::~QskTextEdit()
{
}
bool QskTextEdit::event( QEvent* event )
{
if ( event->type() == QEvent::ShortcutOverride )
{
return m_data->wrappedEdit->handleEvent( event );
}
else if ( event->type() == QEvent::LocaleChange )
{
qskUpdateInputMethod( this, Qt::ImPreferredLanguage );
}
return Inherited::event( event );
}
void QskTextEdit::keyPressEvent( QKeyEvent* event )
{
if ( isEditing() )
{
switch ( event->key() )
{
case Qt::Key_Enter:
case Qt::Key_Return:
{
QGuiApplication::inputMethod()->commit();
if ( !( inputMethodHints() & Qt::ImhMultiLine ) )
{
setEditing( false );
// When returning from a virtual keyboard
qskForceActiveFocus( this, Qt::PopupFocusReason );
}
break;
}
#if 1
case Qt::Key_Escape:
{
setEditing( false );
qskForceActiveFocus( this, Qt::PopupFocusReason );
break;
}
#endif
default:
{
m_data->wrappedEdit->handleEvent( event );
}
}
return;
}
if ( ( m_data->activationModes & ActivationOnKey ) && !event->isAutoRepeat() )
{
if ( event->key() == Qt::Key_Select || event->key() == Qt::Key_Space )
{
setEditing( true );
return;
}
}
Inherited::keyPressEvent( event );
}
void QskTextEdit::keyReleaseEvent( QKeyEvent* event )
{
Inherited::keyReleaseEvent( event );
}
void QskTextEdit::mousePressEvent( QMouseEvent* event )
{
m_data->wrappedEdit->handleEvent( event );
if ( !isReadOnly() && !qGuiApp->styleHints()->setFocusOnTouchRelease() )
setEditing( true );
}
void QskTextEdit::mouseMoveEvent( QMouseEvent* event )
{
m_data->wrappedEdit->handleEvent( event );
}
void QskTextEdit::mouseReleaseEvent( QMouseEvent* event )
{
m_data->wrappedEdit->handleEvent( event );
if ( !isReadOnly() && qGuiApp->styleHints()->setFocusOnTouchRelease() )
setEditing( true );
}
void QskTextEdit::mouseDoubleClickEvent( QMouseEvent* event )
{
m_data->wrappedEdit->handleEvent( event );
}
void QskTextEdit::inputMethodEvent( QInputMethodEvent* event )
{
m_data->wrappedEdit->handleEvent( event );
}
void QskTextEdit::focusInEvent( QFocusEvent* event )
{
if ( m_data->activationModes & ActivationOnFocus )
{
switch ( event->reason() )
{
case Qt::ActiveWindowFocusReason:
case Qt::PopupFocusReason:
break;
default:
#if 1
// auto selecting the complete text ???
#endif
setEditing( true );
}
}
Inherited::focusInEvent( event );
}
void QskTextEdit::focusOutEvent( QFocusEvent* event )
{
switch ( event->reason() )
{
case Qt::ActiveWindowFocusReason:
case Qt::PopupFocusReason:
{
break;
}
default:
{
m_data->wrappedEdit->deselect();
setEditing( false );
}
}
Inherited::focusOutEvent( event );
}
void QskTextEdit::updateLayout()
{
m_data->wrappedEdit->updateMetrics();
qskSetItemGeometry( m_data->wrappedEdit, subControlRect( Text ) );
}
void QskTextEdit::updateNode( QSGNode* node )
{
m_data->wrappedEdit->updateColors();
Inherited::updateNode( node );
}
QString QskTextEdit::text() const
{
return m_data->wrappedEdit->text();
}
void QskTextEdit::setText( const QString& text )
{
m_data->wrappedEdit->setText( text );
}
void QskTextEdit::clear()
{
m_data->wrappedEdit->clear();
}
void QskTextEdit::selectAll()
{
m_data->wrappedEdit->selectAll();
}
QskTextEdit::ActivationModes QskTextEdit::activationModes() const
{
return static_cast< QskTextEdit::ActivationModes >( m_data->activationModes );
}
void QskTextEdit::setActivationModes( ActivationModes modes )
{
if ( static_cast< ActivationModes >( m_data->activationModes ) != modes )
{
m_data->activationModes = modes;
Q_EMIT activationModesChanged();
}
}
static inline void qskUpdateInputMethodFont( const QskTextEdit* input )
{
const auto queries = Qt::ImCursorRectangle | Qt::ImFont | Qt::ImAnchorRectangle;
qskUpdateInputMethod( input, queries );
}
void QskTextEdit::setFontRole( const QskFontRole& role )
{
if ( setFontRoleHint( Text, role ) )
{
qskUpdateInputMethodFont( this );
Q_EMIT fontRoleChanged();
}
}
void QskTextEdit::resetFontRole()
{
if ( resetFontRoleHint( Text ) )
{
qskUpdateInputMethodFont( this );
Q_EMIT fontRoleChanged();
}
}
QskFontRole QskTextEdit::fontRole() const
{
return fontRoleHint( Text );
}
void QskTextEdit::setAlignment( Qt::Alignment alignment )
{
if ( setAlignmentHint( Text, alignment ) )
{
m_data->wrappedEdit->setAlignment( alignment );
Q_EMIT alignmentChanged();
}
}
void QskTextEdit::resetAlignment()
{
if ( resetAlignmentHint( Text ) )
{
m_data->wrappedEdit->setAlignment( alignment() );
Q_EMIT alignmentChanged();
}
}
Qt::Alignment QskTextEdit::alignment() const
{
return alignmentHint( Text, Qt::AlignLeft | Qt::AlignTop );
}
void QskTextEdit::setWrapMode( QskTextOptions::WrapMode wrapMode )
{
m_data->wrappedEdit->setWrapMode(
static_cast< QQuickTextEdit::WrapMode >( wrapMode ) );
}
QskTextOptions::WrapMode QskTextEdit::wrapMode() const
{
return static_cast< QskTextOptions::WrapMode >(
m_data->wrappedEdit->wrapMode() );
}
void QskTextEdit::setSelectByMouse( bool on )
{
m_data->wrappedEdit->setSelectByMouse( on );
}
bool QskTextEdit::selectByMouse() const
{
return m_data->wrappedEdit->selectByMouse();
}
QFont QskTextEdit::font() const
{
return effectiveFont( QskTextEdit::Text );
}
bool QskTextEdit::isReadOnly() const
{
return m_data->wrappedEdit->isReadOnly();
}
void QskTextEdit::setReadOnly( bool on )
{
auto edit = m_data->wrappedEdit;
if ( edit->isReadOnly() == on )
return;
#if 1
// do we want to be able to restore the previous policy ?
setFocusPolicy( Qt::NoFocus );
#endif
edit->setReadOnly( on );
// we are killing user settings here ?
edit->setFlag( QQuickItem::ItemAcceptsInputMethod, !on );
qskUpdateInputMethod( this, Qt::ImEnabled );
setSkinStateFlag( ReadOnly, on );
}
void QskTextEdit::setEditing( bool on )
{
if ( isReadOnly() || on == isEditing() )
return;
setSkinStateFlag( Editing, on );
m_data->wrappedEdit->setEditing( on );
if ( on )
{
#if 0
updateInputMethod( Qt::ImCursorRectangle | Qt::ImAnchorRectangle );
QGuiApplication::inputMethod()->inputDirection
#endif
qskInputMethodSetVisible( this, true );
}
else
{
Q_EMIT m_data->wrappedEdit->editingFinished();
#if 0
inputMethod->reset();
#endif
qskInputMethodSetVisible( this, false );
}
Q_EMIT editingChanged( on );
}
bool QskTextEdit::isEditing() const
{
return hasSkinState( Editing );
}
int QskTextEdit::cursorPosition() const
{
return m_data->wrappedEdit->cursorPosition();
}
void QskTextEdit::setCursorPosition( int pos )
{
m_data->wrappedEdit->setCursorPosition( pos );
}
QString QskTextEdit::preeditText() const
{
return m_data->wrappedEdit->preeditText();
}
bool QskTextEdit::overwriteMode() const
{
return m_data->wrappedEdit->overwriteMode();
}
void QskTextEdit::setOverwriteMode( bool overwrite )
{
m_data->wrappedEdit->setOverwriteMode( overwrite );
}
void QskTextEdit::setTextFormat( QskTextOptions::TextFormat textFormat )
{
m_data->wrappedEdit->setTextFormat(
static_cast< QQuickTextEdit::TextFormat >( textFormat ) );
}
QskTextOptions::TextFormat QskTextEdit::textFormat() const
{
return static_cast< QskTextOptions::TextFormat >(
m_data->wrappedEdit->textFormat() );
}
int QskTextEdit::lineCount() const
{
return m_data->wrappedEdit->lineCount();
}
QVariant QskTextEdit::inputMethodQuery(
Qt::InputMethodQuery property ) const
{
return inputMethodQuery( property, QVariant() );
}
QVariant QskTextEdit::inputMethodQuery(
Qt::InputMethodQuery query, const QVariant& argument ) const
{
switch ( query )
{
case Qt::ImEnabled:
{
return QVariant( ( bool ) ( flags() & ItemAcceptsInputMethod ) );
}
case Qt::ImFont:
{
return font();
}
case Qt::ImPreferredLanguage:
{
return locale();
}
case Qt::ImInputItemClipRectangle:
case Qt::ImCursorRectangle:
{
QVariant v = m_data->wrappedEdit->inputMethodQuery( query, argument );
#if 1
if ( v.canConvert< QRectF >() )
v.setValue( v.toRectF().translated( m_data->wrappedEdit->position() ) );
#endif
return v;
}
default:
{
return m_data->wrappedEdit->inputMethodQuery( query, argument );
}
}
}
bool QskTextEdit::canUndo() const
{
return m_data->wrappedEdit->canUndo();
}
bool QskTextEdit::canRedo() const
{
return m_data->wrappedEdit->canRedo();
}
Qt::InputMethodHints QskTextEdit::inputMethodHints() const
{
return m_data->wrappedEdit->inputMethodHints();
}
void QskTextEdit::setInputMethodHints( Qt::InputMethodHints hints )
{
if ( m_data->wrappedEdit->inputMethodHints() != hints )
{
m_data->wrappedEdit->setInputMethodHints( hints );
qskUpdateInputMethod( this, Qt::ImHints );
}
}
int QskTextEdit::tabStopDistance() const
{
return m_data->wrappedEdit->tabStopDistance();
}
void QskTextEdit::setTabStopDistance( qreal distance )
{
m_data->wrappedEdit->setTabStopDistance( distance );
}
void QskTextEdit::setupFrom( const QQuickItem* item )
{
if ( item == nullptr )
return;
// finding attributes from the input hints of item
int maxCharacters = 32767;
Qt::InputMethodQueries queries = Qt::ImQueryAll;
queries &= ~Qt::ImEnabled;
QInputMethodQueryEvent event( queries );
QCoreApplication::sendEvent( const_cast< QQuickItem* >( item ), &event );
if ( event.queries() & Qt::ImMaximumTextLength )
{
// needs to be handled before Qt::ImCursorPosition !
const auto max = event.value( Qt::ImMaximumTextLength ).toInt();
maxCharacters = qBound( 0, max, maxCharacters );
}
if ( event.queries() & Qt::ImSurroundingText )
{
const auto text = event.value( Qt::ImSurroundingText ).toString();
setText( text );
}
if ( event.queries() & Qt::ImCursorPosition )
{
const auto pos = event.value( Qt::ImCursorPosition ).toInt();
setCursorPosition( pos );
}
if ( event.queries() & Qt::ImCurrentSelection )
{
#if 0
const auto text = event.value( Qt::ImCurrentSelection ).toString();
if ( !text.isEmpty() )
{
}
#endif
}
}
#include "moc_QskTextEdit.cpp"

190
src/controls/QskTextEdit.h Normal file
View File

@ -0,0 +1,190 @@
/******************************************************************************
* QSkinny - Copyright (C) The authors
* SPDX-License-Identifier: BSD-3-Clause
*****************************************************************************/
#ifndef QSK_TEXT_EDIT_H
#define QSK_TEXT_EDIT_H
#include "QskControl.h"
#include "QskTextOptions.h"
class QValidator;
class QskFontRole;
class QSK_EXPORT QskTextEdit : public QskControl
{
Q_OBJECT
Q_PROPERTY( QString text READ text
WRITE setText NOTIFY textChanged USER true )
Q_PROPERTY( QskFontRole fontRole READ fontRole
WRITE setFontRole RESET resetFontRole NOTIFY fontRoleChanged )
Q_PROPERTY( QFont font READ font )
Q_PROPERTY( Qt::Alignment alignment READ alignment
WRITE setAlignment RESET resetAlignment NOTIFY alignmentChanged )
Q_PROPERTY( QskTextOptions::WrapMode wrapMode READ wrapMode
WRITE setWrapMode NOTIFY wrapModeChanged )
Q_PROPERTY( int lineCount READ lineCount NOTIFY lineCountChanged )
Q_PROPERTY( QskTextOptions::TextFormat textFormat READ textFormat
WRITE setTextFormat NOTIFY textFormatChanged )
Q_PROPERTY( ActivationModes activationModes READ activationModes
WRITE setActivationModes NOTIFY activationModesChanged )
Q_PROPERTY( bool selectByMouse READ selectByMouse
WRITE setSelectByMouse NOTIFY selectByMouseChanged )
Q_PROPERTY( bool editing READ isEditing
WRITE setEditing NOTIFY editingChanged )
Q_PROPERTY( qreal tabStopDistance READ tabStopDistance
WRITE setTabStopDistance NOTIFY tabStopDistanceChanged )
using Inherited = QskControl;
public:
QSK_SUBCONTROLS( TextPanel, Text )
QSK_STATES( ReadOnly, Editing, Error )
enum ActivationMode
{
NoActivation,
ActivationOnFocus = 1 << 0,
ActivationOnMouse = 1 << 1,
ActivationOnKey = 1 << 2,
ActivationOnInput = ActivationOnMouse | ActivationOnKey,
ActivationOnAll = ActivationOnFocus | ActivationOnMouse | ActivationOnKey
};
Q_ENUM( ActivationMode )
Q_DECLARE_FLAGS( ActivationModes, ActivationMode )
QskTextEdit( QQuickItem* parent = nullptr );
~QskTextEdit() override;
void setupFrom( const QQuickItem* );
QString text() const;
void setFontRole( const QskFontRole& role );
void resetFontRole();
QskFontRole fontRole() const;
void setAlignment( Qt::Alignment );
void resetAlignment();
Qt::Alignment alignment() const;
void setWrapMode( QskTextOptions::WrapMode );
QskTextOptions::WrapMode wrapMode() const;
void setTextFormat( QskTextOptions::TextFormat );
QskTextOptions::TextFormat textFormat() const;
int lineCount() const;
void setActivationModes( ActivationModes );
ActivationModes activationModes() const;
void setSelectByMouse( bool );
bool selectByMouse() const;
bool isEditing() const;
QFont font() const;
bool isReadOnly() const;
void setReadOnly( bool );
int cursorPosition() const;
void setCursorPosition( int );
QString preeditText() const;
bool overwriteMode() const;
void setOverwriteMode( bool );
QVariant inputMethodQuery( Qt::InputMethodQuery ) const override;
QVariant inputMethodQuery( Qt::InputMethodQuery, const QVariant& argument ) const;
bool canUndo() const;
bool canRedo() const;
Qt::InputMethodHints inputMethodHints() const;
void setInputMethodHints( Qt::InputMethodHints );
int tabStopDistance() const;
void setTabStopDistance( qreal );
public Q_SLOTS:
void clear();
void selectAll();
void setText( const QString& );
void setEditing( bool );
Q_SIGNALS:
void textChanged( const QString& );
void preeditTextChanged( const QString& );
void editingChanged( bool );
void activationModesChanged();
void readOnlyChanged( bool );
void panelChanged( bool );
void displayTextChanged( const QString& );
void textEdited( const QString& );
void placeholderTextChanged( const QString& );
void fontRoleChanged();
void alignmentChanged();
void wrapModeChanged( QskTextOptions::WrapMode );
void lineCountChanged( int );
void selectByMouseChanged( bool );
void textFormatChanged( QskTextOptions::TextFormat );
void overwriteModeChanged( bool );
void tabStopDistanceChanged( qreal );
protected:
bool event( QEvent* ) override;
void inputMethodEvent( QInputMethodEvent* ) override;
void focusInEvent( QFocusEvent* ) override;
void focusOutEvent( QFocusEvent* ) override;
void mousePressEvent( QMouseEvent* ) override;
void mouseMoveEvent( QMouseEvent* ) override;
void mouseReleaseEvent( QMouseEvent* ) override;
void mouseDoubleClickEvent( QMouseEvent* ) override;
void keyPressEvent( QKeyEvent* ) override;
void keyReleaseEvent( QKeyEvent* ) override;
void updateLayout() override;
void updateNode( QSGNode* ) override;
private:
class PrivateData;
std::unique_ptr< PrivateData > m_data;
};
Q_DECLARE_OPERATORS_FOR_FLAGS( QskTextEdit::ActivationModes )
Q_DECLARE_METATYPE( QskTextEdit::ActivationModes )
#endif

View File

@ -0,0 +1,92 @@
/******************************************************************************
* QSkinny - Copyright (C) The authors
* SPDX-License-Identifier: BSD-3-Clause
*****************************************************************************/
#include "QskTextEditSkinlet.h"
#include "QskTextEdit.h"
#include <qfontmetrics.h>
using Q = QskTextEdit;
QSK_SYSTEM_STATE( QskTextEditSkinlet, Selected, QskAspect::FirstSystemState << 3 )
QskTextEditSkinlet::QskTextEditSkinlet( QskSkin* skin )
: Inherited( skin )
{
setNodeRoles( { TextPanelRole } );
}
QskTextEditSkinlet::~QskTextEditSkinlet()
{
}
QRectF QskTextEditSkinlet::subControlRect( const QskSkinnable* skinnable,
const QRectF& contentsRect, QskAspect::Subcontrol subControl ) const
{
if ( subControl == Q::TextPanel )
{
return contentsRect;
}
else if ( subControl == Q::Text )
{
auto rect = skinnable->subControlContentsRect( contentsRect, Q::TextPanel );
rect = rect.marginsAdded( skinnable->marginHint( Q::Text ) );
return rect;
}
return Inherited::subControlRect( skinnable, contentsRect, subControl );
}
QSGNode* QskTextEditSkinlet::updateSubNode(
const QskSkinnable* skinnable, quint8 nodeRole, QSGNode* node ) const
{
switch ( nodeRole )
{
case TextPanelRole:
{
return updateBoxNode( skinnable, node, Q::TextPanel );
}
}
return Inherited::updateSubNode( skinnable, nodeRole, node );
}
QSizeF QskTextEditSkinlet::sizeHint( const QskSkinnable* skinnable,
Qt::SizeHint which, const QSizeF& ) const
{
if ( which != Qt::PreferredSize )
return QSizeF();
const auto textEdit = static_cast< const QskTextEdit* >( skinnable );
const QFontMetricsF fm( textEdit->effectiveFont( Q::Text ) );
int flags = Qt::TextExpandTabs;
const auto wm = textEdit->wrapMode();
if( wm & QskTextOptions::WordWrap )
{
flags |= Qt::TextWordWrap;
}
else if( wm & QskTextOptions::WrapAnywhere )
{
flags |= Qt::TextWrapAnywhere;
}
else if( wm & QskTextOptions::Wrap )
{
flags |= Qt::TextWordWrap | Qt::TextWrapAnywhere;
}
auto hint = fm.boundingRect( textEdit->subControlRect( Q::Text ), flags, textEdit->text() ).size();
hint = textEdit->outerBoxSize( Q::TextPanel, hint );
hint = hint.expandedTo( textEdit->strutSizeHint( Q::TextPanel ) );
return hint;
}
#include "moc_QskTextEditSkinlet.cpp"

View File

@ -0,0 +1,39 @@
/******************************************************************************
* QSkinny - Copyright (C) The authors
* SPDX-License-Identifier: BSD-3-Clause
*****************************************************************************/
#ifndef QSK_TEXT_EDIT_SKINLET_H
#define QSK_TEXT_EDIT_SKINLET_H
#include "QskSkinlet.h"
class QSK_EXPORT QskTextEditSkinlet : public QskSkinlet
{
using Inherited = QskSkinlet;
public:
QSK_STATES( Selected )
enum NodeRole : quint8
{
TextPanelRole,
RoleCount
};
~QskTextEditSkinlet() override;
QRectF subControlRect( const QskSkinnable*,
const QRectF& rect, QskAspect::Subcontrol ) const override;
QSizeF sizeHint( const QskSkinnable*,
Qt::SizeHint, const QSizeF& ) const override;
protected:
QskTextEditSkinlet( QskSkin* = nullptr );
QSGNode* updateSubNode( const QskSkinnable*,
quint8 nodeRole, QSGNode* ) const override;
};
#endif

View File

@ -236,7 +236,7 @@ namespace
const auto state = QskTextInputSkinlet::Selected;
setSelectionColor( input->color( Q::TextPanel | state ) );
setSelectedTextColor( input->color( QskTextInput::Text | state ) );
setSelectedTextColor( input->color( Q::Text | state ) );
}
}