如何在SDL2中用鼠标高亮显示文本(例如,在msword中,您可以单击并高亮显示文本)



我正在尝试高效地突出显示SDL2中的文本。假设我显示了一个文本字符串,该文本类似于:"你好,世界&";,并且我想强调";orl";来自世界。若我使用单词,我可以点击"l"并用鼠标向左移动,反之亦然,点击"o"并从那里向右移动。有什么有效的方法可以做到这一点吗?如果是,如何?我找不到一个有效的解决方案,不让我的fps随着文本的增加而呈指数级下降。此外(如果可能的话,还有其他内容(,如果我有一个文本框,它将文本包裹起来,如果不合适,则换行,如果我开始点击第一个单词,然后向下移动鼠标,它将高亮显示整个行,直到我悬停的字符,我该如何高亮显示文本。感谢

仅供参考:这些是我的文本对象(头和cpp文件(

text.h

#ifndef TEXT_H
#define TEXT_H
#include <SDL2/SDL_ttf.h>
#include <SDL2/SDL.h>
#include "events.h"

#include <iostream>
#include <string>
#include <vector>
using std::vector;
using std::string;
using std::cout;
using std::endl;
using std::exception;

class Text
{
public:
Events EVENTS;
Text(void);

///Public variables
static vector <TTF_Font*> fonts;
const int lowestFontSize = 1;
const int highestFontSize = 100;
int fontIndex;
int textSize;
SDL_Rect textRect;
int x;
int y;
int initialX, initialY;
bool setup;
static int numOfInstances;

///Functions
void Setup(SDL_Renderer *renderer, string txt, int x, int y, int txtSize, SDL_Color Colour = {0,0,0}, bool isBold = false, string fontType = "arial.ttf", bool isWrapped=false, int theWrapWidth=0);
string Get_Text();
void Change_Text(SDL_Renderer *renderer, string newText);
void Draw_Text(SDL_Renderer *renderer);
void Change_Position(SDL_Renderer *renderer, int xPos, int yPos);
void Change_Position_And_Text(SDL_Renderer *renderer, int xPos, int yPos, string newText);
int Text_Width(int FirstCharIndex, int numOfCharsPastFirstIndex);
int Text_Height(int FirstCharIndex, int numOfCharsPastFirstIndex);

void Free_All();

private:
///SDL stuff
SDL_Texture *textTexture;
SDL_Surface *textSurface;

SDL_Color colour;
SDL_Point point;
///text varibales
string text;
int textW;
int textH;
bool bold;
bool wrapped;
int wrappedWidth;


};

#endif

text.cpp

#include "text.h"

int Text::numOfInstances = 0;
vector <TTF_Font*> Text::fonts;

Text::Text()
{
setup = false;

textTexture = NULL;
textSurface = NULL;

}

void Text::Setup(SDL_Renderer *renderer, string txt, int xPos, int yPos, int txtSize, SDL_Color Colour, bool isBold, string fontType, bool isWrapped, int theWrapWidth )
{
if (setup == false){

numOfInstances += 1;

wrapped = isWrapped;
wrappedWidth = theWrapWidth;
text = txt;
textSize = txtSize;
bold = isBold;
colour = Colour;

textW = 0;
textH = 0;
x = xPos;
y = yPos;
initialX = x;
initialY = y;

fontIndex = textSize-lowestFontSize -1;

///One time setups
if (numOfInstances == 1){
try{
TTF_Init();
//cout << "Initialised ttf" << endl;
}
catch (exception &err){
cout << "Could not initialise ttf for text "" << text << "". Error from SDL is: " << TTF_GetError() << ". Error from C++ is: " << err.what() << endl;
}
for (int i=lowestFontSize; i <= highestFontSize; i++){
TTF_Font *currentFont = TTF_OpenFont(fontType.c_str(), i);
if (!currentFont){
cout << "Error with font in text "" << txt << "" Error is: " << SDL_GetError() << endl;
}
//TTF_SetFontKerning(currentFont, 0);

fonts.push_back(currentFont);
}
}

if (bold == true){
TTF_SetFontStyle(fonts[fontIndex], TTF_STYLE_BOLD);
}

if (!SDL_SetHint( SDL_HINT_RENDER_SCALE_QUALITY, "2" ) ){  ///2 is highest
cout << "Text rendering quality not enabled " << text << endl;
}
if (text != ""){    ///Only create textures if there is text
if (wrapped == true){
textSurface = TTF_RenderText_Blended_Wrapped(fonts[fontIndex], text.c_str(), colour, wrappedWidth);     ///Recreate the textures/surfaces
}
else{
textSurface = TTF_RenderText_Blended(fonts[fontIndex], text.c_str(), colour);     ///Recreate the textures/surfaces
}
if (!textSurface){
cout << "Unable to create surface of text " << text << " error is: " << SDL_GetError() << endl;
}
textTexture = SDL_CreateTextureFromSurface(renderer, textSurface);
if (!textTexture){
cout << "Unable to create texture from surface of text " << text << " error is: " << SDL_GetError() << endl;
}
SDL_FreeSurface(textSurface);
textSurface = NULL;
SDL_QueryTexture(textTexture, NULL, NULL, &textW, &textH);
textRect = {x, y, textW, textH};
}



point = {0, 0};
setup = true;


}

else{
//cout << "Trying to setup a text already setup! " << text << endl;
}

}


void Text::Change_Position_And_Text(SDL_Renderer *renderer, int xPos, int yPos, string newText )
{
if (setup == true){
text = newText;
x = xPos;
y = yPos;
textRect.x = x;
textRect.y = y;

if (textTexture != NULL){
SDL_DestroyTexture(textTexture);    ///Free memory not going to be used again.
textTexture = NULL;
}
if (text != ""){
if (wrapped == true){
textSurface = TTF_RenderText_Blended_Wrapped(fonts[fontIndex], text.c_str(), colour, wrappedWidth);     ///Recreate the textures/surfaces
}
else{
textSurface = TTF_RenderText_Blended(fonts[fontIndex], text.c_str(), colour);     ///Recreate the textures/surfaces
}
if (!textSurface){
cout << "Unable to create surface of text " << text << " error is: " << SDL_GetError() << endl;
}
textTexture = SDL_CreateTextureFromSurface(renderer, textSurface);
if (!textTexture){
cout << "Unable to create texture from surface of text " << text << " error is: " << SDL_GetError() << endl;
}
SDL_FreeSurface(textSurface);
textSurface = NULL;
SDL_QueryTexture(textTexture, NULL, NULL, &textW, &textH);   ///neeed this
textRect = {x, y, textW, textH};

}

}
}

void Text::Change_Position(SDL_Renderer *renderer,  int xPos, int yPos)
{
if (setup == true){
x = xPos;
y = yPos ;
textRect.x = xPos;
textRect.y = yPos;
}
}


void Text::Change_Text(SDL_Renderer *renderer, string newText)
{
if (setup == true){
text = newText;
if (textTexture != NULL){
SDL_DestroyTexture(textTexture);    ///Free memory not going to be used again.
textTexture = NULL;
}

if (text != ""){
if (wrapped == true){
textSurface = TTF_RenderText_Blended_Wrapped(fonts[fontIndex], text.c_str(), colour, wrappedWidth);     ///Recreate the textures/surfaces
}
else{
textSurface = TTF_RenderText_Blended(fonts[fontIndex], text.c_str(), colour);     ///Recreate the textures/surfaces
}
if (!textSurface){
cout << "Unable to create surface of text " << text << " error is: " << SDL_GetError() << endl;
}
textTexture = SDL_CreateTextureFromSurface(renderer, textSurface);
if (!textTexture){
cout << "Unable to create texture from surface of text " << text << " error is: " << SDL_GetError() << endl;
}
SDL_FreeSurface(textSurface);
textSurface = NULL;
SDL_QueryTexture(textTexture, NULL, NULL, &textW, &textH);      ///neeed this
textRect = {x, y, textW, textH};
}
}

}

int Text::Text_Width(int FirstCharIndex, int numOfCharsPastFirstIndex)
{
int w,h;
string textSelection = text.substr(FirstCharIndex, numOfCharsPastFirstIndex);
TTF_SizeText(fonts[fontIndex], textSelection.c_str(), &w, &h);
return w;
}

int Text::Text_Height(int FirstCharIndex, int numOfCharsPastFirstIndex)
{
int w,h;
string textSelection = text.substr(FirstCharIndex, numOfCharsPastFirstIndex);
TTF_SizeText(fonts[fontIndex], textSelection.c_str(), &w, &h);
return h;
}

string Text::Get_Text()
{
if (setup == true){
return text;
}
else{
return "";
//cout << "Text not setup when trying to obtain text through Get_Text() function" << endl;
}
}

void Text::Draw_Text(SDL_Renderer *renderer)
{
if (setup == true){
if (SDL_PointInRect(&EVENTS.mousePos, &textRect)   &&    EVENTS.currentCursor !=  SDL_SYSTEM_CURSOR_IBEAM){
EVENTS.Change_Cursor(SDL_SYSTEM_CURSOR_IBEAM);
}
SDL_RenderCopy(renderer, textTexture, NULL, &textRect);
}
else{
//cout << "Text not setup when trying to draw it in Draw_Text() function" << endl;
}
}


void Text::Free_All()
{
if (setup == true){

if (textSurface == NULL){
//cout << "Text surface already free'd" << endl;
}
else{
SDL_FreeSurface(textSurface);
textSurface = NULL;
//cout << "Free'd surface n";
}
if (textTexture == NULL){
//cout << "Could not free memory for text "" << text << "". Error from SDL is: " << TTF_GetError() << endl;
}
else{
SDL_DestroyTexture(textTexture);
textTexture = NULL;
}


if (numOfInstances == 1){
for (int i=0; i <= (highestFontSize-lowestFontSize); i++){
TTF_CloseFont(fonts[i]);
//cout << "Closed " << lowestFontSize+i << endl;
}
try{
TTF_Quit();
//cout << "Quit ttf" << endl;
}
catch (exception &err){
cout << "Could not quit ttf for text "" << text << "". Error from SDL is: " << TTF_GetError() << ". Error from C++ is: " << err.what() << endl;
}
}

///For TTF_Init();
numOfInstances -= 1;

//cout << "Free'd " << text << endl;
}


else{
//cout << "Text not setup yet when trying to free!" << endl;
}

setup = false;

}

如果有人有同样的问题,我会想出一些很好的办法。它的性能损失实际上为零。现在,我可以得到每个单独角色的矩形,我会更新它以突出显示,因为我会想出更多的方法。现在,我有两个属于text类的整数向量。当我设置文本或更改文本时,我遍历字符串,比如说用abcdefg字符串,第一次迭代我的currentString是a,第二次,ab,第三次abc,第四次,abcd等等。然后,我在每个循环的当前字符串上调用TTF_SizeText。然后,我将由此获得的宽度和高度附加到向量中。您可以使用向量中连续值宽度的变化,而不是在高亮显示时重新进行此操作。例如,如果我想要"c"的宽度,我可以这样做,int deltaWidth = widthOfChars[3] - widthOfChars[2],差就是"c"宽度。这样,当我遍历字符串时,我会创建一个当前字符的矩形,在这种情况下是它的"c",在执行此操作的同时,检查鼠标是否悬停在它上面。目前,我只是为每个字符绘制一个填充的矩形,这不会给我带来任何明显的性能损失!

最新更新