axmol/core/ui/UIEditBox/UIEditBoxImpl-winrt.cpp

529 lines
19 KiB
C++

///****************************************************************************
//Copyright (c) 2014 cocos2d-x.org
//Copyright (c) 2017-2018 Xiamen Yaji Software Co., Ltd.
//
//https://axmolengine.github.io/
//
//* Portions Copyright (c) Microsoft Open Technologies, Inc.
//* All Rights Reserved
//
//Permission is hereby granted, free of charge, to any person obtaining a copy
//of this software and associated documentation files (the "Software"), to deal
//in the Software without restriction, including without limitation the rights
//to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
//copies of the Software, and to permit persons to whom the Software is
//furnished to do so, subject to the following conditions:
//
//The above copyright notice and this permission notice shall be included in
//all copies or substantial portions of the Software.
//
//THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
//IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
//FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
//AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
//LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
//OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
//THE SOFTWARE.
//****************************************************************************/
#include "platform/PlatformConfig.h"
#if (AX_TARGET_PLATFORM == AX_PLATFORM_WINRT)
#include "ui/UIEditBox/UIEditBoxImpl-winrt.h"
#include "platform/winrt/WinRTUtils.h"
#include "platform/winrt/GLViewImpl-winrt.h"
#include "2d/FontFreeType.h"
#include <winrt/Windows.UI.Xaml.Input.h>
#include <winrt/Windows.UI.ViewManagement.h>
#include <winrt/Windows.Foundation.Collections.h>
#include <winrt/Windows.UI.Xaml.Media.h>
#if defined(WINAPI_FAMILY) && WINAPI_FAMILY == WINAPI_FAMILY_PHONE_APP
#define XAML_TOP_PADDING 10.0f
#else
#define XAML_TOP_PADDING 0.0f
#endif
#define EDIT_BOX_PADDING 5.0f
namespace ax {
namespace ui {
static const winrt::hstring EDIT_BOX_XAML_NAME = L"axmol_editbox";
static const winrt::hstring CANVAS_XAML_NAME = L"axmol_canvas";
EditBoxImpl* __createSystemEditBox(EditBox* pEditBox)
{
return new UIEditBoxImplWinrt(pEditBox);
}
EditBoxWinRT::EditBoxWinRT(
winrt::delegate<Windows::Foundation::IInspectable const&, winrt::hstring const&> const& beginHandler,
winrt::delegate<Windows::Foundation::IInspectable const&, winrt::hstring const&> const& changeHandler,
winrt::delegate<Windows::Foundation::IInspectable const&, ax::EndEventArgs const&> const& endHandler)
:
_beginHandler(beginHandler),
_changeHandler(changeHandler),
_endHandler(endHandler),
_alignment(),
_initialText(L""),
_fontFamily(L"Segoe UI"),
_fontSize(12),
_password(false),
_isEditing(false),
_multiline(false),
_maxLength(0 /* unlimited */)
{
m_dispatcher = ax::GLViewImpl::sharedOpenGLView()->getDispatcher();
m_panel = ax::GLViewImpl::sharedOpenGLView()->getPanel();
}
void EditBoxWinRT::closeKeyboard()
{
m_dispatcher.get().RunAsync(Windows::UI::Core::CoreDispatcherPriority::Normal, [this]() {
removeTextBox();
_textBox = nullptr;
auto canvas = findXamlElement(m_panel.get(), CANVAS_XAML_NAME).as<Canvas>();
canvas.Visibility(Visibility::Collapsed);
});
}
Windows::UI::Xaml::Controls::Control EditBoxWinRT::createPasswordBox()
{
auto passwordBox = PasswordBox();
passwordBox.BorderThickness(Windows::UI::Xaml::Thickness{0});
passwordBox.Name(EDIT_BOX_XAML_NAME);
passwordBox.Width(_size.Width);
passwordBox.Height(_size.Height);
passwordBox.Foreground(Media::SolidColorBrush(_color));
passwordBox.FontSize(_fontSize);
passwordBox.FontFamily(Media::FontFamily(_fontFamily));
passwordBox.MaxLength(_maxLength);
passwordBox.Password(_initialText);
_changeToken = passwordBox.PasswordChanged({this, &ax::ui::EditBoxWinRT::onPasswordChanged});
return passwordBox;
}
Windows::UI::Xaml::Controls::Control EditBoxWinRT::createTextBox()
{
auto textBox = TextBox();
textBox.BorderThickness(Windows::UI::Xaml::Thickness{0});
textBox.Name(EDIT_BOX_XAML_NAME);
textBox.Width(_size.Width);
textBox.Height(_size.Height);
textBox.Foreground(Media::SolidColorBrush(_color));
textBox.FontSize(_fontSize);
textBox.FontFamily(Media::FontFamily(_fontFamily));
textBox.MaxLength(_maxLength);
textBox.AcceptsReturn(_multiline);
textBox.TextWrapping(_multiline ? TextWrapping::Wrap : TextWrapping::NoWrap);
textBox.Text(_initialText);
setInputScope(textBox);
_setTextHorizontalAlignment(textBox);
_changeToken = textBox.TextChanged({this, &ax::ui::EditBoxWinRT::onTextChanged});
return textBox;
}
void EditBoxWinRT::onPasswordChanged(Windows::Foundation::IInspectable const& sender,
Windows::UI::Xaml::RoutedEventArgs const& args)
{
onTextChanged(sender, nullptr);
}
void EditBoxWinRT::onTextChanged(Windows::Foundation::IInspectable const& sender,
Windows::UI::Xaml::Controls::TextChangedEventArgs const& e)
{
winrt::hstring text = L"";
if (_password) {
text = _textBox.as<PasswordBox>().Password();
}
else {
text = _textBox.as<TextBox>().Text();
}
std::shared_ptr<ax::InputEvent> inputEvent(new UIEditBoxEvent(*this, text, _changeHandler));
ax::GLViewImpl::sharedOpenGLView()->QueueEvent(inputEvent);
}
void EditBoxWinRT::onKeyDown(Windows::Foundation::IInspectable const& sender,
Windows::UI::Xaml::Input::KeyRoutedEventArgs const& args)
{
if (args.Key() == Windows::System::VirtualKey::Enter && !_multiline)
{
onLostFocus(nullptr, args);
}
else if (args.Key() == Windows::System::VirtualKey::Tab)
{
onLostFocus(nullptr, args);
}
}
void EditBoxWinRT::onGotFocus(Windows::Foundation::IInspectable const& sender,
Windows::UI::Xaml::RoutedEventArgs const& args)
{
Concurrency::critical_section::scoped_lock lock(_critical_section);
std::shared_ptr<ax::InputEvent> inputEvent(new UIEditBoxEvent(*this, winrt::hstring{}, _beginHandler));
ax::GLViewImpl::sharedOpenGLView()->QueueEvent(inputEvent);
_isEditing = true;
}
void EditBoxWinRT::onLostFocus(Windows::Foundation::IInspectable const& sender,
Windows::UI::Xaml::RoutedEventArgs const& args)
{
EditBoxDelegate::EditBoxEndAction action = EditBoxDelegate::EditBoxEndAction::UNKNOWN;
Windows::UI::Xaml::Input::KeyRoutedEventArgs keyArgs = args.try_as<Windows::UI::Xaml::Input::KeyRoutedEventArgs>();
if (keyArgs) {
if (keyArgs.Key() == Windows::System::VirtualKey::Enter && !_multiline)
{
action = EditBoxDelegate::EditBoxEndAction::RETURN;
}
else if (keyArgs.Key() == Windows::System::VirtualKey::Tab)
{
action = EditBoxDelegate::EditBoxEndAction::TAB_TO_NEXT;
}
}
_isEditing = false;
Concurrency::critical_section::scoped_lock lock(_critical_section);
winrt::hstring text = L"";
if (_password) {
text = _textBox.as<PasswordBox>().Password();
_textBox.as <PasswordBox>().PasswordChanged(_changeToken);
}
else {
text = _textBox.as<TextBox>().Text();
_textBox.as<TextBox>().TextChanged(_changeToken);
}
std::shared_ptr<ax::InputEvent> inputEvent(new UIEditBoxEndEvent(*this, text, static_cast<int>(action), _endHandler));
ax::GLViewImpl::sharedOpenGLView()->QueueEvent(inputEvent);
_textBox.LostFocus(_unfocusToken);
_textBox.GotFocus(_focusToken);
_textBox.KeyDown(_keydownToken);
closeKeyboard();
}
bool EditBoxWinRT::isEditing() {
return _isEditing;
}
void EditBoxWinRT::openKeyboard()
{
m_dispatcher.get().RunAsync(Windows::UI::Core::CoreDispatcherPriority::Normal, [this]() {
removeTextBox();
Canvas canvas = findXamlElement(m_panel.get(), CANVAS_XAML_NAME).as<Canvas>();
if (_password) {
_textBox = createPasswordBox();
}
else {
_textBox = createTextBox();
}
// Position the text box
canvas.SetLeft(_textBox, _rect.X);
canvas.SetTop(_textBox, _rect.Y - XAML_TOP_PADDING);
_setTexVerticalAlignment(_textBox);
_setPadding(_textBox);
// Finally, insert it into the XAML scene hierarchy and make the containing canvas visible
canvas.Children().InsertAt(0, _textBox);
canvas.Background(Media::SolidColorBrush());
canvas.Visibility(Visibility::Visible);
_keydownToken = _textBox.KeyDown({this, &ax::ui::EditBoxWinRT::onKeyDown});
_focusToken = _textBox.GotFocus({this, &ax::ui::EditBoxWinRT::onGotFocus});
_unfocusToken = _textBox.LostFocus({this, &ax::ui::EditBoxWinRT::onLostFocus});
_textBox.Focus(FocusState::Programmatic);
if (_password) {
_textBox.as<PasswordBox>().SelectAll();
}
else {
_textBox.as<TextBox>().Select(_initialText.size(), 0);
}
auto inputPane = Windows::UI::ViewManagement::InputPane::GetForCurrentView();
});
}
void EditBoxWinRT::setFontColor(Windows::UI::Color const& color)
{
_color = color;
}
void EditBoxWinRT::setFontFamily(winrt::hstring const& fontFamily)
{
_fontFamily = fontFamily;
}
void EditBoxWinRT::setFontSize(int fontSize)
{
_fontSize = fontSize;
}
void EditBoxWinRT::removeTextBox()
{
auto canvas = findXamlElement(m_panel.get(), CANVAS_XAML_NAME);
auto box = findXamlElement(canvas, EDIT_BOX_XAML_NAME);
removeXamlElement(canvas, box);
_isEditing = false;
}
void EditBoxWinRT::setInputFlag(int inputFlags) {
_password = false;
switch ((EditBox::InputFlag)inputFlags) {
case EditBox::InputFlag::PASSWORD:
_password = true;
break;
default:
AXLOG("Warning: cannot set INITIAL_CAPS_* input flags for WinRT edit boxes");
}
}
void EditBoxWinRT::setInputMode(int inputMode) {
_multiline = (EditBox::InputMode)inputMode == EditBox::InputMode::ANY;
_inputMode = inputMode;
}
void EditBoxWinRT::setTextHorizontalAlignment(int alignment) {
_alignment = alignment;
}
void EditBoxWinRT::setMaxLength(int maxLength) {
_maxLength = maxLength;
}
void EditBoxWinRT::_setTextHorizontalAlignment(TextBox textBox)
{
switch (_alignment) {
default:
case 0:
textBox.TextAlignment(TextAlignment::Left);
break;
case 1:
textBox.TextAlignment(TextAlignment::Center);
break;
case 2:
textBox.TextAlignment(TextAlignment::Right);
break;
}
}
void EditBoxWinRT::_setTexVerticalAlignment(Windows::UI::Xaml::Controls::Control textBox) {
textBox.VerticalAlignment(_multiline ? VerticalAlignment::Top : VerticalAlignment::Center);
}
void EditBoxWinRT::_setPadding(Windows::UI::Xaml::Controls::Control editBox)
{
double padding = EDIT_BOX_PADDING*ax::Director::getInstance()->getOpenGLView()->getScaleX();
if (_multiline) {
editBox.Padding(Thickness{padding, padding, 0.0, 0.0});
}
else {
editBox.Padding(Thickness{padding, 0.0, 0.0, 0.0});
}
}
void EditBoxWinRT::setInputScope(TextBox textBox)
{
InputScope inputScope = InputScope();
InputScopeName name = InputScopeName();
switch ((EditBox::InputMode)_inputMode) {
case EditBox::InputMode::SINGLE_LINE:
case EditBox::InputMode::ANY:
name.NameValue(InputScopeNameValue::Default);
break;
case EditBox::InputMode::EMAIL_ADDRESS:
name.NameValue(InputScopeNameValue::EmailSmtpAddress);
break;
case EditBox::InputMode::DECIMAL:
case EditBox::InputMode::NUMERIC:
name.NameValue(InputScopeNameValue::Number);
break;
case EditBox::InputMode::PHONE_NUMBER:
name.NameValue(InputScopeNameValue::TelephoneNumber);
break;
case EditBox::InputMode::URL:
name.NameValue(InputScopeNameValue::Url);
break;
}
textBox.InputScope(nullptr);
inputScope.Names().Append(name);
textBox.InputScope(inputScope);
}
void EditBoxWinRT::setPosition(Windows::Foundation::Rect const& rect)
{
_rect = rect;
}
void EditBoxWinRT::setSize(Windows::Foundation::Size const& size)
{
_size = size;
}
void EditBoxWinRT::setText(winrt::hstring const& text)
{
_initialText = text;
// If already editing
if (_isEditing) {
m_dispatcher.get().RunAsync(Windows::UI::Core::CoreDispatcherPriority::Normal, [this]() {
if (!_password) {
auto textBox = _textBox.as<TextBox>();
unsigned int currentStart = textBox.SelectionStart();
bool cursorAtEnd = currentStart == textBox.Text().size();
textBox.Text(_initialText);
if (cursorAtEnd || currentStart > textBox.Text().size())
{
currentStart = textBox.Text().size();
}
textBox.Select(currentStart, 0);
}
});
}
}
void EditBoxWinRT::setVisible(bool visible)
{
_visible = visible;
// If already editing
m_dispatcher.get().RunAsync(Windows::UI::Core::CoreDispatcherPriority::Normal, [this]() {
Canvas canvas = findXamlElement(m_panel.get(), CANVAS_XAML_NAME).as<Canvas>();
canvas.Visibility(_visible ? Visibility::Visible : Visibility::Collapsed);
});
}
UIEditBoxImplWinrt::UIEditBoxImplWinrt(EditBox* pEditText) : EditBoxImplCommon(pEditText)
{
auto beginHandler = ([this](Windows::Foundation::IInspectable const& sender, winrt::hstring const& arg) {
this->editBoxEditingDidBegin();
});
auto changeHandler = ([this](Windows::Foundation::IInspectable const& sender, winrt::hstring const& arg) {
auto text = PlatformStringToString(arg);
this->editBoxEditingChanged(text);
});
auto endHandler = ([this](Windows::Foundation::IInspectable const& sender, ax::EndEventArgs const& arg) {
auto text = PlatformStringToString(arg.GetText());
auto action = arg.GetAction();
this->editBoxEditingDidEnd(text, static_cast<ax::ui::EditBoxDelegate::EditBoxEndAction>(action));
this->onEndEditing(text);
});
_system_control_agile =
winrt::make_agile(winrt::make<EditBoxWinRT>(beginHandler, changeHandler, endHandler));
_system_control = _system_control_agile.get().as<EditBoxWinRT>();
}
void UIEditBoxImplWinrt::setNativeFont(const char* pFontName, int fontSize)
{
// fontSize
_fontSize = fontSize;
auto transform = _editBox->getNodeToWorldTransform();
ax::Vec3 scale;
transform.getScale(&scale);
_system_control->setFontSize(
_fontSize *
ax::Director::getInstance()->getOpenGLView()->getScaleY() /** scale.y*/);
// fontFamily
auto font = ax::FontFreeType::create(pFontName, fontSize, ax::GlyphCollection::DYNAMIC, ""sv);
if (font != nullptr) {
std::string fontName = "ms-appx:///Content/" + std::string(pFontName) +'#' + font->getFontFamily();
_system_control->setFontFamily(PlatformStringFromString(fontName));
}
}
void UIEditBoxImplWinrt::setNativeFontColor(const Color4B& color)
{
Windows::UI::Color win_color = { 0xFF, color.r, color.g, color.b };
_system_control->setFontColor(win_color);
}
void UIEditBoxImplWinrt::setNativeInputMode(EditBox::InputMode inputMode)
{
_system_control->setInputMode((int)inputMode);
}
void UIEditBoxImplWinrt::setNativeInputFlag(EditBox::InputFlag inputFlag)
{
_system_control->setInputFlag((int)inputFlag);
}
void UIEditBoxImplWinrt::setNativeTextHorizontalAlignment(ax::TextHAlignment alignment)
{
_system_control->setTextHorizontalAlignment((int)alignment);
}
void UIEditBoxImplWinrt::setNativeText(const char* pText)
{
_system_control->setText(PlatformStringFromString(pText));
}
void UIEditBoxImplWinrt::setNativeVisible(bool visible)
{
_system_control->setVisible(visible);
}
void UIEditBoxImplWinrt::updateNativeFrame(const Rect& rect)
{
}
void UIEditBoxImplWinrt::nativeOpenKeyboard()
{
// Update the text
_system_control->setText(PlatformStringFromString(getText()));
// Size
auto glView = ax::Director::getInstance()->getOpenGLView();
auto transform = _editBox->getNodeToWorldTransform();
ax::Vec3 scale;
transform.getScale(&scale);
Windows::Foundation::Size xamlSize = { _editBox->getContentSize().width * glView->getScaleX() * scale.x, _editBox->getContentSize().height * glView->getScaleY() * scale.y };
_system_control->setSize(xamlSize);
_system_control->setFontSize(_fontSize * ax::Director::getInstance()->getOpenGLView()->getScaleY() /** scale.y*/);
// Position
auto directorInstance = ax::Director::getInstance();
auto frameSize = glView->getFrameSize();
auto winSize = directorInstance->getWinSize();
auto leftBottom = _editBox->convertToWorldSpace(ax::Point::ZERO);
auto rightTop = _editBox->convertToWorldSpace(ax::Point(_editBox->getContentSize().width, _editBox->getContentSize().height));
Windows::Foundation::Rect rect;
rect.X = frameSize.width / 2 + (leftBottom.x - winSize.width / 2) * glView->getScaleX();
rect.Y = frameSize.height / 2 - (rightTop.y - winSize.height / 2) * glView->getScaleY();
rect.Width = (rightTop.x - leftBottom.x) * glView->getScaleX();
rect.Height = (rightTop.y - leftBottom.y) * glView->getScaleY();
_system_control->setPosition(rect);
// .. and open
_system_control->openKeyboard();
}
void UIEditBoxImplWinrt::nativeCloseKeyboard()
{
_system_control->closeKeyboard();
}
void UIEditBoxImplWinrt::setNativeMaxLength(int maxLength)
{
_system_control->setMaxLength(maxLength);
}
ax::Vec2 UIEditBoxImplWinrt::convertDesignCoordToXamlCoord(const ax::Vec2& designCoord)
{
auto glView = ax::Director::getInstance()->getOpenGLView();
float viewH = glView->getFrameSize().height;
Vec2 visiblePos = Vec2(designCoord.x * glView->getScaleX(), designCoord.y * glView->getScaleY());
Vec2 screenGLPos = visiblePos + glView->getViewPortRect().origin;
Vec2 xamlPos(screenGLPos.x, viewH - screenGLPos.y);
return xamlPos;
}
} // namespace ui
} // namespace cocos2d
#endif // (CC_TARGET_PLATFORM == CC_PLATFORM_WP8 || CC_TARGET_PLATFORM == CC_PLATFORM_WINRT)