mirror of
				https://github.com/vcmi/vcmi.git
				synced 2025-10-31 00:07:39 +02:00 
			
		
		
		
	- chinese fonts now use fallback to H3 fonts for ASCII characters
- proper messages for not implemented main menu entries - some cleanup of CMessage::breakText()
This commit is contained in:
		| @@ -129,44 +129,54 @@ SDL_Surface * CMessage::drawDialogBox(int w, int h, PlayerColor playerColor) | ||||
| 	return ret; | ||||
| } | ||||
|  | ||||
| std::vector<std::string> CMessage::breakText( std::string text, size_t maxLineSize, EFonts font ) | ||||
| std::vector<std::string> CMessage::breakText( std::string text, size_t maxLineWidth, EFonts font ) | ||||
| { | ||||
| 	std::vector<std::string> ret; | ||||
|  | ||||
| 	boost::algorithm::trim_right_if(text,boost::algorithm::is_any_of(std::string(" "))); | ||||
|  | ||||
| 	// each interation generates one output line | ||||
| 	while (text.length()) | ||||
| 	{ | ||||
| 		ui32 lineLength = 0;	//in characters or given char metric | ||||
| 		ui32 wordBreak = -1; | ||||
| 		ui32 currPos = 0; //our position in text | ||||
| 		bool opened = false;//if we have an unclosed brace in current line | ||||
| 		bool lineManuallyBroken = false; | ||||
| 		ui32 lineWidth = 0;    //in characters or given char metric | ||||
| 		ui32 wordBreak = -1;    //last position for line break (last space character) | ||||
| 		ui32 currPos = 0;       //current position in text | ||||
| 		bool opened = false;    //set to true when opening brace is found | ||||
|  | ||||
| 		while(currPos < text.length()  &&  text[currPos] != 0x0a  &&  lineLength < maxLineSize) | ||||
| 		size_t symbolSize = 0; // width of character, in bytes | ||||
| 		size_t glyphWidth = 0; // width of printable glyph, pixels | ||||
|  | ||||
| 		// loops till line is full or end of text reached | ||||
| 		while(currPos < text.length()  &&  text[currPos] != 0x0a  &&  lineWidth < maxLineWidth) | ||||
| 		{ | ||||
| 			if (ui8(text[currPos]) <= ui8(' ')) // candidate for line break | ||||
| 			symbolSize = graphics->fonts[font]->getCharacterSize(text[currPos]); | ||||
| 			glyphWidth = graphics->fonts[font]->getGlyphWidth(text.data() + currPos); | ||||
|  | ||||
| 			// candidate for line break | ||||
| 			if (ui8(text[currPos]) <= ui8(' ')) | ||||
| 				wordBreak = currPos; | ||||
|  | ||||
| 			/* We don't count braces in string length. */ | ||||
| 			if (text[currPos] == '{') | ||||
| 				opened=true; | ||||
| 			else if (text[currPos]=='}') | ||||
| 				opened=false; | ||||
| 			else | ||||
| 				lineLength += graphics->fonts[font]->getGlyphWidth(text.data() + currPos); | ||||
| 			currPos += graphics->fonts[font]->getCharacterSize(text[currPos]); | ||||
| 				lineWidth += glyphWidth; | ||||
| 			currPos += symbolSize; | ||||
| 		} | ||||
|  | ||||
| 		// long line, create line break | ||||
| 		if (currPos < text.length()  &&  (text[currPos] != 0x0a)) | ||||
| 		{ | ||||
| 			// We have a long line. Try to do a nice line break, if possible | ||||
| 			if (wordBreak != ui32(-1)) | ||||
| 				currPos = wordBreak; | ||||
| 			else | ||||
| 				currPos--; | ||||
| 				currPos -= symbolSize; | ||||
| 		} | ||||
|  | ||||
| 		if(currPos) //non-blank line | ||||
| 		//non-blank line | ||||
| 		if(currPos != 0) | ||||
| 		{ | ||||
| 			ret.push_back(text.substr(0, currPos)); | ||||
|  | ||||
| @@ -176,39 +186,34 @@ std::vector<std::string> CMessage::breakText( std::string text, size_t maxLineSi | ||||
|  | ||||
| 			text.erase(0, currPos); | ||||
| 		} | ||||
| 		else if(text[currPos] == 0x0a) //blank line | ||||
| 		else if(text[currPos] == 0x0a) | ||||
| 		{ | ||||
| 			ret.push_back(""); //add empty string, no extra actions needed | ||||
| 		} | ||||
|  | ||||
| 		if (text.length() && text[0] == 0x0a) | ||||
| 		if (text.length() != 0 && text[0] == 0x0a) | ||||
| 		{ | ||||
| 			/* Braces do not carry over lines. The map author forgot | ||||
| 			 * to close it. */ | ||||
| 			opened = false; | ||||
|  | ||||
| 			/* Remove LF */ | ||||
| 			text.erase(0, 1); | ||||
|  | ||||
| 			lineManuallyBroken = true; | ||||
| 		} | ||||
|  | ||||
| 		//if(!allowLeadingWhitespace || !lineManuallyBroken) | ||||
| 		if(!lineManuallyBroken) | ||||
| 		else | ||||
| 		{ | ||||
| 			// trim only if line does not starts with LF | ||||
| 			// FIXME: necessary? All lines will be trimmed before returning anyway | ||||
| 			boost::algorithm::trim_left_if(text,boost::algorithm::is_any_of(std::string(" "))); | ||||
| 		} | ||||
|  | ||||
| 		if (opened) | ||||
| 		{ | ||||
| 			/* Add an opening brace for the next line. */ | ||||
| 			if (text.length()) | ||||
| 			if (text.length() != 0) | ||||
| 				text.insert(0, "{"); | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	/* Trim whitespaces of every line. */ | ||||
| 	//if(!allowLeadingWhitespace) | ||||
| 		for (auto & elem : ret) | ||||
| 			boost::algorithm::trim(elem); | ||||
| 	for (auto & elem : ret) | ||||
| 		boost::algorithm::trim(elem); | ||||
|  | ||||
| 	return ret; | ||||
| } | ||||
|   | ||||
| @@ -332,7 +332,8 @@ static std::function<void()> genCommand(CMenuScreen* menu, std::vector<std::stri | ||||
| 						case 0: return boost::bind(&CGPreGame::openSel, CGP, CMenuScreen::newGame, CMenuScreen::SINGLE_PLAYER); | ||||
| 						case 1: return &pushIntT<CMultiMode>; | ||||
| 						case 2: return boost::bind(&CGPreGame::openSel, CGP, CMenuScreen::campaignList, CMenuScreen::SINGLE_PLAYER); | ||||
| 						case 3: return std::function<void()>();//TODO: start tutorial | ||||
| 						//TODO: start tutorial | ||||
| 						case 3: return boost::bind(CInfoWindow::showInfoDialog, "Sorry, tutorial is not implemented yet\n", (const std::vector<CComponent*>*)nullptr, false, PlayerColor(1)); | ||||
| 					} | ||||
| 				} | ||||
| 				break; case 3://load | ||||
| @@ -341,8 +342,10 @@ static std::function<void()> genCommand(CMenuScreen* menu, std::vector<std::stri | ||||
| 					{ | ||||
| 						case 0: return boost::bind(&CGPreGame::openSel, CGP, CMenuScreen::loadGame, CMenuScreen::SINGLE_PLAYER); | ||||
| 						case 1: return boost::bind(&CGPreGame::openSel, CGP, CMenuScreen::loadGame, CMenuScreen::MULTI_HOT_SEAT); | ||||
| 						case 2: return std::function<void()>();//TODO: load campaign | ||||
| 						case 3: return std::function<void()>();//TODO: load tutorial | ||||
| 						//TODO: load campaign | ||||
| 						case 2: return boost::bind(CInfoWindow::showInfoDialog, "This function is not implemented yet. Campaign saves can be loaded from \"Single Player\" menu", (const std::vector<CComponent*>*)nullptr, false, PlayerColor(1)); | ||||
| 						//TODO: load tutorial | ||||
| 						case 3: return boost::bind(CInfoWindow::showInfoDialog, "Sorry, tutorial is not implemented yet\n", (const std::vector<CComponent*>*)nullptr, false, PlayerColor(1)); | ||||
| 					} | ||||
| 				} | ||||
| 				break; case 4://exit | ||||
| @@ -351,7 +354,8 @@ static std::function<void()> genCommand(CMenuScreen* menu, std::vector<std::stri | ||||
| 				} | ||||
| 				break; case 5://highscores | ||||
| 				{ | ||||
| 					return std::function<void()>(); //TODO: high scores &pushIntT<CHighScores>; | ||||
| 					//TODO: high scores | ||||
| 					return boost::bind(CInfoWindow::showInfoDialog, "Sorry, high scores menu is not implemented yet\n", (const std::vector<CComponent*>*)nullptr, false, PlayerColor(1)); | ||||
| 				} | ||||
| 			} | ||||
| 		} | ||||
|   | ||||
| @@ -721,6 +721,12 @@ void CInfoWindow::showAll(SDL_Surface * to) | ||||
| 	CIntObject::showAll(to); | ||||
| } | ||||
|  | ||||
| void CInfoWindow::showInfoDialog(const std::string &text, const std::vector<CComponent *> *components, bool DelComps, PlayerColor player) | ||||
| { | ||||
| 	CInfoWindow * window = CInfoWindow::create(text, player, components, DelComps); | ||||
| 	GH.pushInt(window); | ||||
| } | ||||
|  | ||||
| void CInfoWindow::showYesNoDialog(const std::string & text, const std::vector<CComponent*> *components, const CFunctionList<void( ) > &onYes, const CFunctionList<void()> &onNo, bool DelComps, PlayerColor player) | ||||
| { | ||||
| 	assert(!LOCPLINT || LOCPLINT->showingDialog->get()); | ||||
|   | ||||
| @@ -104,7 +104,9 @@ public: | ||||
| 	CInfoWindow(); //c-tor | ||||
| 	~CInfoWindow(); //d-tor | ||||
|  | ||||
| 	static void showYesNoDialog( const std::string & text, const std::vector<CComponent*> *components, const CFunctionList<void( ) > &onYes, const CFunctionList<void()> &onNo, bool DelComps = true, PlayerColor player = PlayerColor(1)); //use only before the game starts! (showYesNoDialog in LOCPLINT must be used then) | ||||
| 	//use only before the game starts! (showYesNoDialog in LOCPLINT must be used then) | ||||
| 	static void showInfoDialog( const std::string & text, const std::vector<CComponent*> *components, bool DelComps = true, PlayerColor player = PlayerColor(1)); | ||||
| 	static void showYesNoDialog( const std::string & text, const std::vector<CComponent*> *components, const CFunctionList<void( ) > &onYes, const CFunctionList<void()> &onNo, bool DelComps = true, PlayerColor player = PlayerColor(1)); | ||||
| 	static CInfoWindow *create(const std::string &text, PlayerColor playerID = PlayerColor(1), const std::vector<CComponent*> *components = nullptr, bool DelComps = false); | ||||
|  | ||||
| 	/// create text from title and description: {title}\n\n description | ||||
|   | ||||
| @@ -365,7 +365,9 @@ void Graphics::addImageListEntry(size_t index, std::string listName, std::string | ||||
| { | ||||
| 	if (!imageName.empty()) | ||||
| 	{ | ||||
| 		if (!CResourceHandler::get()->existsResource(ResourceID("SPRITES/" + imageName, EResType::IMAGE))) | ||||
| 		ResourceID resID("SPRITES/" + imageName, EResType::IMAGE); | ||||
| 		if (!CResourceHandler::get()->existsResource(resID) && // file not found | ||||
| 		    imageName.find(':') == std::string::npos)          // and entry does not refers to frame in def file | ||||
| 			logGlobal->errorStream() << "Required image " << "SPRITES/" << imageName << " is missing!"; | ||||
|  | ||||
| 		JsonNode entry; | ||||
|   | ||||
| @@ -353,7 +353,7 @@ void CBitmapHanFont::renderText(SDL_Surface * surface, const std::string & data, | ||||
| 	for(size_t i=0; i<data.size(); i += getCharacterSize(data[i])) | ||||
| 	{ | ||||
| 		if (ui8(data[i]) < 0x80) | ||||
| 			renderCharacter(surface, getCharacterIndex(0xa3, data[i] + 0x80), color, posX, posY); | ||||
| 			fallback->renderCharacter(surface, fallback->chars[data[i]], color, posX, posY); | ||||
| 		else | ||||
| 			renderCharacter(surface, getCharacterIndex(data[i], data[i+1]), color, posX, posY); | ||||
| 	} | ||||
| @@ -361,6 +361,7 @@ void CBitmapHanFont::renderText(SDL_Surface * surface, const std::string & data, | ||||
| } | ||||
|  | ||||
| CBitmapHanFont::CBitmapHanFont(const JsonNode &config): | ||||
|     fallback(new CBitmapFont(config["fallback"].String())), | ||||
|     data(CResourceHandler::get()->load(ResourceID("data/" + config["name"].String(), EResType::OTHER))->readAll()), | ||||
|     size(config["size"].Float()) | ||||
| { | ||||
| @@ -378,6 +379,8 @@ size_t CBitmapHanFont::getLineHeight() const | ||||
|  | ||||
| size_t CBitmapHanFont::getGlyphWidth(const char * data) const | ||||
| { | ||||
| 	if (ui8(data[0]) < 0x80) | ||||
| 		return fallback->getGlyphWidth(data); | ||||
| 	return size + 1; | ||||
| } | ||||
|  | ||||
|   | ||||
| @@ -97,6 +97,7 @@ public: | ||||
| /// supports multi-byte characters for such languages like Chinese | ||||
| class CBitmapHanFont : public IFont | ||||
| { | ||||
| 	std::unique_ptr<CBitmapFont> fallback; | ||||
| 	// data, directly copied from file | ||||
| 	const std::pair<std::unique_ptr<ui8[]>, ui64> data; | ||||
|  | ||||
|   | ||||
		Reference in New Issue
	
	Block a user