QSyntaxHighlighter and multiline comments - c++

I am using Qt's QSyntaxHighlighter to color some C like syntax in a QML TextEdit
Everything works great except for multiline comments.
I am detecting them this way :
void highlightBlock(QString const& text) override {
bool inMultilineComment = previousBlockState() == STATES::COMMENT;
bool inSingleLineComment = false;
int previousIndex = 0;
QRegularExpression expr("(\\/\\*|\\*\\/|\\/\\/|\n)"); // will match either /**, /**, // or \n
QRegularExpressionMatchIterator it = expr.globalMatch(text);
while(it.hasNext()) {
QRegularExpressionMatch match = it.next();
const QString captured = match.captured(1);
if(captured == "/*" && !inSingleLineComment) {
inMultilineComment = true;
previousIndex = match.capturedStart(1);
}
if(captured == "*/" && inMultilineComment) {
inMultilineComment = false;
setFormat(previousIndex, match.capturedEnd(1) - previousIndex, _commentFormat);
}
if(captured == "//" && !inMultilineComment) {
inSingleLineComment = true;
}
if(captured == "\n" && inSingleLineComment) {
inSingleLineComment = false;
}
}
if(inMultilineComment) {
setFormat(previousIndex, text.size() - previousIndex, _commentFormat);
setCurrentBlockState(STATES::COMMENT);
}
else {
setCurrentBlockState(STATES::NONE);
}
}
It works until I take a multiline comment already colored and I remove the /* at the begining. Only the block that contains the /* is processed and recolored, but not the following ones, which means that they continue to appear commented when they are not.
Is there an easy way to tell QSyntaxHighlighter to re-process the following blocks to prevent such mis-colorations ?

I ran into this same problem recently and discovered that Qt actually should be handling this for you, assuming that you set your blockState correctly.
If you look at the sourceCode for QSyntaxHighlighterPrivate::reformatBlocks in the Qt5 source code, you'll see
while (block.isValid() && (block.position() < endPosition || forceHighlightOfNextBlock)) {
const int stateBeforeHighlight = block.userState();
reformatBlock(block);
forceHighlightOfNextBlock = (block.userState() != stateBeforeHighlight);
block = block.next();
}
retrieved from https://code.woboq.org/qt5/qtbase/src/gui/text/qsyntaxhighlighter.cpp.html#165
That code (which is fired by a contentsChange signal from the QTextDocument your highlighter is on) will iterate through each block (line) starting from the block that was just modified. Assuming that the state of the block changed based on the typing change that just happened, it will continue to process the following blocks. This means that you need to get your userState correct for every line and Qt should handle the rest.
Given the example
/*
* This is a comment
*
* That I made
*/
You would want to start in the condition where every line had the STATES::COMMENT set except for the last line which should be set to STATES::NONE. Once you do something like deleting the initial /* you need to make sure that the block state is reset to STATES::NONE. That will trigger Qt to rerun the next block, which will also need to change its state, etc.
In my (python) code, I ended up using a combination of print statements and real debugging to track the propagation of state changes and figured out where it was not correctly updating and breaking the chain of updates. Your code looks superficially correct, though I did not try to compile and run it, but I suspect there is a some case being triggered where the state is not being updated correctly after an edit.

Related

How to make a loading screen state transition to game level state?

I am trying to make my basic loading screen transition over to game level screen. So what i wanted to do is, once the loading screen is active (or has appeared onscreen), I want at this point to start loading my game state. What it is doing at the moment is loading everything at the start, and this does take a while.
So currently my project starts off with a main menu. Then when i press enter, its starts the loading screen. I have my manual state change using keypresses like so:
void Game::update()
{
static bool enterPreviouslyPressed = false;
static bool escapePreviousPressed = false;
const Uint8 *keys = SDL_GetKeyboardState(NULL);
if (keys[::SDL_SCANCODE_ESCAPE] && !escapePreviousPressed && typeid(*fsm->getState()) == typeid(GameState))
{
fsm->setState(menuState);
}
else if (keys[::SDL_SCANCODE_RETURN] && !enterPreviouslyPressed && typeid(*fsm->getState()) == typeid(MainMenuState))
{
fsm->setState(loadingState);
}
else if ((keys[::SDL_SCANCODE_RETURN] && !enterPreviouslyPressed) && typeid(*fsm->getState()) == typeid(LoadScreenState))
{
fsm->setState(gameState);
}
else if (keys[::SDL_SCANCODE_ESCAPE] && !escapePreviousPressed && typeid(*fsm->getState()) == typeid(MainMenuState))
{
exit(0);
}
enterPreviouslyPressed = keys[::SDL_SCANCODE_RETURN] != 0;
escapePreviousPressed = keys[::SDL_SCANCODE_ESCAPE] != 0;
fsm->update();
}
I did this to initially does this so i could change states manually to check that everything works. I was wondering if there was an easy(ish) way, like boolean flags for example or another simpler way to do this. I wasn't able find any tutorials online so wondering if someone knows the best solution as to how to do this. I did see a question on here, kindda similar but I wasn't sure if it answered my question as the person did this in threads which I am not familiar with how to implement. Apologies if I dont seem to have the logic correct - so please advise otherwise.
Looks fairly standard, except I would simplify it by keeping two keyboard state variables declared as class variables, like:
const Uint8 *curKeys = SDL_GetKeyboardState(NULL), *prevKeys;
// ...
void Game::update() {
prevKeys = curKeys;
curKeys = = SDL_GetKeyboardState(NULL);
//and so then compare curKeys to prevkeys
//and ditch the booleans
// ...
}

How do I make a custom event with VTK?

I'm making a thread software with VTK, where I need to change the model itself in real time, while I need to change his method of rendering. Everything is working fine, but, the problem start with the interactor->start(); , the model data gets updated just fine, but it's only showed on screen when I move The camera. Also I have selected some methods for generating a 3D data from a imagedata file, for that I need to close the vtk window (interactor window) and then the code will reopen it and send the new data generated to it...
I would need something like these:
int force close_window = false; int refresh_interactor = false;
I managed to make the Window close, but only with vtkcommand::Keypressed command, but idk how do I do with a new command :S, I tried the vtkcommand::UserEvent but I didn't found a good information about how to deal with that data (like some way to call it)
the way I'm dealing with VTK is with two threads, the first one, is just about the vtk iren loop, and the second one would manage the models and check if iren requires to be updated.
In my dream code it should be something like this:
=======================================================
bool VTKWindow()
{
...
vtkSmartPointer ator = vtkSmartPointer::New();
iren = vtkSmartPointer::New();
RenWindow = vtkSmartPointer::New();
render->SetBackground(.1, .2, .3);
RenWindow->AddRenderer(renderer);
iren->SetRenderWindow(RenWindow);
if(data_type == voxel_type)
{
Render->AddViewProp(VoxelData);
}
else
{
actor->SetMapper(PolyData);
Render->AddActor(Actor);
}
RenWindow->Render();
iren->Start();
}
void ManageVTK()
{
while true loop...
if(force close_window == true)
do some command to exit the iren loop
if(refresh_interactor == true)
do some command to refresh iren
}
Sorry for the english, it's not my native language, and also sorry about the question format, it's the first time I'm using stackoverflow
It may sounds stupid, but, I found a kind of solution for the problem.
I saw on related links this guy vtkRenderWindowInteractor event loop and threading and, it's almost the same problem...
class VTKNewEvent : public vtkCommand{
public:
vtkTypeMacro(VTKNewEvent , vtkCommand);
static VTKNewEvent * New(){
return new VTKNewEvent ;
}
void Execute(vtkObject * caller, unsigned long vtkNotUsed(eventId), void * vtkNotUsed(callData)){
vtkRenderWindowInteractor *iren = static_cast<vtkRenderWindowInteractor*>(caller);
if (iren_close == true){
iren->GetRenderWindow()->Finalize // Stop the interactor
iren->TerminateApp();
iren_close = false;
}
if (iren_update== true){
renderJanela->Render();
iren_update= false;
}
}
};
bool VTKWindow(){
vtkSmartPointer<VTKNewEvent > IrenRefresh= vtkSmartPointer<VTKNewEvent>::New();
...
iren->CreateRepeatingTimer(1);//this makes that IrenRefresh will be called at every 1ms
iren->AddObserver(vtkCommand::TimerEvent, IrenRefresh);
iren->Start();
...
}
it's simple, but, maybe not the best, but it did Th job, I hope this link will help people that are starting into the VTK world, since threads + rendering loop wasn't a simple job to understand what was going on

Global alt+space hotkey grabbing - weird keyboard focus behaviour

I'm grabbing Alt+Space global hotkey using xcb_grab_key, as follows:
xcb_key_symbols_t *keysyms = xcb_key_symbols_alloc(c);
xcb_keycode_t *keycodes = xcb_key_symbols_get_keycode(keysyms, XK_space), keycode;
// add bindings for all screens
xcb_screen_iterator_t iter;
iter = xcb_setup_roots_iterator (xcb_get_setup (c));
for (; iter.rem; xcb_screen_next (&iter)) {
int i = 0;
while(keycodes[i] != XCB_NO_SYMBOL) {
keycode = keycodes[i];
xcb_grab_key(c, true, iter.data->root, XCB_MOD_MASK_ANY, keycode, XCB_GRAB_MODE_SYNC, XCB_GRAB_MODE_SYNC);
i += 1;
}
}
Then in Qt's QAbstractNativeEventFilter subclass I process it and emit a Qt signal if key matches Alt+Space:
xcb_keycode_t *keycodes = xcb_key_symbols_get_keycode(keysyms, XK_space);
int i = 0;
bool found = false;
while(keycodes[i] != XCB_NO_SYMBOL) {
if(event->detail == keycodes[i]) {
if(event->state & GetModifier(c, keysyms, XK_Alt_L) || event->state & GetModifier(c, keysyms, XK_Alt_R)) {
xcb_allow_events(c, XCB_ALLOW_ASYNC_KEYBOARD, event->time);
emit gotHotKey();
found = true;
} else {
xcb_allow_events(c, XCB_ALLOW_REPLAY_KEYBOARD, event->time);
}
break;
}
i += 1;
}
if(found) return true;
(GetModifier is copied from VLC but I think this part doesn't matter since Alt-key is matched correctly)
The problem I'm having is that after show()ing main window when the hotkey is pressed, keyboard is most of the times1 not focused properly. I can type, but the cursor is not visible, input's border is not highlighted, and the shortcut Ctrl+Q for quitting desn't work. It can be worked around by moving the window, or pressing space - then focus is restored - cursor+border reappears and Ctrl+Q works. What might be causing this behaviour?
I'm using Qt 5.0.0 and xcb 1.8.1. Complete application can be downloaded for compiling from github.
1 it means sometimes the issue is not reproducible - focus is set correctly even for repeated window hide/shows, but then other times it happens multiple times in a row of hide/shows. It occurs more often than not overall.
(Edit: I've implemented a (very ugly...) workaround, so to reproduce the issue for the github project, the following code needs to be removed)
#ifndef WIN32
// Very ugly workaround for the problem described at http://stackoverflow.com/questions/14553810/
// (just show and hide a modal dialog box, which for some reason restores proper keyboard focus)
hackDialog.setGeometry(0, 0, 0, 0);
hackDialog.setModal(true);
hackDialog.show();
QTimer::singleShot(100, &hackDialog, SLOT(reject()));
#endif

Why does this code not highlight the search term once found?

The code below does not highlight the search term when it is found. In fact the cursor disappears from the QPlainTextEdit (called ui->Editor) after pressing the 'next' button. What's causing it?
void TextEditor::findNextInstanceOfSearchTerm()
{
QString searchTerm = this->edtFind->text();
if(this->TextDocument == NULL)
{
this->TextDocument = ui->Editor->document();
}
QTextCursor documentCursor(this->TextDocument);
documentCursor = this->TextDocument->find(searchTerm,documentCursor);
if(!documentCursor.isNull())
{
documentCursor.select(QTextCursor::WordUnderCursor);
}else
{
ui->statusbar->showMessage("\""+searchTerm+"\" could not be found",MESSAGE_DURATION);
}
}
Firstly, your code creates a new cursor at the beginning of the document each time you press the next button, so you will always search from the beginning. Secondly, you must understand that the cursor you manipulate has nothing to do with the one in your QPlainTextEdit: you manipulate a copy. If you want to impact the text edit, you must modify its cursor using setTextCursor. Here is a working solution:
void TextEditor::findNextInstanceOfSearchTerm()
{
QString searchTerm = this->edtFind->text();
if(this->TextDocument == NULL)
{
this->TextDocument = ui->Editor->document();
}
// get the current cursor
QTextCursor documentCursor = ui->Editor->textCursor();
documentCursor = this->TextDocument->find(searchTerm,documentCursor);
if(!documentCursor.isNull())
{
// needed only if you want the entire word to be selected
documentCursor.select(QTextCursor::WordUnderCursor);
// modify the text edit cursor
ui->Editor->setTextCursor(documentCursor);
}
else
{
ui->statusbar->showMessage(
"\""+searchTerm+"\" could not be found",MESSAGE_DURATION);
}
}
As a side note, you might want to know that QPlainTextEdit provides a find method, so this might be an easier way to achieve what you want:
void TextEditor::findNextInstanceOfSearchTerm()
{
QString searchTerm = this->edtFind->text();
bool found = ui->Editor->find(searchTerm);
if (found)
{
QTextCursor cursor = ui->Editor->textCursor();
cursor.select(QTextCursor::WordUnderCursor);
ui->Editor->setTextCursor(cursor);
}
else
{
// set message in status bar
}
}
Use QTextCursor::EndOfWord
Use QPlainTextEdit::setExtraSelections to select/highlight something in QPlainTextEdit
Simply you already have cursor that would highlight word, but you didn't apply it to text edit

Handling Complex Rules in GUI applications (C++ or C#)

Im working on a dialog box in which several rules must be satisfied before the OK button is enabled.
Currently any action on the page such as entering data or selecting an item from a drop down list (amongst other things) calls a single function called ProcessEvent() - this function handles all logic and either enables or disables the OK button.
My problem is I finding it difficult making the rules concise and understandable.
Some of the rules can be negated by another action on the dialog and I have now ended up with if else statements all over the place or which are difficult to read and follow & extend.
The code below is a simplification of the problem but demonstrates it well. How do I handle this problem better (If its Possible)
bool CWorkstation::ProcessEvent(void)
{
UpdateData();
CharCount = GetDlgItemInt(IDC_CharCount, NULL, FALSE); //get latest
if ( IsDlgButtonChecked(IDC_USEDBNAME))
{
if (!IsDlgButtonChecked(IDC_MAXDBNAME))
{
EnableNext(TRUE);
}
}
if (IsDlgButtonChecked(IDC_MAXDBNAME) && CharCount)
{
if (IsDlgButtonChecked(IDC_USEXMLNAME))
{
if ( PrefixName.IsEmpty() )
{
EnableNext(FALSE);
}
else
{
EnableNext(TRUE);
}
}
}
if (IsDlgButtonChecked(IDC_USEXMLNAME) && PrefixName.GetLength() > 1)
{
EnableNext(TRUE);
}
if ( IsDlgButtonChecked(IDC_WSAUTONAME) || IsDlgButtonChecked(IDC_RENAMEIFDUP))
{
// TRACE("IDC_WSAUTONAME is Checked\n");
if ( IsDlgButtonChecked(IDC_USEXMLNAME) && PrefixName.GetLength() > 1 )
{
if ( IsDlgButtonChecked(IDC_IDC_USESHORTNAME) )
{
EnableNext(TRUE);
}
else if ( IsDlgButtonChecked(IDC_USELONGNAME) )
{
EnableNext(TRUE);
}
else
{
EnableNext(FALSE);
}
}
if ( !IsDlgButtonChecked(IDC_USEPREFIX) )
{
if ( IsDlgButtonChecked(IDC_IDC_USESHORTNAME) || IsDlgButtonChecked(IDC_USELONGNAME) )
{
EnableNext(TRUE);
}
}
return false;
}
}
I would split your if/else statements into multiple functions, and do an &= on the parameter you send to EnableNext. You should be calling EnableNext only once.
So, for example:
// in CWorkStation::ProcessEvent
bool enableNext = true; // start with true
enableNext &= Condition1(); // of course pick better names than Condition1
enableNext &= Condition2(); // this is just for an example
EnableNext(enableNext);
Where Condition1() might be:
bool Condition1()
{
return (IsDlgButtonChecked(IDC_USEDBNAME)
&& !IsDlgButtonChecked(IDC_MAXDBNAME));
}
And so on.
What's happening here is that the enableNext variable starts with true. Then, each &= you do means that if any of the ConditionX() functions returns false, enableNext will end up false. It will only be true at the end if ALL of the conditions are true.
That problem can be solved with the concept of listeners.
You can make each of your GUI components have a isEnabled() method, which checks its conditions based on some conditions. The isEnabled() is called on each GUI component when any action that changes the state of any component is called.
This way you can have the following declarations:
bool CheckBoxComponent::isValid() {
return isNameFilled() && isEmailChecked();
}
bool OkButton::canSend() {
return checkBoxName->isValid() && isEmailChecked();
}
Then, when creating your GUI components you make each of them connect to each other via listener.
This way you have the rules for each component where they belong and you don't have tons of if statements.
It may help to try to formulate the rules as a state-machine, but if that is practical depends on their nature. In that approach, whenever the user fills out some field in the dialog, or checks a checkbox or whatever, you update the state of your sate-machine accordingly. If you have that, you can use Boost.Statechart to implement it.
In cases like that, I tend to make it as simple as possible by (for example) enabling the button by default, and if any other condition is set (or not), disable it; this limit the different cases in "if" conditions with "else".
Restate your condition as a proper boolean statement, properly indent all conditions and add some comments. IMHO, you shouldn't hide the real checks in single-use methods. If you want to comment code, comment it but don't create methods for that purpose, it only obfuscates things and your conditions don't get any simpler:
EnableNext(
// condition 1
IsDlgButtonChecked(IDC_USEDBNAME) && !IsDlgButtonChecked(IDC_MAXDBNAME)
// condition 2
|| IsDlgButtonChecked(IDC_MAXDBNAME) && CharCount
&& IsDlgButtonChecked(IDC_USEXMLNAME) && !PrefixName.IsEmpty()
// condition 3
|| IsDlgButtonChecked(IDC_USEXMLNAME) && PrefixName.GetLength() > 1
// and so on
)
This way it becomes immediately obvious that you seem to check the same condition twice USEXMLNAME && !PrefixName().IsEmpty(). It is also obvious now, that EnableNext is always called.
Though it might be a bit "heavier" of a solution than you'd like, you might want to look at Adobe's Adam and Eve libraries. Eve deals with widget layout, and Adam takes a set of statements about the logic of the widgets and puts them together into a controller that enables and disables widgets based on that logic, as well as handling initialization and putting results into the proper variables (e.g., when the user clicks "Ok").