![]() ROBLOX Studio 2013 is a tool designed for teens and pre-teens to design and build games. The users share and play games with each other through a social network website. Game logic is written in Lua so it's a great introduction to programming and classifies the tool/website as educational entertainment. The code was written in C++ and Qt using Ogre as the backend rendering engine. #include "stdafx.h" #include "ScriptSideWidget.h" // Qt Headers #include #include #include // Roblox Studio Headers #include "RobloxScriptDoc.h" #include "ScriptTextEditor.h" #include "AuthoringSettings.h" #include "QtUtilities.h" FASTFLAG(LuaDebugger) ScriptSideWidget::ScriptSideWidget(ScriptTextEditor* editor) : QWidget(editor), m_pScriptEditor(editor), m_foldArea(QtUtilities::BranchIndicatorSize) { } ScriptSideWidget::~ScriptSideWidget() { } void ScriptSideWidget::updateArea() { int digits = 1; int max = qMax(1,m_pScriptEditor->blockCount()); while (max >= 10) { max /= 10; ++digits; } QFontMetrics fm(m_font); int newWidth = fm.width(QLatin1Char('9')) * digits + 10 + (FFlag::LuaDebugger ? 2 * m_foldArea : m_foldArea);; if (width() != newWidth) { setFixedWidth(newWidth); m_pScriptEditor->setViewportMargins(newWidth,0,0,0); } update(); } void ScriptSideWidget::toggleFold(int lineNumber) { if (isFolded(lineNumber)) unfold(lineNumber); else fold(lineNumber); } void ScriptSideWidget::fold(int lineNumber) { QTextBlock startBlock = m_pScriptEditor->document()->findBlockByNumber(lineNumber - 1); if (!isFoldable(startBlock) || isFolded(lineNumber)) return; QTextBlock endBlock = ScriptEditorUtils::findFoldBoundary(startBlock,false); QTextBlock block = startBlock.next(); while (block.isValid() && (block.blockNumber() <= endBlock.blockNumber())) { block.setVisible(false); block.setLineCount(0); block = block.next(); } m_FoldedBlocks << startBlock.blockNumber(); QTextBlock closingBlock = endBlock; if (endBlock != m_pScriptEditor->document()->lastBlock()) closingBlock = endBlock.next(); QTextCursor cursor = m_pScriptEditor->textCursor(); int blockNumber = cursor.block().blockNumber(); if ( blockNumber > startBlock.blockNumber() && blockNumber <= endBlock.blockNumber() ) { cursor.setPosition(endBlock.position() + endBlock.length(),QTextCursor::MoveAnchor); m_pScriptEditor->setTextCursor(cursor); } m_pScriptEditor->document()->markContentsDirty(startBlock.position(),closingBlock.position() + closingBlock.length()); m_pScriptEditor->updateFolds(); m_pScriptEditor->viewport()->resize(m_pScriptEditor->viewport()->sizeHint()); update(); } void ScriptSideWidget::unfold(int lineNumber) { QTextBlock startBlock = m_pScriptEditor->document()->findBlockByNumber(lineNumber - 1); if (!isFoldable(startBlock)) return; QTextBlock endBlock = ScriptEditorUtils::findFoldBoundary(startBlock,false); QTextBlock block = startBlock.next(); while (block.isValid() && (block.blockNumber() <= endBlock.blockNumber())) { block.setVisible(true); block.setLineCount(block.layout()->lineCount()); if (m_FoldedBlocks.contains(block.blockNumber())) block = ScriptEditorUtils::findFoldBoundary(block,false); block = block.next(); } m_FoldedBlocks.removeOne(startBlock.blockNumber()); QTextBlock closingBlock = endBlock; if (endBlock != m_pScriptEditor->document()->lastBlock()) closingBlock = endBlock.next(); m_pScriptEditor->document()->markContentsDirty(startBlock.position(),closingBlock.position() + closingBlock.length()); m_pScriptEditor->updateFolds(); m_pScriptEditor->viewport()->resize(m_pScriptEditor->viewport()->sizeHint()); update(); } bool ScriptSideWidget::isFolded(int lineNumber) { QTextBlock block = m_pScriptEditor->document()->findBlockByNumber(lineNumber); if (!block.isValid()) return false; return !block.isVisible(); } bool ScriptSideWidget::isFoldable(QTextBlock& block) { if (!block.isValid()) return false; bool bFold = false; RBXTextUserData* pUserData = dynamic_cast(block.userData()); if (pUserData) bFold = (pUserData->getFoldState() > 0); return bFold; } void ScriptSideWidget::toggleAllFolds(bool expand) { QTextBlock currentBlock = expand ? m_pScriptEditor->document()->begin() : m_pScriptEditor->document()->end().previous(); while (currentBlock.isValid()) { if (isFoldable(currentBlock)) expand ? unfold(currentBlock.blockNumber()+1) : fold(currentBlock.blockNumber()+1); currentBlock = expand ? currentBlock.next() : currentBlock.previous(); } } void ScriptSideWidget::paintEvent(QPaintEvent* evt) { QPainter painter(this); painter.fillRect(rect(),QColor(Qt::lightGray).lighter(115)); painter.setFont(m_font); const QFontMetrics fm(m_font); const int pageBottom = m_pScriptEditor->viewport()->height(); const QTextBlock currentBlock = m_pScriptEditor->document()->findBlock(m_pScriptEditor->textCursor().position()); const QPointF viewportOffset = m_pScriptEditor->contentOffset(); const int xofs = width() - m_foldArea; QTextBlock block = m_pScriptEditor->firstVisibleBlock(); int lineCount = block.blockNumber(); while ( block.isValid() ) { lineCount += 1; // The top left position of the block in the document QPointF position = m_pScriptEditor->blockBoundingGeometry(block).topLeft() + viewportOffset; // Check if the position of the block is outside of the visible area if (position.y() > pageBottom) break; // We want the line number for the selected line to be bold. bool isBold = false; if (block == currentBlock) { isBold = true; QFont font = painter.font(); font.setBold(true); painter.setFont(font); } // Draw the line number right justified at the y position of the // line. 3 is a magic padding number. drawText(x, y, text). if (block.isVisible()) { const QString strNumber = QString::number(lineCount); const int x = width() - (FFlag::LuaDebugger ? 2 * m_foldArea : m_foldArea) - fm.width(strNumber) - 3; const int y = position.y() + fm.ascent() + fm.descent() - 1; painter.drawText(x,y,strNumber); } // Remove the bold style if it was set previously. if (isBold) { QFont font = painter.font(); font.setBold(false); painter.setFont(font); } block = block.next(); } // Breakpoints region int xofsB = width() - 2*m_foldArea; if (FFlag::LuaDebugger) painter.fillRect(xofsB, 0, m_foldArea, height(),QColor(Qt::lightGray).lighter(120)); // Code Folding painter.fillRect(xofs,0,m_foldArea,height(),QColor(Qt::lightGray).lighter(130)); block = m_pScriptEditor->firstVisibleBlock(); while ( block.isValid() ) { // Check if the position of the block is outside of the visible area const QPointF position = m_pScriptEditor->blockBoundingGeometry(block).topLeft() + viewportOffset; if ( position.y() > pageBottom ) break; QString blockText = block.text(); if (block.isVisible()) { if (FFlag::LuaDebugger) { RBXTextUserData* pUserData = dynamic_cast(block.userData()); if(pUserData) { if (pUserData->getBreakpointState()) painter.drawPixmap(xofsB, qRound(position.y())+2, QtUtilities::getBreakpointPixmap(pUserData->getBreakpointState())); if (!pUserData->getMarker().isEmpty()) { QPixmap marker(pUserData->getMarker()); painter.drawPixmap(xofsB, qRound(position.y())+2, marker.scaled(QtUtilities::BranchIndicatorSize, QtUtilities::BranchIndicatorSize, Qt::KeepAspectRatio, Qt::SmoothTransformation)); } } } if (isFoldable(block)) { if (m_FoldedBlocks.contains(block.blockNumber())) painter.drawPixmap(xofs,qRound(position.y()),QtUtilities::getBranchIndicator(QStyle::State_Children)); else painter.drawPixmap(xofs,qRound(position.y()),QtUtilities::getBranchIndicator(QStyle::State_Children | QStyle::State_Open)); } } block = block.next(); } painter.end(); QWidget::paintEvent(evt); } void ScriptSideWidget::mousePressEvent(QMouseEvent* evt) { if (m_foldArea > 0) { QFontMetrics fm(m_font); const int xofs = FFlag::LuaDebugger ? 0 : (width() - m_foldArea); const int fh = fm.lineSpacing(); const int ys = evt->posF().y(); int lineNumber = 0; if (evt->pos().x() > xofs) { QTextBlock block = m_pScriptEditor->firstVisibleBlock(); const QPointF viewportOffset = m_pScriptEditor->contentOffset(); const int pageBottom = m_pScriptEditor->viewport()->height(); while (block.isValid()) { const QPointF position = m_pScriptEditor->blockBoundingGeometry(block).topLeft() + viewportOffset; if (position.y() > pageBottom) break; if ( (position.y() < ys) && ((position.y() + fh) > ys) ) { int xofF = width() - m_foldArea; if ( FFlag::LuaDebugger && (evt->pos().x() > xofs) && (evt->pos().x() < xofF) ) { Q_EMIT toggleBreakpoint(block.blockNumber()); update(); } else if (isFoldable(block)) { lineNumber = block.blockNumber() + 1; break; } } block = block.next(); } } if (lineNumber > 0) toggleFold(lineNumber); } } void ScriptSideWidget::onPropertyChanged(const RBX::Reflection::PropertyDescriptor* pDescriptor) { if ( !pDescriptor || pDescriptor->category.str == sScriptCategoryName ) { m_font = AuthoringSettings::singleton().editorFont; m_font.setFixedPitch(true); m_font.setStrikeOut(false); m_font.setUnderline(false); m_font.setBold(false); updateArea(); } } |
Projects >