I tried to create a qspinbox64 as suggested in
64bit int Spin Box in QT
qspinbox64.h
#define QSPINBOX64_H
#include <QtGui>
#include <QtWidgets>
namespace Ui {
class QSpinBox64;
}
class QSpinBox64Private;
class Q_WIDGETS_EXPORT QSpinBox64 : public QAbstractSpinBox//QSpinBox
{
Q_OBJECT
Q_PROPERTY(int64_t minimum READ minimum WRITE setMinimum)
Q_PROPERTY(int64_t maximum READ maximum WRITE setMaximum)
Q_PROPERTY(int64_t value READ value WRITE setValue NOTIFY valueChanged USER true)
int64_t m_minimum;
int64_t m_maximum;
int64_t m_value;
public:
explicit QSpinBox64(QWidget *parent = nullptr)
{
connect(lineEdit(), SIGNAL(textEdited(QString)), this, SLOT(onEditFinished()));
}
~QSpinBox64()
{
}
int64_t value() const
{
return m_value;
}
int64_t minimum() const
{
return m_minimum;
}
int64_t maximum() const
{
return m_maximum;
}
void setMinimum(int64_t min)
{
m_minimum = min;
}
void setMaximum(int64_t max)
{
m_maximum = max;
}
void setRange(int64_t min, int64_t max)
{
setMinimum(min);
setMaximum(max);
}
virtual void stepBy(int steps)
{
auto new_value = m_value;
if (steps < 0 && new_value + steps > new_value) {
new_value = std::numeric_limits<qlonglong>::min();
}
else if (steps > 0 && new_value + steps < new_value) {
new_value = std::numeric_limits<qlonglong>::max();
}
else {
new_value += steps;
}
lineEdit()->setText(textFromValue(new_value));
setValue(new_value);
}
protected:
virtual QValidator::State validate(QString &text, int &pos) const
{
//return validator->validate(text, pos);
bool ok;
int64_t val = text.toLongLong(&ok);
if (!ok)
return QValidator::Invalid;
if (val < m_minimum || val > m_maximum)
return QValidator::Invalid;
return QValidator::Acceptable;
}
virtual int64_t valueFromText(const QString &text) const
{
bool ok;
return text.toLongLong(&ok, 10);
}
virtual QString textFromValue(int64_t value) const
{
return QString::number(value, 10).toUpper();
}
virtual QAbstractSpinBox::StepEnabled stepEnabled() const;
public
Q_SLOTS:
void setValue(int64_t val)
{
if (m_value != val) {
lineEdit()->setText(textFromValue(val));
m_value = val;
}
}
void onEditFinished()
{
QString input = lineEdit()->text();
int pos = 0;
if (QValidator::Acceptable == validate(input, pos))
setValue(valueFromText(input));
else
lineEdit()->setText(textFromValue(m_value));
}
Q_SIGNALS:
void valueChanged(int64_t v);
private:
Ui::QSpinBox64 *ui;
Q_DISABLE_COPY(QSpinBox64)
Q_DECLARE_PRIVATE(QSpinBox64)
};
#endif
main.cpp
#include <QHBoxLayout>
#include "qspinbox64.h"
int main(int argc, char *argv[])
{
QApplication app(argc, argv);
QSpinBox64 spinBox;
spinBox.setWindowTitle(QObject::tr("QSpinBox64"));
spinBox.show();
return app.exec();
}
An error occurs after compilation:
G:\proj\build-qspinbox64-Desktop_Qt_5_15_2_MSVC2019_64bit-Debug\debug\moc_qspinbox64.cpp:141:
error: C2491: 'QSpinBox64::staticMetaObject': definition of dllimport static data member not allowed
What should I do to avoid this error?
The culprit is Q_WIDGETS_EXPORT, defined in qtwidgetsglobal.h
# if defined(QT_BUILD_WIDGETS_LIB)
# define Q_WIDGETS_EXPORT Q_DECL_EXPORT
# else
# define Q_WIDGETS_EXPORT Q_DECL_IMPORT
# endif
You should declare QT_BUILD_WIDGETS_LIB in your project by adding this line to the .pro file
DEFINES += QT_BUILD_WIDGETS_LIB
Some side notes:
You are missing the header guard #ifndef QSPINBOX64_H
The constructor should call the base class constructor and set the min/max as instructed in the original post.
stepEnabled() needs to be implemented
explicit QSpinBox64(QWidget *parent = nullptr) : QAbstractSpinBox{parent}
{
setMaximum(std::numeric_limits<int64_t>::max());
setMinimum(std::numeric_limits<int64_t>::min());
connect(lineEdit(), SIGNAL(textEdited(QString)), this, SLOT(onEditFinished()));
}
virtual QAbstractSpinBox::StepEnabled stepEnabled() const
{
return QAbstractSpinBox::StepUpEnabled | QAbstractSpinBox::StepDownEnabled;
}
Related
I'm recently learning to make a 2d game in SFML using a tutorial series on youtube by Suraj Sharma(Video 57):
https://www.youtube.com/watch?v=kwd_AVCkvXE&list=PL6xSOsbVA1ebkU66okpi-KViAO8_9DJKg&index=57
His Source Code:
https://github.com/Headturna/SFML_RPG
Right now he's teaching me to symplify the game's menu system by making a mini class 'StateData' in the parent class 'State' so any inherited class can access 'State' parameters via 'StateData'(Ex:MainMenu(StData* Stdata){}).
After debugging the game seems to runs fine.But everytime i click on something on the menu(start,settings,etc...) a 'Read access violation:Stdata was nullptr' occurs in the 'State' class constructor.
Here's the code:
State.h:
#pragma once
#ifndef STATE_H
#define STATE_H
#include "Player.h"
#include "GrphSettings.h"
class Player;
class GrphSettings;
class State;
class StData {
public:
StData(){}
//Vars
float GridSize;
sf::RenderWindow* Window;
GrphSettings* GSettings;
std::map<std::string, int>* SupportedKeys;
std::stack<State*>* states;
};
class State
{
private:
protected:
StData* Stdata;
std::stack<State*>* states;
sf::RenderWindow* window;
std::map<std::string, int>* SupportedKeys ;
std::map<std::string, int> Keybinds;
bool quit;
bool pause;
float keyTime;
float keyTimeMax;
float GridSize;
sf::Vector2i MousePosScr;
sf::Vector2i MousePosWind;
sf::Vector2f MousePosView;
//Resources
std::map<std::string,sf::Texture> texture;
//Funcs
virtual void InitKeybinds() = 0;
public:
State(StData* Stdata);
virtual~State();
//Access
const bool getKeytime();
const bool& getquit()const;
//Funcs
void Endstate();
void PauseSt();
void UnPauseSt();
virtual void UpdateInput(const float& dt) = 0;
virtual void UpdateMousePos();
virtual void UpdateKeyTime(const float& dt);
virtual void Update(const float& dt) = 0;
virtual void Render(sf::RenderTarget* target = nullptr) = 0;
};
#endif // !1
State.cpp:
#include "pch.h"
#include "State.h"
State::State(StData* Stdata)
{
this->Stdata = Stdata;
this->window = Stdata->Window;
this->SupportedKeys = Stdata->SupportedKeys;
this->states = Stdata->states;
this->quit = false;
this->pause = false;
this->keyTime = 0.f;
this->keyTimeMax = 10.f;
this->GridSize = Stdata->GridSize;
}
State::~State()
{
}
//Access
const bool State::getKeytime()
{
if (this->keyTime >= this->keyTimeMax) {
this->keyTime = 0.f;
return true;
}
return false;
}
const bool& State::getquit() const
{
// TODO: insert return statement here
return this->quit;
}
//Funcs
void State::Endstate()
{
this->quit = true;
}
void State::PauseSt()
{
this->pause = true;
}
void State::UnPauseSt()
{
this->pause = false;
}
void State::UpdateMousePos()
{
this->MousePosScr = sf::Mouse::getPosition();
this->MousePosWind = sf::Mouse::getPosition(*this->window);
this->MousePosView = this->window->mapPixelToCoords(sf::Mouse::getPosition(*this->window));
}
void State::UpdateKeyTime(const float& dt)
{
if (this->keyTime < this->keyTimeMax)
this->keyTime += 100.f * dt;
}
Every button one the menu(start,exit...)is an inherited state from 'State' class.This is,for example,the 'Edit' state.
Edit.h:
#pragma once
#ifndef EDIT_H
#define EDIT_H
#include "State.h"
#include "Gui.h"
#include "PauseMenu.h"
#include "TileMap.h"
class State;
class Gui;
class PauseMenu;
class TileMap;
class Edit:public State
{
private:
//Vars
sf::Font Fnt;
PauseMenu* PMenu;
std::map<std::string, gui::Button*> buttons;
TileMap Map;
//Functions
void InitVars();
void InitBackGrnd();
void InitFonts();
void InitKeybinds();
void InitPauseMenu();
void InitBtn();
public:
Edit(StData* Stdata);
virtual~Edit();
//Functions
void UpdateInput(const float& dt);
void Update(const float& dt);
void UpdatePButtons();
void UpdateBtn();
void Endstate();
void RenderBtn(sf::RenderTarget& target);
void Render(sf::RenderTarget* target = nullptr);
};
#endif // ! EDIT_H
Edit.cpp:
#include "pch.h"
#include "Edit.h"
void Edit::InitVars()
{
}
void Edit::InitBackGrnd()
{
}
void Edit::InitFonts()
{
if (!this->Fnt.loadFromFile("Fonts/SPACEMAN.ttf")) {
throw("Error::Edit::Couldn't load font");
}
}
void Edit::InitKeybinds()
{
std::ifstream ifs("Config/EditKeys.ini");
if (ifs.is_open()) {
std::string key = "";
std::string key2 = "";
int keyval = 0;
while (ifs >> key >> key2)
{
this->Keybinds[key] = this->SupportedKeys->at(key2);
}
}
ifs.close();
this->Keybinds["Close"] = this->SupportedKeys->at("ESC");
this->Keybinds["Left"] = this->SupportedKeys->at("A");
this->Keybinds["Right"] = this->SupportedKeys->at("D");
this->Keybinds["Up"] = this->SupportedKeys->at("W");
this->Keybinds["Down"] = this->SupportedKeys->at("S");
}
void Edit::InitPauseMenu()
{
this->PMenu = new PauseMenu(*this->window, this->Fnt);
this->PMenu->addButtons("Quit", 800.f, "Quit");
}
void Edit::InitBtn()
{
}
Edit::Edit(StData* Stdata)
:State(Stdata)
{
this->InitVars();
this->InitBackGrnd();
this->InitFonts();
this->InitKeybinds();
this->InitPauseMenu();
this->InitBtn();
}
Edit::~Edit()
{
auto it = this->buttons.begin();
for (it = this->buttons.begin(); it != this->buttons.end(); ++it) {
delete it->second;
}
delete this->PMenu;
}
//Funcs
void Edit::UpdateInput(const float& dt)
{
if (sf::Keyboard::isKeyPressed(sf::Keyboard::Key(this->Keybinds.at("Close")))
&& this->getKeytime()) {
if (!this->pause)
this->PauseSt();
else this->UnPauseSt();
}
}
void Edit::Update(const float& dt)
{
this->UpdateMousePos();
this->UpdateKeyTime(dt);
this->UpdateInput(dt);
if (!this->pause) {//Unpaused
this->UpdateBtn();
}
else {//Paused
this->PMenu->Update(this->MousePosView);
this->UpdatePButtons();
}
this->UpdateBtn();
std::cout << this->MousePosView.x << " " << this->MousePosView.y << "\r";
}
void Edit::UpdatePButtons()
{
if (this->PMenu->isPressed("Quit"))
this->Endstate();
}
void Edit::UpdateBtn()
{ //Update buttons and handle their functions
for (auto& it : this->buttons) {
it.second->Update(MousePosView);
}
}
void Edit::Endstate()
{
std::cout << "Ending State" << "\n";
}
void Edit::RenderBtn(sf::RenderTarget& target)
{
for (auto& it : this->buttons) {
it.second->Render(target);
}
}
void Edit::Render(sf::RenderTarget* target)
{
if (!target)
target = this->window;
this->RenderBtn(*target);
this->Map.Render(*target);
if (this->pause) {//Pause Menu
this->PMenu->Render(*target);
}
sf::Text MText;
MText.setPosition(this->MousePosView);
MText.setFont(this->Fnt);
MText.setCharacterSize(12);
std::stringstream ss;
ss << this->MousePosView.x << ' ' << this->MousePosView.y;
MText.setString(ss.str());
target->draw(MText);
}
Can anyone help me ?
I have a Timer class that calls function pointer on timeout. The problem that this Times should be able to be copied (instances of it can be swapped in array). After copying the m_callbackOwner became invalid.
So, how to deal with this problem? How to implement callbacks to allow copying?
#pragma once
#include "common/Function.h"
#include "helpers/Utils.h"
template<typename TimeoutCallbackOwner, typename TimeoutCallback>
class Timer
{
public:
Timer() : m_interval(0), m_value(0), m_callbackFunction(nullptr) {}
void setCallback(TimeoutCallbackOwner *cbOwner, TimeoutCallback cb)
{
assert(cbOwner && cb);
m_callbackOwner = cbOwner;
m_callbackFunction = cb;
}
void update(double dt)
{
if (m_interval < 0 || isEqual(m_interval, 0))
{
return;
}
m_value += dt;
if (m_value >= m_interval)
{
m_value = 0;
if (m_callbackOwner, m_callbackFunction)
CALL_MEMBER_FN(m_callbackOwner, m_callbackFunction);
}
}
void setInterval(double interval)
{
m_interval = interval;
m_value = 0;
}
double interval() const
{
return m_interval;
}
double value() const
{
return m_value;
}
bool isRunning() const
{
return m_value != 0;
}
private:
double m_interval;
double m_value;
TimeoutCallbackOwner * m_callbackOwner;
TimeoutCallback m_callbackFunction;
};
QMetaEnum contains method to convert enum index to actual value:
int value(int index) const
But how to convert back to index, for example, if I want to use enum in some control where I need sequence by index?
int index(int value) const
?
Use the following function:
int indexFromValue(const QMetaEnum & e, int value){
for(int ix=0; ix< e.keyCount(); ix++){
if(e.key(ix) == e.valueToKey(value))
return ix;
}
return -1;
}
Example:
#include <QCoreApplication>
#include <QMetaEnum>
#include <QObject>
class Foo : public QObject
{
Q_OBJECT
public:
using QObject::QObject;
enum class FooEnumType { TypeA=10, TypeB=21 };
Q_ENUM(FooEnumType)
};
static int indexFromValue(const QMetaEnum & e, int value){
for(int ix=0; ix< e.keyCount(); ix++){
if(e.key(ix) == e.valueToKey(value))
return ix;
}
return -1;
}
int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);
const QMetaObject &mo = Foo::staticMetaObject;
int index = mo.indexOfEnumerator("FooEnumType");
QMetaEnum metaEnum = mo.enumerator(index);
Q_ASSERT(indexFromValue(metaEnum, 10) == 0);
Q_ASSERT(indexFromValue(metaEnum, 21) == 1);
Q_ASSERT(indexFromValue(metaEnum, 100) == -1);
return 0;
}
#include "main.moc"
I'm new to Qt, so I'm not sure what to use.
I thought I would have a QStringList to give to my QStringListModel and display it in a ListView.
Now, however, I need to divide QStringList in the values of 2 types. So, I need to have string + some typeId, not just one string, but QStringList is for one-dimensional list only.
Anyone could give an advice on what is the best way to try to implement this?
The solution is to use QAbstractListModel subclass as a Qt Quick model. An example of base class for a models (I use it for convenience):
// abstractobjectlistmodel.h
#pragma once
#include <QtCore>
struct AbstractObjectListModel
: public QAbstractListModel
{
explicit AbstractObjectListModel(QObject * const parent = Q_NULLPTR)
: QAbstractListModel{parent}
{ ; }
int rowCount(QModelIndex const & parent = {}) const Q_DECL_OVERRIDE Q_DECL_FINAL
{
Q_UNUSED(parent);
return items.count();
}
QVariant data(QModelIndex const & index, const int role = Qt::DisplayRole) const Q_DECL_OVERRIDE Q_DECL_FINAL
{
if (!index.isValid()) {
return {};
}
switch (role) {
case Qt::UserRole : {
return QVariant::fromValue(items[index.row()].data());
}
default : {
return {};
}
}
}
QHash< int, QByteArray > roleNames() const Q_DECL_OVERRIDE Q_DECL_FINAL
{
auto roleNames = QAbstractListModel::roleNames();
roleNames.insert(Qt::UserRole, "modelData");
return roleNames;
}
Q_INVOKABLE
virtual
QObject * get(int row) const
{
if (row < 0) {
return {};
}
if (row >= rowCount()) {
return {};
}
return items[row];
}
void remove(int row, int count = 1)
{
Q_ASSERT(count > 0);
Q_ASSERT(row >= 0);
Q_ASSERT(row + count <= rowCount());
beginRemoveRows({}, row, row + count - 1);
while (0 < count) {
items.takeAt(row)->deleteLater();
--count;
}
endRemoveRows();
}
void clear()
{
if (!items.isEmpty()) {
remove(0, rowCount());
}
}
protected :
~AbstractObjectListModel() Q_DECL_OVERRIDE Q_DECL_EQ_DEFAULT; // derived classes should not meant to be manipulated polymorphically
QList< QPointer< QObject > > items;
void insert(int row, QObject * const item)
{
item->setParent(this);
beginInsertRows({}, row, row);
items.insert(row, item);
endInsertRows();
}
void append(QObject * const item)
{
insert(rowCount(), item);
}
};
But one need to override get to access items' Q_PROPERTY properties (in addition to dynamic ones):
// type of element
class Project
: public QObject
{
Q_OBJECT
Q_PROPERTY(QString name MEMBER name NOTIFY nameChanged)
Q_PROPERTY(QString path MEMBER path NOTIFY pathChanged)
public :
Project(QString name, QString path,
QObject * const parent = Q_NULLPTR)
: QObject{parent}
, name{name}
, path{path}
{ ; }
Q_SIGNALS :
void nameChanged(QString name);
void pathChanged(QString path);
private :
QString name;
QString path;
};
// custom model
class ProjectsListModel
: public AbstractObjectListModel
{
Q_OBJECT
public :
explicit ProjectsListModel(QObject * const parent = Q_NULLPTR)
: AbstractObjectListModel{parent}
{ ; }
void appendProject(QString name, QString path)
{
AbstractObjectListModel::append(::new Project{name, path});
}
Q_INVOKABLE
Project *
get(int row) const Q_DECL_OVERRIDE
{
return qobject_cast< Project * >(AbstractObjectListModel::get(row));
}
};
Before use one need to register concrete model with qmlRegisterType< ProjectsListModel >();. Properties of Project class are avaliable in delegate and highlight by means of members of modelData.
Another example:
struct TimeZoneModel Q_DECL_FINAL
: public QAbstractListModel
{
Q_OBJECT
public :
explicit TimeZoneModel(QObject * const parent = Q_NULLPTR)
: QAbstractListModel{parent}
{ ; }
int rowCount(QModelIndex const & parent = {}) const Q_DECL_OVERRIDE
{
Q_UNUSED(parent);
return timeZoneIds.count();
}
QVariant data(QModelIndex const & index, const int role = Qt::DisplayRole) const Q_DECL_OVERRIDE
{
if (!index.isValid() || (role > Qt::UserRole + 4)) {
return {};
}
QTimeZone timeZone{timeZoneIds[index.row()]};
if (!timeZone.isValid()) {
return {};
}
return roleData(timeZone, role);
}
QHash< int, QByteArray > roleNames() const Q_DECL_OVERRIDE
{
auto roleNames = QAbstractListModel::roleNames();
int i = Qt::UserRole;
for (const auto role : {"modelData", "id", "comment", "name", "country"}) {
roleNames.insert(i++, role);
}
return roleNames;
}
Q_INVOKABLE
QByteArray get(int row) const
{
if (row < 0) {
return {};
}
if (row >= rowCount()) {
return {};
}
return timeZoneIds[row];
}
private :
QVariant roleData(QTimeZone const & timeZone, int role = Qt::UserRole) const
{
switch (role) {
case Qt::UserRole : {
QVariantMap modelData;
const auto names = roleNames();
while (++role < Qt::UserRole + 5) {
modelData.insert(QString::fromUtf8(names[role]), roleData(timeZone, role));
}
return modelData;
}
case Qt::UserRole + 1: {
return QString::fromUtf8(timeZone.id());
}
case Qt::UserRole + 2 : {
return timeZone.comment();
}
case Qt::UserRole + 3 : {
return timeZone.displayName(QTimeZone::StandardTime);
}
case Qt::UserRole + 4 : {
return QLocale::countryToString(timeZone.country());
}
default : {
return {};
}
}
}
const QByteArrayList timeZoneIds = QTimeZone::availableTimeZoneIds();
};
In addition to access via modelData's fields (modelData.id, modelData.comment etc) all the symbols are accessible directly (i.e. id, comment etc) in delegate and highlight contexts of the ListView.
Model TimeZoneModel is const and can be injected into global scope directly without any performance drawbacks:
QQmlApplicationEngine engine;
TimeZoneModel timeZoneModel;
Q_SET_OBJECT_NAME(timeZoneModel);
qmlRegisterType< TimeZoneModel >();
const auto rootContext = engine.rootContext();
rootContext->setContextProperty(timeZoneModel.objectName(), &timeZoneModel);
If you need dictionary that contains QString and any other type I suggest you to use
QMap<QString, YourType> myMap;
Here you have some example of usage:
QMap<int, QString> myMap;
myMap.insert(1,"A");
myMap.insert(2,"B");
myMap[3] = "C";
foreach(int i, myMap.keys()) qDebug() << myMap[i];
I'm having trouble drawing a custom sf::Drawable derived object.
//Textbox.h
#pragma once
#include "Header.h"
#ifndef TEXTBOX_H
#define TEXTBOX_H
class Textbox : public Drawable {
public:
Textbox(int max_chars, bool numeric);
Textbox(int max_chars);
Textbox(bool numeric);
Textbox();
void setTextColor(Color color);
void setPosition(float x, float y);
Vector2f getPosition() {
return m_gshape.getPosition();
}
Vector2f getSize();
String getString();
void setFocus(bool value);
bool isFocused();
void input(Uint32 text_char);
void clear();
private:
virtual void Textbox::draw(sf::RenderTarget& target, sf::RenderStates states) const {
target.draw(m_gshape, states);
target.draw(m_textbox, states);
}
unsigned int max_length;
int min_ascii = 32;
int max_ascii = 127;
bool focus;
string content;
Text m_textbox;
RectangleShape m_gshape;
};
#endif // !TEXTBOX_H
And
//Textbox.cpp
#pragma once
#include "Textbox.h"
Textbox::Textbox(int max_chars, bool numeric) {
max_length = max_chars;
m_gshape.setSize(Vector2f(6 + 15 * max_length, 30));
m_gshape.setFillColor(Color::White);
m_gshape.setOutlineThickness(2);
m_gshape.setOutlineColor(Color(60, 60, 60));
m_gshape.setPosition(0, 0);
m_textbox.setFont(default_font);
m_textbox.setCharacterSize(25);
m_textbox.setFillColor(Color::White);
if (max_chars > 1)
m_textbox.setString(to_string((int)pow(10, max_chars - 1)));
else
m_textbox.setString("0");
if (numeric) {
min_ascii = 47;
max_ascii = 58;
}
}
Textbox::Textbox(int max_chars) : Textbox(max_chars, false) {}
Textbox::Textbox(bool numeric) : Textbox(2, numeric) {}
Textbox::Textbox() : Textbox(2, false) {}
void Textbox::setTextColor(Color color) {
m_textbox.setFillColor(color);
}
void Textbox::setPosition(float x, float y) {
FloatRect textbox_bounds = m_textbox.getGlobalBounds();
m_gshape.setPosition(x, y);
m_textbox.setPosition(m_gshape.getPosition().x + (m_gshape.getSize().x - textbox_bounds.width) / 2 - textbox_bounds.left,
m_gshape.getPosition().y + (m_gshape.getSize().y - textbox_bounds.height) / 2 - textbox_bounds.top);
}
Vector2f Textbox::getSize() {
return m_gshape.getSize();
}
String Textbox::getString() {
return m_textbox.getString();
}
void Textbox::setFocus(bool value) {
focus = true;
}
bool Textbox::isFocused() {
return focus;
}
void Textbox::input(Uint32 text_char) {
content = m_textbox.getString().toAnsiString();
if (text_char == 13) {
focus = false;
return;
}
if (m_textbox.getString().getSize() < max_length) {
if (text_char > min_ascii && text_char < max_ascii) {
m_textbox.setString(m_textbox.getString() + text_char);
}
}
if (text_char == 8 && m_textbox.getString().getSize() > 0) {
content.resize(m_textbox.getString().getSize() - 1);
m_textbox.setString(content);
}
}
void Textbox::clear() {
m_textbox.setString("");
}
Everything works except for the drawing part: while g_shape gets drawn and rendered m_textbox doesn't. I'm sure of this because I can still edit the text, however it's not displayed.
I must admit I didn't fully understand the sf::Drawable inheritance and consequently I'm not sure I overrid draw() correctly.
Thanks to #AlexMeuer I found the solution.
In my header file I had my global font set as extern sf::Font default_font however in my main.cpp I never declared it.