Hello again, I'm having some trouble with a resource class that contains a debug window with a fully functional scrolling system. It also adjusts itself to new screen (RenderWindow) sizes. Here's my problem and how to reproduce it:
[TO REPRODUCE] : Compile/Build/Run in either Debug or Release mode. Resize the window, again, and again... and sometimes, the text will completely disappear from the debug window. The scroll-bar will still be there tho. LaurentGomila-SFML-2b3d9bd (SFML-2.0 rc) used. Code Blocks 10.05 used. Win7 x86 used.
Normal:
(http://img694.imageshack.us/img694/8403/b4e.png)
Resized, this happens:
(http://img707.imageshack.us/img707/923/15453128.png)
Here's the complete code:
//camelCase all the way!
#include <SFML/Graphics.hpp> //Obvious header
#include <string> //std::string
#include <iostream> //std::cin && std::cout
//#include <vector> //std::vector<type>
#include <cstdio> //std::getchar() (Just to pause an element, it's better than using std::cin >> to pause because you don't need to declare a variable)
#include <deque> //std::deque<type> (Stores sf::Text), I guess I should replace it with pointers to sf::Text)
#include <fstream> //std::ofstream
#include <sstream> //std::stringstream << number << string .str().c_str, used for log + number + .txt
/// A CLASS FOR COMPLETE RESOURCE CONTROL PER WINDOW:
/// resources.hpp
class resources
{
///Debugger information and methods:
sf::RectangleShape debugBar; //The little scroll bar on the right side. Size_x = debugWindow.getSize().x/20?, size_y defined by amount of text. Can be modded to be click-drag-able
std::deque<sf::Text> debugInfo; //push the front, pop the back.
sf::RenderTexture debugWindow; //The actual window; size_x = window.getSize().x, size_y = window.getSize().y/3
void debugUpdate(bool scrolled); //Called after ever << input, as well as scrollDebug
void debugLog(); //Called if the size of debugInfo becomes too large, takes a chunk and puts it in a log file. File is cleaned on startup. File named after the instance-number of this class e.g.: "log2.txt"
sf::Font debugFont;
int debugCharacterSize, scroll;
int debugThisInstance; //Based off the static debugInstance
float debugTextsPerWindow, debugTextH, debugWindowY;
static int debugInstance; //Used for the naming of logs
bool debugVisible;
///Debugger information end.
int windowSizeX, windowSizeY, windowBitsPerPixel;
std::string windowTitle;
sf::View view;
public:
resources(); //Ctor initialized from the same folder the binaries are in. Currently replacement for dirty, quick initialization
resources(std::string& rootFolder); //Ctor loads settings from a specific folder, that's how I'd like to handle different windows and their respective settings
~resources();
void resize(); //Also modifies the debugging field
///To do with debugging:
void operator << (std::string debugMessage); //Merely adds a message to the deque
void operator << (const char * debugMessage);
void debugScroll(bool upOrDown); //The boolean specifies up or down
inline void debugDraw(); //Function assumes you have already cleared the window, and that you will display it afterwards. Should always be the last draw();
void debugToggle();
///End of debugging specifications
sf::Event event;
sf::RenderWindow window;
};
/// resources.cpp
int resources::debugInstance = 0;
//Private member functions:
void resources::debugUpdate(bool scrolled = false)
{
if (debugInfo.size() >= 100) //If the size is this large, it'll take a chunk of 20 items and store it, then continue
debugLog();
if (!scrolled)
scroll = 0;
debugWindow.clear(sf::Color(127,127,127,127));
for (int i = scroll; i < debugInfo.size(); i++)
{
debugInfo[i].setPosition(0.f, (debugWindow.getSize().y - (debugInfo[(i)].getGlobalBounds().height+5)) - debugInfo[i].getGlobalBounds().height*(i-scroll));
debugWindow.draw(debugInfo[(i)]);
}
debugBar.setSize(sf::Vector2f(debugBar.getSize().x, debugWindowY * (debugTextsPerWindow/debugInfo.size()))); ///sets a level of percentage, fully represents one screen
debugBar.setPosition(sf::Vector2f(windowSizeX - debugBar.getGlobalBounds().width, (debugWindow.getSize().y - debugBar.getGlobalBounds().height) - (scroll/debugTextsPerWindow)*(debugBar.getGlobalBounds().height) )); ///Okay, so I can change position based on amount of texts and current scroll position.
debugWindow.draw(debugBar);
debugWindow.display();
}
void resources::debugLog() //Operation takes 4.2~0.x ms on a bad school-computer, 5200RPM HDD, 320GB
{
std::stringstream filename;
filename << "log" << debugThisInstance << ".txt";
std::ofstream log(filename.str().c_str(), std::ios::app);
if (log.is_open())
{
for (int i = debugInfo.size()-1; i > (debugInfo.size()-80) && i > 0; i--)
{
log << (std::string)debugInfo[i].getString() << '\n';
debugInfo.pop_back();
}
}
else
std::cout << std::string("Error saving debug info to log file!\n");
//If we were to do (*this) << "...", and the file could not be made/opened, we'd end up with a massive, recursive spam!
//Eventually, the call stack would overflow, the program would terminate.
}
//Public member functions:
resources::resources()
{
debugInstance++;
debugThisInstance = debugInstance;
debugCharacterSize = 10;
scroll = 0;
windowSizeX = 400;
windowSizeY = 300;
windowBitsPerPixel = 32;
windowTitle = "Debugger Test!";
///DEBUGGER INFORMATION:
debugFont.loadFromFile("visitor1.ttf"); //Quickly load a font file.
debugWindow.create(windowSizeX, windowSizeY/3);
debugWindow.clear(sf::Color(127,127,127,127));
debugWindow.display();
debugVisible = true;
debugBar.setSize(sf::Vector2f(windowSizeX/50, 0));
debugBar.setFillColor(sf::Color(180,180,180,127));
debugWindowY = (float) debugWindow.getSize().y;
*this << " Debugger initialization";
debugTextH = (float) debugInfo[0].getGlobalBounds().height;
debugTextsPerWindow = debugWindowY / debugTextH;
debugUpdate();
///END DEBUGGER INFORMATION
window.create(sf::VideoMode(windowSizeX,windowSizeY,windowBitsPerPixel),windowTitle.c_str(), sf::Style::Default);
}
resources::resources(std::string& rootFolder)
{
}
resources::~resources()
{
}
void resources::resize()
{
windowSizeX = window.getSize().x;
windowSizeY = window.getSize().y;
view = sf::View(sf::FloatRect(0,0,windowSizeX, windowSizeY));
window.setView(view);
///We need to re-initialize some parameters of the debug-texture:
debugWindow.create(windowSizeX,windowSizeY/3);
debugBar.setSize(sf::Vector2f(windowSizeX/50, 0));
debugWindowY = (float) debugWindow.getSize().y;
debugTextH = (float) debugInfo[0].getGlobalBounds().height;
debugTextsPerWindow = debugWindowY / debugTextH;
debugUpdate();
std::stringstream windowSizes; windowSizes << " Window resized: " << windowSizeX << "x" << windowSizeY;
*this << windowSizes.str();
}
//Operator << for the debug window
void resources::operator << (std::string debugMessage)
{
debugInfo.push_front(sf::Text(debugMessage, debugFont, debugCharacterSize));
debugUpdate();
}
void resources::operator << (const char * debugMessage)
{
*this << std::string(debugMessage);
}
void resources::debugScroll(bool upOrDown = true)
{
if (upOrDown && scroll < debugInfo.size()-debugTextsPerWindow && debugInfo.size() > debugTextsPerWindow)
scroll++;
else if (scroll > 0 && !upOrDown)
scroll--;
debugUpdate(true);
}
inline void resources::debugDraw()
{
if (debugVisible)
window.draw(sf::Sprite(debugWindow.getTexture()));
}
void resources::debugToggle()
{
if (debugVisible)
*this << " Closing debug window";
else
*this << " Opening debug window";
debugVisible = !debugVisible;
}
/// RESOURCE CLASS.
int main()
{
resources rsc;
try
{
//A simple loop:
while (true)
{
if (rsc.window.waitEvent(rsc.event))
{
switch (rsc.event.type)
{
case sf::Event::Resized:
rsc.resize();
break;
case sf::Event::KeyPressed:
switch (rsc.event.key.code)
{
case sf::Keyboard::Escape:
return 0;
case sf::Keyboard::Insert:
rsc.debugToggle();
break;
case sf::Keyboard::O:
rsc.debugScroll();
break;
case sf::Keyboard::E:
rsc.debugScroll(false);
break;
case sf::Keyboard::Up:
rsc << " Up pressed";
break;
case sf::Keyboard::Down:
rsc << " Down pressed";
break;
case sf::Keyboard::Left:
rsc << " Left pressed";
break;
case sf::Keyboard::Right:
rsc << " Right pressed";
break;
default: rsc << " unknown button pressed"; break;
}
break;
case sf::Event::Closed:
return 0;
default: break;
}
}
rsc.window.clear(sf::Color::Black);
rsc.debugDraw();
rsc.window.display();
}
}
catch (const char * error)
{
std::cout << "Error: " << error << std::endl << "Press enter to exit.";
std::getchar();
}
std::getchar();
return 0;
}
http://fontgal.com/font/10468-visitor
is the font used.
Press 'O' to scroll up (Only works if there's more text than the window)
Press 'E' to scroll down.
Press any arrow key to lodge a respective message.
Insert toggles the debug window.
Press any other key to lodge a standard message.
I've spent several hours tracing it, I searched the web, this forum, no hits. So I am forced to ask it here. My apologies if the question is already answered and I didn't find it.
I see. But keep in mind that it really will only show new texts when an input happens, thus you if you print out something that isn't directly connected to an event you might get a delay until a new event occurs.
Aaahh, I know what you mean.
window.clear();
debugDraw();
window.display();
added to debugUpdate(bool); thanks, useful if a message is lodged without any event to trigger it.
However, the problem of disappearing texts still persists.
Ah I now just saw the pictures (didn't show before) in your first post and I see that your scrollbar is located on the right, whereas I see the scrollbar in the beginning on the left (see picture).
Maybe there is something wrong with your debugUpdate(bool) function. I shall re-paste it here:
Edit: I edited the first post to include the pictures. So it's not your fault for not spotting them.
void resources::debugUpdate(bool debugScrolled = false)
{
if (debugInfo.size() >= 100) //If the size is this large, it'll take a chunk of 20 items and store it, then continue
debugLog();
if (!debugScrolled)
debugScroll = 0;
debugWindow.clear(sf::Color(127,127,127,127));
for (int i = debugScroll; i < debugInfo.size(); i++)
{
debugInfo[i].setPosition(0.f, (debugWindow.getSize().y - (debugInfo[(i)].getGlobalBounds().height+5)) - debugInfo[i].getGlobalBounds().height*(i-debugScroll));
debugWindow.draw(debugInfo[(i)]);
}
debugBar.setSize(sf::Vector2f(debugBar.getSize().x, debugWindowY * (debugTextsPerWindow/debugInfo.size()))); ///sets a level of percentage, fully represents one screen
debugBar.setPosition(sf::Vector2f(windowSizeX - debugBar.getGlobalBounds().width, (debugWindow.getSize().y - debugBar.getGlobalBounds().height) - (debugScroll/debugTextsPerWindow)*(debugBar.getGlobalBounds().height) )); ///Okay, so I can change position based on amount of texts and current debugScroll position.
debugWindow.draw(debugBar);
debugWindow.display();
window.clear();
debugDraw();
window.display();
}
If you care to help me finding out why the debugBar doesn't show correctly with you I'd be grateful. (I'd like this to work on different computers).
I'm back with the absolute minimum, but complete example that reproduces the issue.
#include <SFML/Graphics.hpp>
int main()
{
sf::RenderWindow window;
window.create(sf::VideoMode(400,300,32), "Test", sf::Style::Default);
sf::Event event;
sf::RenderTexture rt;
sf::Sprite sp;
sf::Font font;
font.loadFromFile("visitor1.ttf");
rt.create(window.getSize().x, window.getSize().y/3.f);
rt.clear(sf::Color(127,127,127,127));
sp.setTexture(rt.getTexture(), true);
sf::Text text;
text.setString(" Initialization Complete");
text.setFont(font);
rt.draw(text);
rt.display();
sf::View view;
while (window.isOpen())
{
while (window.pollEvent(event))
{
switch (event.type)
{
case sf::Event::KeyPressed:
window.setSize(sf::Vector2u(std::rand()%1166+200, std::rand()%618+150));
view.setSize(window.getSize().x, window.getSize().y);
view.setCenter(window.getSize().x/2.f, window.getSize().y/2.f);
window.setView(view);
rt.create(window.getSize().x, window.getSize().y/3.f);
rt.clear(sf::Color(127,127,127,127));
rt.draw(text);
rt.display();
sp.setTexture(rt.getTexture(), true);
break;
case sf::Event::Closed:
window.close();
return 0;
default: break;
}
}
window.clear();
window.draw(sp);
window.display();
}
return 0;
}
Doesn't matter if you use waitEvent, or set a framerateLimit(), if or while for the event loop. It will always, out of complete randomness, after having resized quickly or slowly (the input per second), regardless of specific window size, regardless of manually resizing with the mouse (you gotta have an event, I deleted the mouse event because it's not necessary) or with an in-game event, the text will sometimes simply not appear in the RenderTexture.
Try adding an sf::CircleShape, and draw it into the RenderTexture at resize, the circle will be present even if the text disappears.
I have updated SFML-2.0 to the latest release candidate: "LaurentGomila-SFML-757a785" 22.08.2012 UCT+1 @ 10PM.