mirror of
				https://github.com/vcmi/vcmi.git
				synced 2025-10-31 00:07:39 +02:00 
			
		
		
		
	Android: make keyboard appear and disappear only on demand
This commit is contained in:
		
				
					committed by
					
						 Andrii Danylchenko
						Andrii Danylchenko
					
				
			
			
				
	
			
			
			
						parent
						
							6da233c387
						
					
				
				
					commit
					2710d1df50
				
			| @@ -268,10 +268,14 @@ void SelectionTab::clickLeft(tribool down, bool previousState) | |||||||
| 	if(down) | 	if(down) | ||||||
| 	{ | 	{ | ||||||
| 		int line = getLine(); | 		int line = getLine(); | ||||||
|  |  | ||||||
| 		if(line != -1) | 		if(line != -1) | ||||||
|  | 		{ | ||||||
| 			select(line); | 			select(line); | ||||||
|  | 		} | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
|  |  | ||||||
| void SelectionTab::keyPressed(const SDL_KeyboardEvent & key) | void SelectionTab::keyPressed(const SDL_KeyboardEvent & key) | ||||||
| { | { | ||||||
| 	if(key.state != SDL_PRESSED) | 	if(key.state != SDL_PRESSED) | ||||||
|   | |||||||
| @@ -67,7 +67,7 @@ void CLabel::setAutoRedraw(bool value) | |||||||
| 	autoRedraw = value; | 	autoRedraw = value; | ||||||
| } | } | ||||||
|  |  | ||||||
| void CLabel::setText(const std::string &Txt) | void CLabel::setText(const std::string & Txt) | ||||||
| { | { | ||||||
| 	text = Txt; | 	text = Txt; | ||||||
| 	if(autoRedraw) | 	if(autoRedraw) | ||||||
| @@ -96,7 +96,7 @@ size_t CLabel::getWidth() | |||||||
| 	return graphics->fonts[font]->getStringWidth(visibleText());; | 	return graphics->fonts[font]->getStringWidth(visibleText());; | ||||||
| } | } | ||||||
|  |  | ||||||
| CMultiLineLabel::CMultiLineLabel(Rect position, EFonts Font, EAlignment Align, const SDL_Color &Color, const std::string &Text): | CMultiLineLabel::CMultiLineLabel(Rect position, EFonts Font, EAlignment Align, const SDL_Color & Color, const std::string & Text) : | ||||||
| 	CLabel(position.x, position.y, Font, Align, Color, Text), | 	CLabel(position.x, position.y, Font, Align, Color, Text), | ||||||
| 	visibleSize(0, 0, position.w, position.h) | 	visibleSize(0, 0, position.w, position.h) | ||||||
| { | { | ||||||
| @@ -124,32 +124,32 @@ void CMultiLineLabel::scrollTextTo(int distance, bool redrawAfterScroll) | |||||||
| 	setVisibleSize(size, redrawAfterScroll); | 	setVisibleSize(size, redrawAfterScroll); | ||||||
| } | } | ||||||
|  |  | ||||||
| void CMultiLineLabel::setText(const std::string &Txt) | void CMultiLineLabel::setText(const std::string & Txt) | ||||||
| { | { | ||||||
| 	splitText(Txt, false); //setText used below can handle redraw | 	splitText(Txt, false); //setText used below can handle redraw | ||||||
| 	CLabel::setText(Txt); | 	CLabel::setText(Txt); | ||||||
| } | } | ||||||
|  |  | ||||||
| void CTextContainer::blitLine(SDL_Surface *to, Rect destRect, std::string what) | void CTextContainer::blitLine(SDL_Surface * to, Rect destRect, std::string what) | ||||||
| { | { | ||||||
| 	const auto f = graphics->fonts[font]; | 	const auto f = graphics->fonts[font]; | ||||||
| 	Point where = destRect.topLeft(); | 	Point where = destRect.topLeft(); | ||||||
|  |  | ||||||
| 	// input is rect in which given text should be placed | 	// input is rect in which given text should be placed | ||||||
| 	// calculate proper position for top-left corner of the text | 	// calculate proper position for top-left corner of the text | ||||||
| 	if (alignment == TOPLEFT) | 	if(alignment == TOPLEFT) | ||||||
| 	{ | 	{ | ||||||
| 		where.x += getBorderSize().x; | 		where.x += getBorderSize().x; | ||||||
| 		where.y += getBorderSize().y; | 		where.y += getBorderSize().y; | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	if (alignment == CENTER) | 	if(alignment == CENTER) | ||||||
| 	{ | 	{ | ||||||
| 		where.x += (int(destRect.w) - int(f->getStringWidth(what))) / 2; | 		where.x += (int(destRect.w) - int(f->getStringWidth(what))) / 2; | ||||||
| 		where.y += (int(destRect.h) - int(f->getLineHeight())) / 2; | 		where.y += (int(destRect.h) - int(f->getLineHeight())) / 2; | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	if (alignment == BOTTOMRIGHT) | 	if(alignment == BOTTOMRIGHT) | ||||||
| 	{ | 	{ | ||||||
| 		where.x += getBorderSize().x + destRect.w - (int)f->getStringWidth(what); | 		where.x += getBorderSize().x + destRect.w - (int)f->getStringWidth(what); | ||||||
| 		where.y += getBorderSize().y + destRect.h - (int)f->getLineHeight(); | 		where.y += getBorderSize().y + destRect.h - (int)f->getLineHeight(); | ||||||
| @@ -162,11 +162,11 @@ void CTextContainer::blitLine(SDL_Surface *to, Rect destRect, std::string what) | |||||||
| 	do | 	do | ||||||
| 	{ | 	{ | ||||||
| 		size_t end = what.find_first_of(delimeters[currDelimeter % 2], begin); | 		size_t end = what.find_first_of(delimeters[currDelimeter % 2], begin); | ||||||
| 		if (begin != end) | 		if(begin != end) | ||||||
| 		{ | 		{ | ||||||
| 			std::string toPrint = what.substr(begin, end - begin); | 			std::string toPrint = what.substr(begin, end - begin); | ||||||
|  |  | ||||||
| 			if (currDelimeter % 2) // Enclosed in {} text - set to yellow | 			if(currDelimeter % 2) // Enclosed in {} text - set to yellow | ||||||
| 				f->renderTextLeft(to, toPrint, Colors::YELLOW, where); | 				f->renderTextLeft(to, toPrint, Colors::YELLOW, where); | ||||||
| 			else // Non-enclosed text, use default color | 			else // Non-enclosed text, use default color | ||||||
| 				f->renderTextLeft(to, toPrint, color, where); | 				f->renderTextLeft(to, toPrint, color, where); | ||||||
| @@ -175,15 +175,15 @@ void CTextContainer::blitLine(SDL_Surface *to, Rect destRect, std::string what) | |||||||
| 			where.x += (int)f->getStringWidth(toPrint); | 			where.x += (int)f->getStringWidth(toPrint); | ||||||
| 		} | 		} | ||||||
| 		currDelimeter++; | 		currDelimeter++; | ||||||
| 	} | 	} while(begin++ != std::string::npos); | ||||||
| 	while (begin++ != std::string::npos); |  | ||||||
| } | } | ||||||
|  |  | ||||||
| CTextContainer::CTextContainer(EAlignment alignment, EFonts font, SDL_Color color): | CTextContainer::CTextContainer(EAlignment alignment, EFonts font, SDL_Color color) : | ||||||
| 	alignment(alignment), | 	alignment(alignment), | ||||||
| 	font(font), | 	font(font), | ||||||
| 	color(color) | 	color(color) | ||||||
| {} | { | ||||||
|  | } | ||||||
|  |  | ||||||
| void CMultiLineLabel::showAll(SDL_Surface * to) | void CMultiLineLabel::showAll(SDL_Surface * to) | ||||||
| { | { | ||||||
| @@ -193,15 +193,15 @@ void CMultiLineLabel::showAll(SDL_Surface * to) | |||||||
|  |  | ||||||
| 	// calculate which lines should be visible | 	// calculate which lines should be visible | ||||||
| 	int totalLines = static_cast<int>(lines.size()); | 	int totalLines = static_cast<int>(lines.size()); | ||||||
| 	int beginLine  = visibleSize.y; | 	int beginLine = visibleSize.y; | ||||||
| 	int endLine    = getTextLocation().h + visibleSize.y; | 	int endLine = getTextLocation().h + visibleSize.y; | ||||||
|  |  | ||||||
| 	if (beginLine < 0) | 	if(beginLine < 0) | ||||||
| 		beginLine = 0; | 		beginLine = 0; | ||||||
| 	else | 	else | ||||||
| 		beginLine /= (int)f->getLineHeight(); | 		beginLine /= (int)f->getLineHeight(); | ||||||
|  |  | ||||||
| 	if (endLine < 0) | 	if(endLine < 0) | ||||||
| 		endLine = 0; | 		endLine = 0; | ||||||
| 	else | 	else | ||||||
| 		endLine /= (int)f->getLineHeight(); | 		endLine /= (int)f->getLineHeight(); | ||||||
| @@ -209,32 +209,32 @@ void CMultiLineLabel::showAll(SDL_Surface * to) | |||||||
|  |  | ||||||
| 	// and where they should be displayed | 	// and where they should be displayed | ||||||
| 	Point lineStart = getTextLocation().topLeft() - visibleSize + Point(0, beginLine * (int)f->getLineHeight()); | 	Point lineStart = getTextLocation().topLeft() - visibleSize + Point(0, beginLine * (int)f->getLineHeight()); | ||||||
| 	Point lineSize  = Point(getTextLocation().w, (int)f->getLineHeight()); | 	Point lineSize = Point(getTextLocation().w, (int)f->getLineHeight()); | ||||||
|  |  | ||||||
| 	CSDL_Ext::CClipRectGuard guard(to, getTextLocation()); // to properly trim text that is too big to fit | 	CSDL_Ext::CClipRectGuard guard(to, getTextLocation()); // to properly trim text that is too big to fit | ||||||
|  |  | ||||||
| 	for (int i = beginLine; i < std::min(totalLines, endLine); i++) | 	for(int i = beginLine; i < std::min(totalLines, endLine); i++) | ||||||
| 	{ | 	{ | ||||||
| 		if (!lines[i].empty()) //non-empty line | 		if(!lines[i].empty()) //non-empty line | ||||||
| 			blitLine(to, Rect(lineStart, lineSize), lines[i]); | 			blitLine(to, Rect(lineStart, lineSize), lines[i]); | ||||||
|  |  | ||||||
| 		lineStart.y += (int)f->getLineHeight(); | 		lineStart.y += (int)f->getLineHeight(); | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
|  |  | ||||||
| void CMultiLineLabel::splitText(const std::string &Txt, bool redrawAfter) | void CMultiLineLabel::splitText(const std::string & Txt, bool redrawAfter) | ||||||
| { | { | ||||||
| 	lines.clear(); | 	lines.clear(); | ||||||
|  |  | ||||||
| 	const auto f = graphics->fonts[font]; | 	const auto f = graphics->fonts[font]; | ||||||
| 	int lineHeight =  static_cast<int>(f->getLineHeight()); | 	int lineHeight = static_cast<int>(f->getLineHeight()); | ||||||
|  |  | ||||||
| 	lines = CMessage::breakText(Txt, pos.w, font); | 	lines = CMessage::breakText(Txt, pos.w, font); | ||||||
|  |  | ||||||
| 	 textSize.y = lineHeight * (int)lines.size(); | 	textSize.y = lineHeight * (int)lines.size(); | ||||||
| 	 textSize.x = 0; | 	textSize.x = 0; | ||||||
| 	for(const std::string &line : lines) | 	for(const std::string & line : lines) | ||||||
| 		vstd::amax( textSize.x, f->getStringWidth(line.c_str())); | 		vstd::amax(textSize.x, f->getStringWidth(line.c_str())); | ||||||
| 	if(redrawAfter) | 	if(redrawAfter) | ||||||
| 		redraw(); | 		redraw(); | ||||||
| } | } | ||||||
| @@ -244,7 +244,7 @@ Rect CMultiLineLabel::getTextLocation() | |||||||
| 	// this method is needed for vertical alignment alignment of text | 	// this method is needed for vertical alignment alignment of text | ||||||
| 	// when height of available text is smaller than height of widget | 	// when height of available text is smaller than height of widget | ||||||
| 	// in this case - we should add proper offset to display text at required position | 	// in this case - we should add proper offset to display text at required position | ||||||
| 	if (pos.h <= textSize.y) | 	if(pos.h <= textSize.y) | ||||||
| 		return pos; | 		return pos; | ||||||
|  |  | ||||||
| 	Point textSize(pos.w, (int)graphics->fonts[font]->getLineHeight() * (int)lines.size()); | 	Point textSize(pos.w, (int)graphics->fonts[font]->getLineHeight() * (int)lines.size()); | ||||||
| @@ -263,12 +263,12 @@ Rect CMultiLineLabel::getTextLocation() | |||||||
| CLabelGroup::CLabelGroup(EFonts Font, EAlignment Align, const SDL_Color & Color) | CLabelGroup::CLabelGroup(EFonts Font, EAlignment Align, const SDL_Color & Color) | ||||||
| 	: font(Font), align(Align), color(Color) | 	: font(Font), align(Align), color(Color) | ||||||
| { | { | ||||||
| 	defActions = 255-DISPOSE; | 	defActions = 255 - DISPOSE; | ||||||
| } | } | ||||||
|  |  | ||||||
| void CLabelGroup::add(int x, int y, const std::string &text) | void CLabelGroup::add(int x, int y, const std::string & text) | ||||||
| { | { | ||||||
| 	OBJECT_CONSTRUCTION_CUSTOM_CAPTURING(255-DISPOSE); | 	OBJECT_CONSTRUCTION_CUSTOM_CAPTURING(255 - DISPOSE); | ||||||
| 	labels.push_back(std::make_shared<CLabel>(x, y, font, align, color, text)); | 	labels.push_back(std::make_shared<CLabel>(x, y, font, align, color, text)); | ||||||
| } | } | ||||||
|  |  | ||||||
| @@ -277,11 +277,11 @@ size_t CLabelGroup::currentSize() const | |||||||
| 	return labels.size(); | 	return labels.size(); | ||||||
| } | } | ||||||
|  |  | ||||||
| CTextBox::CTextBox(std::string Text, const Rect &rect, int SliderStyle, EFonts Font, EAlignment Align, const SDL_Color &Color): | CTextBox::CTextBox(std::string Text, const Rect & rect, int SliderStyle, EFonts Font, EAlignment Align, const SDL_Color & Color) : | ||||||
| 	sliderStyle(SliderStyle), | 	sliderStyle(SliderStyle), | ||||||
| 	slider(nullptr) | 	slider(nullptr) | ||||||
| { | { | ||||||
| 	OBJECT_CONSTRUCTION_CAPTURING(255-DISPOSE); | 	OBJECT_CONSTRUCTION_CAPTURING(255 - DISPOSE); | ||||||
| 	label = std::make_shared<CMultiLineLabel>(rect, Font, Align, Color); | 	label = std::make_shared<CMultiLineLabel>(rect, Font, Align, Color); | ||||||
|  |  | ||||||
| 	type |= REDRAW_PARENT; | 	type |= REDRAW_PARENT; | ||||||
| @@ -310,7 +310,7 @@ void CTextBox::resize(Point newSize) | |||||||
| 	setText(label->getText()); // force refresh | 	setText(label->getText()); // force refresh | ||||||
| } | } | ||||||
|  |  | ||||||
| void CTextBox::setText(const std::string &text) | void CTextBox::setText(const std::string & text) | ||||||
| { | { | ||||||
| 	label->pos.w = pos.w; // reset to default before textSize.y check | 	label->pos.w = pos.w; // reset to default before textSize.y check | ||||||
| 	label->setText(text); | 	label->setText(text); | ||||||
| @@ -332,9 +332,9 @@ void CTextBox::setText(const std::string &text) | |||||||
| 		label->pos.w = pos.w - 32; | 		label->pos.w = pos.w - 32; | ||||||
| 		label->setText(text); | 		label->setText(text); | ||||||
|  |  | ||||||
| 		OBJECT_CONSTRUCTION_CUSTOM_CAPTURING(255-DISPOSE); | 		OBJECT_CONSTRUCTION_CUSTOM_CAPTURING(255 - DISPOSE); | ||||||
| 		slider = std::make_shared<CSlider>(Point(pos.w - 32, 0), pos.h, std::bind(&CTextBox::sliderMoved, this, _1), | 		slider = std::make_shared<CSlider>(Point(pos.w - 32, 0), pos.h, std::bind(&CTextBox::sliderMoved, this, _1), | ||||||
| 		                     label->pos.h, label->textSize.y, 0, false, CSlider::EStyle(sliderStyle)); | 			label->pos.h, label->textSize.y, 0, false, CSlider::EStyle(sliderStyle)); | ||||||
| 		slider->setScrollStep((int)graphics->fonts[label->font]->getLineHeight()); | 		slider->setScrollStep((int)graphics->fonts[label->font]->getLineHeight()); | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
| @@ -364,7 +364,7 @@ CGStatusBar::CGStatusBar(std::shared_ptr<CPicture> background_, EFonts Font, EAl | |||||||
| CGStatusBar::CGStatusBar(int x, int y, std::string name, int maxw) | CGStatusBar::CGStatusBar(int x, int y, std::string name, int maxw) | ||||||
| 	: CLabel(x, y, FONT_SMALL, CENTER) | 	: CLabel(x, y, FONT_SMALL, CENTER) | ||||||
| { | { | ||||||
| 	OBJECT_CONSTRUCTION_CAPTURING(255-DISPOSE); | 	OBJECT_CONSTRUCTION_CAPTURING(255 - DISPOSE); | ||||||
| 	background = std::make_shared<CPicture>(name); | 	background = std::make_shared<CPicture>(name); | ||||||
| 	pos = background->pos; | 	pos = background->pos; | ||||||
|  |  | ||||||
| @@ -391,12 +391,12 @@ void CGStatusBar::init() | |||||||
| Point CGStatusBar::getBorderSize() | Point CGStatusBar::getBorderSize() | ||||||
| { | { | ||||||
| 	//Width of borders where text should not be printed | 	//Width of borders where text should not be printed | ||||||
| 	static const Point borderSize(5,1); | 	static const Point borderSize(5, 1); | ||||||
|  |  | ||||||
| 	switch(alignment) | 	switch(alignment) | ||||||
| 	{ | 	{ | ||||||
| 	case TOPLEFT:     return Point(borderSize.x, borderSize.y); | 	case TOPLEFT:     return Point(borderSize.x, borderSize.y); | ||||||
| 	case CENTER:      return Point(pos.w/2, pos.h/2); | 	case CENTER:      return Point(pos.w / 2, pos.h / 2); | ||||||
| 	case BOTTOMRIGHT: return Point(pos.w - borderSize.x, pos.h - borderSize.y); | 	case BOTTOMRIGHT: return Point(pos.w - borderSize.x, pos.h - borderSize.y); | ||||||
| 	} | 	} | ||||||
| 	assert(0); | 	assert(0); | ||||||
| @@ -408,35 +408,43 @@ void CGStatusBar::lock(bool shouldLock) | |||||||
| 	textLock = shouldLock; | 	textLock = shouldLock; | ||||||
| } | } | ||||||
|  |  | ||||||
| CTextInput::CTextInput(const Rect &Pos, EFonts font, const CFunctionList<void(const std::string &)> &CB) | CTextInput::CTextInput(const Rect & Pos, EFonts font, const CFunctionList<void(const std::string &)> & CB) | ||||||
| 	: CLabel(Pos.x, Pos.y, font, CENTER), | 	: CLabel(Pos.x, Pos.y, font, CENTER), | ||||||
| 	cb(CB) | 	cb(CB), | ||||||
|  | 	CFocusable(std::make_shared<CKeyboardFocusListener>(this)) | ||||||
| { | { | ||||||
| 	type |= REDRAW_PARENT; | 	type |= REDRAW_PARENT; | ||||||
| 	focus = false; |  | ||||||
| 	pos.h = Pos.h; | 	pos.h = Pos.h; | ||||||
| 	pos.w = Pos.w; | 	pos.w = Pos.w; | ||||||
| 	captureAllKeys = true; | 	captureAllKeys = true; | ||||||
| 	background.reset(); | 	background.reset(); | ||||||
| 	addUsedEvents(LCLICK | KEYBOARD | TEXTINPUT); | 	addUsedEvents(LCLICK | KEYBOARD | TEXTINPUT); | ||||||
|  |  | ||||||
|  | #ifndef VCMI_ANDROID | ||||||
| 	giveFocus(); | 	giveFocus(); | ||||||
|  | #endif | ||||||
| } | } | ||||||
|  |  | ||||||
| CTextInput::CTextInput(const Rect & Pos, const Point & bgOffset, const std::string & bgName, const CFunctionList<void(const std::string &)> & CB) | CTextInput::CTextInput(const Rect & Pos, const Point & bgOffset, const std::string & bgName, const CFunctionList<void(const std::string &)> & CB) | ||||||
| 	:cb(CB) | 	:cb(CB), 	CFocusable(std::make_shared<CKeyboardFocusListener>(this)) | ||||||
| { | { | ||||||
| 	focus = false; |  | ||||||
| 	pos += Pos; | 	pos += Pos; | ||||||
|  | 	pos.h = Pos.h; | ||||||
|  | 	pos.w = Pos.w; | ||||||
|  |  | ||||||
| 	captureAllKeys = true; | 	captureAllKeys = true; | ||||||
| 	OBJ_CONSTRUCTION; | 	OBJ_CONSTRUCTION; | ||||||
| 	background = std::make_shared<CPicture>(bgName, bgOffset.x, bgOffset.y); | 	background = std::make_shared<CPicture>(bgName, bgOffset.x, bgOffset.y); | ||||||
| 	addUsedEvents(LCLICK | KEYBOARD | TEXTINPUT); | 	addUsedEvents(LCLICK | KEYBOARD | TEXTINPUT); | ||||||
|  |  | ||||||
|  | #ifndef VCMI_ANDROID | ||||||
| 	giveFocus(); | 	giveFocus(); | ||||||
|  | #endif | ||||||
| } | } | ||||||
|  |  | ||||||
| CTextInput::CTextInput(const Rect & Pos, SDL_Surface * srf) | CTextInput::CTextInput(const Rect & Pos, SDL_Surface * srf) | ||||||
|  | 	:CFocusable(std::make_shared<CKeyboardFocusListener>(this)) | ||||||
| { | { | ||||||
| 	focus = false; |  | ||||||
| 	pos += Pos; | 	pos += Pos; | ||||||
| 	captureAllKeys = true; | 	captureAllKeys = true; | ||||||
| 	OBJ_CONSTRUCTION; | 	OBJ_CONSTRUCTION; | ||||||
| @@ -450,35 +458,48 @@ CTextInput::CTextInput(const Rect & Pos, SDL_Surface * srf) | |||||||
| 	pos.h = background->pos.h; | 	pos.h = background->pos.h; | ||||||
| 	background->pos = pos; | 	background->pos = pos; | ||||||
| 	addUsedEvents(LCLICK | KEYBOARD | TEXTINPUT); | 	addUsedEvents(LCLICK | KEYBOARD | TEXTINPUT); | ||||||
| 	giveFocus(); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| void CTextInput::focusGot() | #ifndef VCMI_ANDROID | ||||||
| { | 	giveFocus(); | ||||||
| 	CSDL_Ext::startTextInput(&pos); |  | ||||||
| #ifdef VCMI_ANDROID |  | ||||||
| 	notifyAndroidTextInputChanged(text); |  | ||||||
| #endif | #endif | ||||||
| } | } | ||||||
|  |  | ||||||
| void CTextInput::focusLost() | std::atomic<int> CKeyboardFocusListener::usageIndex(0); | ||||||
|  |  | ||||||
|  | CKeyboardFocusListener::CKeyboardFocusListener(CTextInput * textInput) | ||||||
|  | 	:textInput(textInput) | ||||||
| { | { | ||||||
| 	CSDL_Ext::stopTextInput(); |  | ||||||
| } | } | ||||||
|  |  | ||||||
|  | void CKeyboardFocusListener::focusGot() | ||||||
|  | { | ||||||
|  | 	CSDL_Ext::startTextInput(&textInput->pos); | ||||||
|  | #ifdef VCMI_ANDROID | ||||||
|  | 	textInput->notifyAndroidTextInputChanged(textInput->text); | ||||||
|  | #endif | ||||||
|  | 	usageIndex++; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | void CKeyboardFocusListener::focusLost() | ||||||
|  | { | ||||||
|  | 	if(0 == --usageIndex) | ||||||
|  | 	{ | ||||||
|  | 		CSDL_Ext::stopTextInput(); | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
| std::string CTextInput::visibleText() | std::string CTextInput::visibleText() | ||||||
| { | { | ||||||
| 	return focus ? text + newText + "_" : text; | 	return focus ? text + newText + "_" : text; | ||||||
| } | } | ||||||
|  |  | ||||||
| void CTextInput::clickLeft( tribool down, bool previousState ) | void CTextInput::clickLeft(tribool down, bool previousState) | ||||||
| { | { | ||||||
| 	if(down && !focus) | 	if(down && !focus) | ||||||
| 		giveFocus(); | 		giveFocus(); | ||||||
| } | } | ||||||
|  |  | ||||||
| void CTextInput::keyPressed( const SDL_KeyboardEvent & key ) | void CTextInput::keyPressed(const SDL_KeyboardEvent & key) | ||||||
| { | { | ||||||
|  |  | ||||||
| 	if(!focus || key.state != SDL_PRESSED) | 	if(!focus || key.state != SDL_PRESSED) | ||||||
| @@ -513,7 +534,7 @@ void CTextInput::keyPressed( const SDL_KeyboardEvent & key ) | |||||||
| 		break; | 		break; | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	if (redrawNeeded) | 	if(redrawNeeded) | ||||||
| 	{ | 	{ | ||||||
| 		redraw(); | 		redraw(); | ||||||
| 		cb(text); | 		cb(text); | ||||||
| @@ -523,7 +544,7 @@ void CTextInput::keyPressed( const SDL_KeyboardEvent & key ) | |||||||
| 	} | 	} | ||||||
| } | } | ||||||
|  |  | ||||||
| void CTextInput::setText( const std::string &nText, bool callCb ) | void CTextInput::setText(const std::string & nText, bool callCb) | ||||||
| { | { | ||||||
| 	CLabel::setText(nText); | 	CLabel::setText(nText); | ||||||
| 	if(callCb) | 	if(callCb) | ||||||
| @@ -550,8 +571,8 @@ void CTextInput::textInputed(const SDL_TextInputEvent & event) | |||||||
|  |  | ||||||
| 	text += event.text; | 	text += event.text; | ||||||
|  |  | ||||||
| 	filters(text,oldText); | 	filters(text, oldText); | ||||||
| 	if (text != oldText) | 	if(text != oldText) | ||||||
| 	{ | 	{ | ||||||
| 		redraw(); | 		redraw(); | ||||||
| 		cb(text); | 		cb(text); | ||||||
| @@ -570,7 +591,7 @@ void CTextInput::textEdited(const SDL_TextEditingEvent & event) | |||||||
|  |  | ||||||
| 	newText = event.text; | 	newText = event.text; | ||||||
| 	redraw(); | 	redraw(); | ||||||
| 	cb(text+newText); | 	cb(text + newText); | ||||||
|  |  | ||||||
| #ifdef VCMI_ANDROID | #ifdef VCMI_ANDROID | ||||||
| 	auto editedText = text + newText; | 	auto editedText = text + newText; | ||||||
| @@ -582,7 +603,7 @@ void CTextInput::filenameFilter(std::string & text, const std::string &) | |||||||
| { | { | ||||||
| 	static const std::string forbiddenChars = "<>:\"/\\|?*\r\n"; //if we are entering a filename, some special characters won't be allowed | 	static const std::string forbiddenChars = "<>:\"/\\|?*\r\n"; //if we are entering a filename, some special characters won't be allowed | ||||||
| 	size_t pos; | 	size_t pos; | ||||||
| 	while ((pos = text.find_first_of(forbiddenChars)) != std::string::npos) | 	while((pos = text.find_first_of(forbiddenChars)) != std::string::npos) | ||||||
| 		text.erase(pos, 1); | 		text.erase(pos, 1); | ||||||
| } | } | ||||||
|  |  | ||||||
| @@ -590,16 +611,16 @@ void CTextInput::numberFilter(std::string & text, const std::string & oldText, i | |||||||
| { | { | ||||||
| 	assert(minValue < maxValue); | 	assert(minValue < maxValue); | ||||||
|  |  | ||||||
| 	if (text.empty()) | 	if(text.empty()) | ||||||
| 		text = "0"; | 		text = "0"; | ||||||
|  |  | ||||||
| 	size_t pos = 0; | 	size_t pos = 0; | ||||||
| 	if (text[0] == '-') //allow '-' sign as first symbol only | 	if(text[0] == '-') //allow '-' sign as first symbol only | ||||||
| 		pos++; | 		pos++; | ||||||
|  |  | ||||||
| 	while (pos < text.size()) | 	while(pos < text.size()) | ||||||
| 	{ | 	{ | ||||||
| 		if (text[pos] < '0' || text[pos] > '9') | 		if(text[pos] < '0' || text[pos] > '9') | ||||||
| 		{ | 		{ | ||||||
| 			text = oldText; | 			text = oldText; | ||||||
| 			return; //new text is not number. | 			return; //new text is not number. | ||||||
| @@ -609,9 +630,9 @@ void CTextInput::numberFilter(std::string & text, const std::string & oldText, i | |||||||
| 	try | 	try | ||||||
| 	{ | 	{ | ||||||
| 		int value = boost::lexical_cast<int>(text); | 		int value = boost::lexical_cast<int>(text); | ||||||
| 		if (value < minValue) | 		if(value < minValue) | ||||||
| 			text = boost::lexical_cast<std::string>(minValue); | 			text = boost::lexical_cast<std::string>(minValue); | ||||||
| 		else if (value > maxValue) | 		else if(value > maxValue) | ||||||
| 			text = boost::lexical_cast<std::string>(maxValue); | 			text = boost::lexical_cast<std::string>(maxValue); | ||||||
| 	} | 	} | ||||||
| 	catch(boost::bad_lexical_cast &) | 	catch(boost::bad_lexical_cast &) | ||||||
| @@ -625,7 +646,7 @@ void CTextInput::numberFilter(std::string & text, const std::string & oldText, i | |||||||
| #ifdef VCMI_ANDROID | #ifdef VCMI_ANDROID | ||||||
| void CTextInput::notifyAndroidTextInputChanged(std::string & text) | void CTextInput::notifyAndroidTextInputChanged(std::string & text) | ||||||
| { | { | ||||||
| 	if (!focus) | 	if(!focus) | ||||||
| 		return; | 		return; | ||||||
|  |  | ||||||
| 	auto fun = [&text](JNIEnv * env, jclass cls, jmethodID method) | 	auto fun = [&text](JNIEnv * env, jclass cls, jmethodID method) | ||||||
| @@ -641,6 +662,12 @@ void CTextInput::notifyAndroidTextInputChanged(std::string & text) | |||||||
| #endif //VCMI_ANDROID | #endif //VCMI_ANDROID | ||||||
|  |  | ||||||
| CFocusable::CFocusable() | CFocusable::CFocusable() | ||||||
|  | 	:CFocusable(std::make_shared<IFocusListener>()) | ||||||
|  | { | ||||||
|  | } | ||||||
|  |  | ||||||
|  | CFocusable::CFocusable(std::shared_ptr<IFocusListener> focusListener) | ||||||
|  | 	: focusListener(focusListener) | ||||||
| { | { | ||||||
| 	focus = false; | 	focus = false; | ||||||
| 	focusables.push_back(this); | 	focusables.push_back(this); | ||||||
| @@ -648,24 +675,30 @@ CFocusable::CFocusable() | |||||||
|  |  | ||||||
| CFocusable::~CFocusable() | CFocusable::~CFocusable() | ||||||
| { | { | ||||||
| 	if(inputWithFocus == this) | 	if(hasFocus()) | ||||||
| 	{ | 	{ | ||||||
| 		focusLost(); |  | ||||||
| 		inputWithFocus = nullptr; | 		inputWithFocus = nullptr; | ||||||
|  | 		focusListener->focusLost(); | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	focusables -= this; | 	focusables -= this; | ||||||
| } | } | ||||||
|  |  | ||||||
|  | bool CFocusable::hasFocus() const | ||||||
|  | { | ||||||
|  | 	return inputWithFocus == this; | ||||||
|  | } | ||||||
|  |  | ||||||
| void CFocusable::giveFocus() | void CFocusable::giveFocus() | ||||||
| { | { | ||||||
| 	focus = true; | 	focus = true; | ||||||
| 	focusGot(); | 	focusListener->focusGot(); | ||||||
| 	redraw(); | 	redraw(); | ||||||
|  |  | ||||||
| 	if(inputWithFocus) | 	if(inputWithFocus) | ||||||
| 	{ | 	{ | ||||||
| 		inputWithFocus->focus = false; | 		inputWithFocus->focus = false; | ||||||
|  | 		inputWithFocus->focusListener->focusLost(); | ||||||
| 		inputWithFocus->redraw(); | 		inputWithFocus->redraw(); | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
|   | |||||||
| @@ -48,12 +48,12 @@ public: | |||||||
|  |  | ||||||
| 	std::string getText(); | 	std::string getText(); | ||||||
| 	virtual void setAutoRedraw(bool option); | 	virtual void setAutoRedraw(bool option); | ||||||
| 	virtual void setText(const std::string &Txt); | 	virtual void setText(const std::string & Txt); | ||||||
| 	virtual void setColor(const SDL_Color & Color); | 	virtual void setColor(const SDL_Color & Color); | ||||||
| 	size_t getWidth(); | 	size_t getWidth(); | ||||||
|  |  | ||||||
| 	CLabel(int x=0, int y=0, EFonts Font = FONT_SMALL, EAlignment Align = TOPLEFT, | 	CLabel(int x = 0, int y = 0, EFonts Font = FONT_SMALL, EAlignment Align = TOPLEFT, | ||||||
| 	       const SDL_Color &Color = Colors::WHITE, const std::string &Text =  ""); | 		const SDL_Color & Color = Colors::WHITE, const std::string & Text = ""); | ||||||
| 	void showAll(SDL_Surface * to) override; //shows statusbar (with current text) | 	void showAll(SDL_Surface * to) override; //shows statusbar (with current text) | ||||||
| }; | }; | ||||||
|  |  | ||||||
| @@ -65,8 +65,8 @@ class CLabelGroup : public CIntObject | |||||||
| 	EAlignment align; | 	EAlignment align; | ||||||
| 	SDL_Color color; | 	SDL_Color color; | ||||||
| public: | public: | ||||||
| 	CLabelGroup(EFonts Font = FONT_SMALL, EAlignment Align = TOPLEFT, const SDL_Color &Color = Colors::WHITE); | 	CLabelGroup(EFonts Font = FONT_SMALL, EAlignment Align = TOPLEFT, const SDL_Color & Color = Colors::WHITE); | ||||||
| 	void add(int x=0, int y=0, const std::string &text =  ""); | 	void add(int x = 0, int y = 0, const std::string & text = ""); | ||||||
| 	size_t currentSize() const; | 	size_t currentSize() const; | ||||||
| }; | }; | ||||||
|  |  | ||||||
| @@ -80,15 +80,15 @@ class CMultiLineLabel : public CLabel | |||||||
| 	// area of text that actually will be printed, default is widget size | 	// area of text that actually will be printed, default is widget size | ||||||
| 	Rect visibleSize; | 	Rect visibleSize; | ||||||
|  |  | ||||||
| 	void splitText(const std::string &Txt, bool redrawAfter); | 	void splitText(const std::string & Txt, bool redrawAfter); | ||||||
| 	Rect getTextLocation(); | 	Rect getTextLocation(); | ||||||
| public: | public: | ||||||
| 	// total size of text, x = longest line of text, y = total height of lines | 	// total size of text, x = longest line of text, y = total height of lines | ||||||
| 	Point textSize; | 	Point textSize; | ||||||
|  |  | ||||||
| 	CMultiLineLabel(Rect position, EFonts Font = FONT_SMALL, EAlignment Align = TOPLEFT, const SDL_Color &Color = Colors::WHITE, const std::string &Text =  ""); | 	CMultiLineLabel(Rect position, EFonts Font = FONT_SMALL, EAlignment Align = TOPLEFT, const SDL_Color & Color = Colors::WHITE, const std::string & Text = ""); | ||||||
|  |  | ||||||
| 	void setText(const std::string &Txt) override; | 	void setText(const std::string & Txt) override; | ||||||
| 	void showAll(SDL_Surface * to) override; | 	void showAll(SDL_Surface * to) override; | ||||||
|  |  | ||||||
| 	void setVisibleSize(Rect visibleSize, bool redrawElement = true); | 	void setVisibleSize(Rect visibleSize, bool redrawElement = true); | ||||||
| @@ -140,24 +140,50 @@ public: | |||||||
| 	void lock(bool shouldLock); //If true, current text cannot be changed until lock(false) is called | 	void lock(bool shouldLock); //If true, current text cannot be changed until lock(false) is called | ||||||
| }; | }; | ||||||
|  |  | ||||||
|  | class CFocusable; | ||||||
|  |  | ||||||
|  | class IFocusListener | ||||||
|  | { | ||||||
|  | public: | ||||||
|  | 	virtual void focusGot() {}; | ||||||
|  | 	virtual void focusLost() {}; | ||||||
|  | 	virtual ~IFocusListener() = default; | ||||||
|  | }; | ||||||
|  |  | ||||||
| /// UIElement which can get input focus | /// UIElement which can get input focus | ||||||
| class CFocusable : public virtual CIntObject | class CFocusable : public virtual CIntObject | ||||||
| { | { | ||||||
| protected: | private: | ||||||
| 	virtual void focusGot(){}; | 	std::shared_ptr<IFocusListener> focusListener; | ||||||
| 	virtual void focusLost(){}; |  | ||||||
| public: | public: | ||||||
| 	bool focus; //only one focusable control can have focus at one moment | 	bool focus; //only one focusable control can have focus at one moment | ||||||
|  |  | ||||||
| 	void giveFocus(); //captures focus | 	void giveFocus(); //captures focus | ||||||
| 	void moveFocus(); //moves focus to next active control (may be used for tab switching) | 	void moveFocus(); //moves focus to next active control (may be used for tab switching) | ||||||
|  | 	bool hasFocus() const; | ||||||
|  |  | ||||||
|  | 	static std::list<CFocusable *> focusables; //all existing objs | ||||||
|  | 	static CFocusable * inputWithFocus; //who has focus now | ||||||
|  |  | ||||||
| 	static std::list<CFocusable*> focusables; //all existing objs |  | ||||||
| 	static CFocusable *inputWithFocus; //who has focus now |  | ||||||
| 	CFocusable(); | 	CFocusable(); | ||||||
|  | 	CFocusable(std::shared_ptr<IFocusListener> focusListener); | ||||||
| 	~CFocusable(); | 	~CFocusable(); | ||||||
| }; | }; | ||||||
|  |  | ||||||
|  | class CTextInput; | ||||||
|  | class CKeyboardFocusListener : public IFocusListener | ||||||
|  | { | ||||||
|  | private: | ||||||
|  | 	static std::atomic<int> usageIndex; | ||||||
|  | 	CTextInput * textInput; | ||||||
|  |  | ||||||
|  | public: | ||||||
|  | 	CKeyboardFocusListener(CTextInput * textInput); | ||||||
|  | 	void focusGot() override; | ||||||
|  | 	void focusLost() override; | ||||||
|  | }; | ||||||
|  |  | ||||||
| /// Text input box where players can enter text | /// Text input box where players can enter text | ||||||
| class CTextInput : public CLabel, public CFocusable | class CTextInput : public CLabel, public CFocusable | ||||||
| { | { | ||||||
| @@ -165,20 +191,17 @@ class CTextInput : public CLabel, public CFocusable | |||||||
| protected: | protected: | ||||||
| 	std::string visibleText() override; | 	std::string visibleText() override; | ||||||
|  |  | ||||||
| 	void focusGot() override; |  | ||||||
| 	void focusLost() override; |  | ||||||
|  |  | ||||||
| #ifdef VCMI_ANDROID | #ifdef VCMI_ANDROID | ||||||
| 	void notifyAndroidTextInputChanged(std::string & text); | 	void notifyAndroidTextInputChanged(std::string & text); | ||||||
| #endif | #endif | ||||||
| public: | public: | ||||||
| 	CFunctionList<void(const std::string &)> cb; | 	CFunctionList<void(const std::string &)> cb; | ||||||
| 	CFunctionList<void(std::string &, const std::string &)> filters; | 	CFunctionList<void(std::string &, const std::string &)> filters; | ||||||
| 	void setText(const std::string &nText, bool callCb = false); | 	void setText(const std::string & nText, bool callCb = false); | ||||||
|  |  | ||||||
| 	CTextInput(const Rect &Pos, EFonts font, const CFunctionList<void(const std::string &)> &CB); | 	CTextInput(const Rect & Pos, EFonts font, const CFunctionList<void(const std::string &)> & CB); | ||||||
| 	CTextInput(const Rect &Pos, const Point &bgOffset, const std::string &bgName, const CFunctionList<void(const std::string &)> &CB); | 	CTextInput(const Rect & Pos, const Point & bgOffset, const std::string & bgName, const CFunctionList<void(const std::string &)> & CB); | ||||||
| 	CTextInput(const Rect &Pos, SDL_Surface *srf = nullptr); | 	CTextInput(const Rect & Pos, SDL_Surface * srf = nullptr); | ||||||
|  |  | ||||||
| 	void clickLeft(tribool down, bool previousState) override; | 	void clickLeft(tribool down, bool previousState) override; | ||||||
| 	void keyPressed(const SDL_KeyboardEvent & key) override; | 	void keyPressed(const SDL_KeyboardEvent & key) override; | ||||||
| @@ -188,8 +211,10 @@ public: | |||||||
| 	void textEdited(const SDL_TextEditingEvent & event) override; | 	void textEdited(const SDL_TextEditingEvent & event) override; | ||||||
|  |  | ||||||
| 	//Filter that will block all characters not allowed in filenames | 	//Filter that will block all characters not allowed in filenames | ||||||
| 	static void filenameFilter(std::string &text, const std::string & oldText); | 	static void filenameFilter(std::string & text, const std::string & oldText); | ||||||
| 	//Filter that will allow only input of numbers in range min-max (min-max are allowed) | 	//Filter that will allow only input of numbers in range min-max (min-max are allowed) | ||||||
| 	//min-max should be set via something like std::bind | 	//min-max should be set via something like std::bind | ||||||
| 	static void numberFilter(std::string &text, const std::string & oldText, int minValue, int maxValue); | 	static void numberFilter(std::string & text, const std::string & oldText, int minValue, int maxValue); | ||||||
|  |  | ||||||
|  | 	friend class CKeyboardFocusListener; | ||||||
| }; | }; | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user