inputpanel/QskInputPredictionBar.cpp

Source code

/******************************************************************************
 * QSkinny - Copyright (C) 2016 Uwe Rathmann
 * This file may be used under the terms of the QSkinny License, Version 1.0
 *****************************************************************************/

#include "QskInputPredictionBar.h"
#include "QskLinearBox.h"
#include "QskPushButton.h"
#include "QskTextOptions.h"

#include <qfontmetrics.h>
#include <qstringlist.h>

QSK_SUBCONTROL( QskInputPredictionBar, Panel )
QSK_SUBCONTROL( QskInputPredictionBar, ButtonPanel )
QSK_SUBCONTROL( QskInputPredictionBar, ButtonText )

namespace
{
    class Button final : public QskPushButton
    {
      public:
        Button( QQuickItem* parent )
            : QskPushButton( parent )
        {
            QskTextOptions options;
            options.setElideMode( Qt::ElideRight );

            setTextOptions( options );
        }

        QSizeF contentsSizeHint(
            Qt::SizeHint which, const QSizeF& ) const override
        {
            if ( which != Qt::PreferredSize )
                return QSizeF();

            auto size = QFontMetricsF( font() ).size( Qt::TextSingleLine, text() );

            size = size.expandedTo( strutSizeHint( Panel ) );
            size = outerBoxSize( Panel, size );

            return size;
        }

        QskAspect::Subcontrol substitutedSubcontrol(
            QskAspect::Subcontrol subControl ) const override
        {
            if ( subControl == QskPushButton::Panel )
                return QskInputPredictionBar::ButtonPanel;

            if ( subControl == QskPushButton::Text )
                return QskInputPredictionBar::ButtonText;

            return subControl;
        }
    };
}

class QskInputPredictionBar::PrivateData
{
  public:
    QskLinearBox* layoutBox;
    QStringList candidates;

    int scrollOffset = 0;
    const int buttonCount = 12;
};

QskInputPredictionBar::QskInputPredictionBar( QQuickItem* parent )
    : Inherited( parent )
    , m_data( new PrivateData )
{
    setAutoLayoutChildren( true );
    initSizePolicy( QskSizePolicy::Expanding, QskSizePolicy::Fixed );

    m_data->layoutBox = new QskLinearBox( Qt::Horizontal, this );

    for ( int i = 0; i < m_data->buttonCount; i++ )
    {
        auto button = new Button( m_data->layoutBox );
        button->setVisible( false );
        button->setSizePolicy( Qt::Horizontal, QskSizePolicy::Maximum );

        connect( button, &QskPushButton::clicked,
            this, &QskInputPredictionBar::buttonClicked );

        if ( i == 0 )
        {
            // to keep the height
            button->setLayoutHint( QskControl::RetainSizeWhenHidden, true );
        }
    }
}

QskInputPredictionBar::~QskInputPredictionBar()
{
}

QskAspect::Subcontrol QskInputPredictionBar::substitutedSubcontrol(
    QskAspect::Subcontrol subControl ) const
{
    if ( subControl == QskBox::Panel )
        return QskInputPredictionBar::Panel;

    return Inherited::substitutedSubcontrol( subControl );
}

void QskInputPredictionBar::setPrediction( const QStringList& candidates )
{
    if ( m_data->candidates != candidates )
    {
        m_data->candidates = candidates;
        setScrollOffset( 0 );
    }
}

QStringList QskInputPredictionBar::candidates() const
{
    return m_data->candidates;
}

void QskInputPredictionBar::setScrollOffset( int offset )
{
    m_data->scrollOffset = offset;

    const int candidateCount = m_data->candidates.length();
    const int count = std::min( candidateCount, m_data->buttonCount );
    const bool continueLeft = m_data->scrollOffset > 0;
    const bool continueRight = ( candidateCount - m_data->scrollOffset ) > count;

    for ( int i = 0; i < count; i++ )
    {
        auto button = qobject_cast< QskPushButton* >(
            m_data->layoutBox->itemAtIndex( i ) );

        if ( continueLeft && i == 0 )
        {
            button->setText( QChar( 0x2B05 ) );
        }
        else if ( continueRight && ( i == m_data->buttonCount - 1 ) )
        {
            button->setText( QChar( 0x27A1 ) );
        }
        else
        {
            const int index = i + m_data->scrollOffset;
            button->setText( m_data->candidates[ index ] );
        }

        button->setVisible( true );
    }

    for ( int i = count; i < m_data->buttonCount; ++i )
        m_data->layoutBox->itemAtIndex( i )->setVisible( false );
}

void QskInputPredictionBar::buttonClicked()
{
    const int index = m_data->layoutBox->indexOf(
        qobject_cast< QQuickItem* > ( sender() ) );

    const int offset = m_data->scrollOffset;

    if ( index == 0 )
    {
        if ( offset > 0 )
        {
            setScrollOffset( offset - 1 );
            return;
        }
    }
    else if ( index == m_data->buttonCount - 1 )
    {
        if ( m_data->candidates.count() - offset > m_data->buttonCount )
        {
            setScrollOffset( offset + 1 );
            return;
        }
    }

    Q_EMIT predictiveTextSelected( offset + index );
}

#include "moc_QskInputPredictionBar.cpp"

Updated on 28 July 2023 at 14:02:30 CEST