TextBlock::TextBlock( std::string id, std::string styleName, Element* pParent, Layout const * pLayout, Frame* pFrame, AssetManager* pAssetManager, NotificationQueue* pNotificationQueue, float relativeScale, float border, bool dimmable, bool adaptiveScaling, bool consumeInput, float innerBorder, FontSize fontSize, TextFlowAlignment alignment, TextFlowVerticalAlignment verticalAlignment, std::u16string content, std::string key) : Block( id, styleName, pParent, pLayout, pFrame, pAssetManager, pNotificationQueue, relativeScale, border, dimmable, adaptiveScaling, consumeInput, innerBorder) { mType = Type::TEXT_BLOCK; // Fill members mKey = key; // Create text flow if (mKey != EMPTY_STRING_ATTRIBUTE) { std::u16string localization = mpLayout->getContentFromLocalization(mKey); if (localization == LOCALIZATION_NOT_FOUND) { throwWarning( OperationNotifier::Operation::RUNTIME, "No localization used or one found for following key: " + mKey + ". Element has following id: " + getId()); mupTextFlow = std::move(mpAssetManager->createTextFlow(fontSize, alignment, verticalAlignment, content)); } else { mupTextFlow = std::move(mpAssetManager->createTextFlow(fontSize, alignment, verticalAlignment, localization)); } } else { mupTextFlow = std::move(mpAssetManager->createTextFlow(fontSize, alignment, verticalAlignment, content)); } }
void WordSuggest::specialPipeNotification(NotificationType notification, Layout* pLayout) { // Pipe notifications to notifier template including own data switch (notification) { case NotificationType::WORD_SUGGEST_CHOSEN: { // Notify listener method with UTF-16 string notifyListener(&WordSuggestListener::chosen, pLayout, getId(), mLastChosenSuggestion); // Convert suggestion value to UTF-8 string std::string lastChosenSuggestionValue8; convertUTF16ToUTF8(mLastChosenSuggestion, lastChosenSuggestionValue8); // Notify listener method with UTF-8 string notifyListener(&WordSuggestListener::chosen, pLayout, getId(), lastChosenSuggestionValue8); break; } default: throwWarning( OperationNotifier::Operation::BUG, "Word suggest got notification which is not thought for it."); break; } }
void Operation::cleanup() { if(fileType() == File) { if(!QFile(dataFilename()).remove()) throwWarning(QObject::tr("Unable to remove temporary file %1").arg(dataFilename())); } }
TextFlow::Word TextFlow::calculateWord(std::u16string content, float scale) const { // Empty word Word word; word.spVertices = std::shared_ptr<std::vector<glm::vec3> >(new std::vector<glm::vec3>); word.spTextureCoordinates = std::shared_ptr<std::vector<glm::vec2> >(new std::vector<glm::vec2>); // Fill word with data float xPixelPen = 0; for (uint i = 0; i < content.size(); i++) { Glyph const * pGlyph = mpFont->getGlyph(mFontSize, content[i]); if (pGlyph == NULL) { throwWarning( OperationNotifier::Operation::RUNTIME, "TextFlow has character in content not covered by character set"); continue; } float yPixelPen = 0 - (scale * (float)(pGlyph->size.y - pGlyph->bearing.y)); // Vertices for this quad glm::vec3 vertexA = glm::vec3(xPixelPen, yPixelPen, 0); glm::vec3 vertexB = glm::vec3(xPixelPen + (scale * pGlyph->size.x), yPixelPen, 0); glm::vec3 vertexC = glm::vec3(xPixelPen + (scale * pGlyph->size.x), yPixelPen + (scale * pGlyph->size.y), 0); glm::vec3 vertexD = glm::vec3(xPixelPen, yPixelPen + (scale * pGlyph->size.y), 0); // Texture coordinates for this quad glm::vec2 textureCoordinateA = glm::vec2(pGlyph->atlasPosition.x, pGlyph->atlasPosition.y); glm::vec2 textureCoordinateB = glm::vec2(pGlyph->atlasPosition.z, pGlyph->atlasPosition.y); glm::vec2 textureCoordinateC = glm::vec2(pGlyph->atlasPosition.z, pGlyph->atlasPosition.w); glm::vec2 textureCoordinateD = glm::vec2(pGlyph->atlasPosition.x, pGlyph->atlasPosition.w); xPixelPen += scale * pGlyph->advance.x; // Fill into data blocks word.spVertices->push_back(vertexA); word.spVertices->push_back(vertexB); word.spVertices->push_back(vertexC); word.spVertices->push_back(vertexC); word.spVertices->push_back(vertexD); word.spVertices->push_back(vertexA); word.spTextureCoordinates->push_back(textureCoordinateA); word.spTextureCoordinates->push_back(textureCoordinateB); word.spTextureCoordinates->push_back(textureCoordinateC); word.spTextureCoordinates->push_back(textureCoordinateC); word.spTextureCoordinates->push_back(textureCoordinateD); word.spTextureCoordinates->push_back(textureCoordinateA); } // Set width of whole word word.pixelWidth = xPixelPen; return word; }
void terminateGUI(GUI* pGUI) { if (pGUI != NULL) { delete pGUI; pGUI = NULL; } else { throwWarning(OperationNotifier::Operation::RUNTIME, "GUI was tried to terminate but is already terminated"); } }
void RemoveOperation::applyData() { QFileInfo fileInfo(localFilename()); if(fileInfo.isFile() && !QFile::remove(localFilename())) { throwWarning(QObject::tr("Failed to remove file")); } else { qCDebug(LOG_RMFILEOP) << "File removed" << path(); } }
void Element::setStyle(std::string styleName) { Style const * pStyle = mpLayout->getStyleFromStylesheet(styleName); if(pStyle != NULL) { mpStyle = pStyle; } else { throwWarning(OperationNotifier::Operation::RUNTIME, "Cannot find style with name: " + styleName); } }
void TextBlock::setContent(std::u16string content) { // Check whether value from key is in use if (mKey != EMPTY_STRING_ATTRIBUTE && mpLayout->getContentFromLocalization(mKey) == LOCALIZATION_NOT_FOUND) { throwWarning( OperationNotifier::Operation::RUNTIME, "Content of TextBlock could not be set because value from key is in use"); } else { mupTextFlow->setContent(content); } }
void TextBlock::setContent(std::u16string content) { // TODO: somehow strange, maybe save the usage of the key in a bool if (mKey != EMPTY_STRING_ATTRIBUTE && mpLayout->getContentFromLocalization(mKey) != LOCALIZATION_NOT_FOUND) { throwWarning( OperationNotifier::Operation::RUNTIME, "Content of TextBlock could not be set because value from key is in use"); } else { mupTextFlow->setContent(content); } }
PixelTexture::PixelTexture( std::string filepath, Filtering filtering, Wrap wrap, int suspectedChannels) : Texture() { // Setup stb_image stbi_set_flip_vertically_on_load(true); // Try to load image int width, height, channelCount; unsigned char* data = stbi_load(buildPath(filepath).c_str(), &width, &height, &channelCount, suspectedChannels); // Check whether file was found and parsed if (data == NULL) { throwError(OperationNotifier::Operation::IMAGE_LOADING, "Image file not found or error at parsing", filepath); } // Decide format GLenum glFormat; GLenum glInternalFormat; switch (suspectedChannels) { case 1: glFormat = GL_RED; glInternalFormat = GL_R8; break; case 3: glFormat = GL_RGB; glInternalFormat = GL_RGB8; break; case 4: glFormat = GL_RGBA; glInternalFormat = GL_RGBA8; break; default: glFormat= GL_RGB; glInternalFormat = GL_R8; throwWarning(OperationNotifier::Operation::IMAGE_LOADING, "Unknown number of color channels", filepath); break; } // Create OpenGL texture createOpenGLTexture(data, filtering, wrap, width, height, channelCount, glFormat, glInternalFormat, false, filepath); // Delete raw image data stbi_image_free(data); }
void TextBlock::setKey(std::string key) { if (key == EMPTY_STRING_ATTRIBUTE) { throwWarning( OperationNotifier::Operation::RUNTIME, "Tried to use empty key for localization"); } else { std::u16string localization = mpLayout->getContentFromLocalization(key); if (localization == LOCALIZATION_NOT_FOUND) { throwWarning( OperationNotifier::Operation::RUNTIME, "No localization found for following key: " + key + ". Element has following id: " + getId()); } else { mKey = key; mupTextFlow->setContent(localization); } } }
void Sensor::specialPipeNotification(Notification notification, Layout* pLayout) { // Pipe notifications to notifier template including own data switch (notification) { case Notification::SENSOR_PENETRATED: notifyListener(&SensorListener::penetrated, pLayout, getId(), mPenetration.getValue()); break; default: throwWarning( OperationNotifier::Operation::BUG, "Sensor got notification which is not thought for it."); break; } }
void Button::specialPipeNotification(NotificationType notification, Layout* pLayout) { // Pipe notifications to notifier template including own data switch (notification) { case NotificationType::BUTTON_HIT: notifyListener(&ButtonListener::hit, pLayout, getId()); break; case NotificationType::BUTTON_DOWN: notifyListener(&ButtonListener::down, pLayout, getId()); break; case NotificationType::BUTTON_UP: notifyListener(&ButtonListener::up, pLayout, getId()); break; default: throwWarning( OperationNotifier::Operation::BUG, "Button got notification which is not thought for it."); break; } }
void TextFlow::specialCalculateMesh( std::u16string streamlinedContent, float lineHeight, std::vector<glm::vec3>& rVertices, std::vector<glm::vec2>& rTextureCoordinates) { // Reset flow width to get longest line's width of this computation mFlowWidth = 0; // OpenGL setup done in calling method // Get size of space character float pixelOfSpace = 0; Glyph const * pGlyph = mpFont->getGlyph(mFontSize, u' '); if (pGlyph == NULL) { throwWarning( OperationNotifier::Operation::RUNTIME, "TextFlow creation does not find space sign in font"); } else { pixelOfSpace = mScale * pGlyph->advance.x; } // Create mark for overflow Word overflowMark = calculateWord(TEXT_FLOW_OVERFLOW_MARK, mScale); // Get pararaphs separated by \n std::vector<std::u16string> paragraphs; std::u16string paragraphDelimiter = u"\n"; // Seperate into paragraphs size_t pos = 0; std::u16string token; while ((pos = streamlinedContent.find(paragraphDelimiter)) != std::u16string::npos) { token = streamlinedContent.substr(0, pos); paragraphs.push_back(token); streamlinedContent.erase(0, pos + paragraphDelimiter.length()); } paragraphs.push_back(streamlinedContent); // Last paragraph (paragraphs never empty) // Do not generate text flow mesh when there is a failure bool failure = false; // Go over paragraphs (pens are in local pixel coordinate system with origin in lower left corner of element) float yPixelPen = -lineHeight; // First line should be also inside flow for (std::u16string& rPargraph : paragraphs) { // Get words out of paragraph std::vector<Word> words; std::u16string wordDelimiter = u" "; while ((pos = rPargraph.find(wordDelimiter)) != std::u16string::npos) { token = rPargraph.substr(0, pos); rPargraph.erase(0, pos + wordDelimiter.length()); failure |= !insertFitWord(words, token, mWidth, mScale); } // Add last token from paragraph as well failure |= !insertFitWord(words, rPargraph, mWidth, mScale); // Failure appeared, forget it if (!failure) { // Prepare some values uint wordIndex = 0; bool hasNext = !words.empty(); // Go over lines to write paragraph while (hasNext && abs(yPixelPen) <= mHeight) { // Collect words in one line std::vector<Word const *> line; float wordsPixelWidth = 0; float newWordsWithSpacesPixelWidth = 0; // Still words in the paragraph and enough space? Fill into line! while (hasNext && newWordsWithSpacesPixelWidth <= mWidth) { // First word should always fit into width because of previous checks wordsPixelWidth += words[wordIndex].pixelWidth; line.push_back(&words[wordIndex]); wordIndex++; if (wordIndex >= words.size()) { // No words in paragraph left hasNext = false; } else { // Calculate next width of line newWordsWithSpacesPixelWidth = std::ceil( (wordsPixelWidth + (float)words[wordIndex].pixelWidth) // Words size (old ones and new one) + (((float)line.size()) - 1.0f) * pixelOfSpace); // Spaces between words } } // If this is last line and after it still words left, replace it by some mark for overflow if (hasNext && abs(yPixelPen - lineHeight) > mHeight && overflowMark.pixelWidth <= mWidth) { line.clear(); wordsPixelWidth = overflowMark.pixelWidth; line.push_back(&overflowMark); } // Remember longest line's width mFlowWidth = mFlowWidth < ((int) wordsPixelWidth + 1) ? ((int)wordsPixelWidth + 1) : mFlowWidth; // Decide dynamic space for line float dynamicSpace = pixelOfSpace; if (line.size() > 1) { if (mAlignment == TextFlowAlignment::JUSTIFY && hasNext && line.size() > 1) // Do not use dynamic space for last line { // For justify, do something dynamic dynamicSpace = ((float)mWidth - wordsPixelWidth) / ((float)line.size() - 1.0f); } else { // Adjust space to compensate precision errors in other alignments float calculatedDynamicSpace = (float)mWidth - (wordsPixelWidth / (float)(line.size() - 1)); dynamicSpace = std::min(dynamicSpace, calculatedDynamicSpace); } } // Now decide xOffset for line float xOffset = 0; if (mAlignment == TextFlowAlignment::RIGHT || mAlignment == TextFlowAlignment::CENTER) { xOffset = (float)mWidth - ((wordsPixelWidth + ((float)line.size() - 1.0f) * dynamicSpace)); if (mAlignment == TextFlowAlignment::CENTER) { xOffset = xOffset / 2.0f; } } // Combine word geometry to one line float xPixelPen = xOffset; for (uint i = 0; i < line.size(); i++) { // Assuming, that the count of vertices and texture coordinates is equal for (uint j = 0; j < line[i]->spVertices->size(); j++) { const glm::vec3& rVertex = line[i]->spVertices->at(j); rVertices.push_back(glm::vec3(rVertex.x + xPixelPen, rVertex.y + yPixelPen, rVertex.z)); const glm::vec2& rTextureCoordinate = line[i]->spTextureCoordinates->at(j); rTextureCoordinates.push_back(glm::vec2(rTextureCoordinate.s, rTextureCoordinate.t)); } // Advance xPen xPixelPen += dynamicSpace + line[i]->pixelWidth; } // Advance yPen yPixelPen -= lineHeight; } } } // If failure appeared, clean up if (failure) { // Vertex count will become zero rVertices.clear(); rTextureCoordinates.clear(); } // Get height of all lines (yPixelPen is one line to low now) mFlowHeight = (int)std::max(std::ceil(abs(yPixelPen) - lineHeight), 0.0f); }