blob: 075e734a838230c64d838ffe3cf7f2bb5b7a1ed5 [file] [log] [blame]
/**************************************************************************
* Copyright (C) 2016 by Savoir-faire Linux *
* Author: Jäger Nicolas <nicolas.jager@savoirfairelinux.com> *
* Author: Traczyk Andreas <andreas.traczyk@savoirfairelinux.com> *
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; either version 3 of the License, or *
* (at your option) any later version. *
* *
* This program is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU General Public License for more details. *
* *
* You should have received a copy of the GNU General Public License *
* along with this program. If not, see <http://www.gnu.org/licenses/>. *
**************************************************************************/
#include "pch.h"
#include "VideoPage.xaml.h"
#include <MemoryBuffer.h> // IMemoryBufferByteAccess
using namespace RingClientUWP::Views;
using namespace ViewModel;
using namespace Video;
using namespace Concurrency;
using namespace Platform;
using namespace Windows::Devices::Enumeration;
using namespace Windows::Foundation;
using namespace Windows::Foundation::Collections;
using namespace Windows::UI::Xaml;
using namespace Windows::UI::Xaml::Controls;
using namespace Windows::UI::Xaml::Controls::Primitives;
using namespace Windows::UI::Xaml::Data;
using namespace Windows::UI::Xaml::Input;
using namespace Windows::UI::Xaml::Media;
using namespace Windows::UI::Xaml::Navigation;
using namespace Windows::Media::Capture;
using namespace Windows::ApplicationModel::Core;
using namespace Windows::UI::Core;
using namespace Windows::UI;
using namespace Windows::Graphics::Display;
using namespace Windows::Graphics::Imaging;
using namespace Windows::Media;
using namespace Windows::UI::Xaml::Media::Imaging;
using namespace Windows::UI::Xaml::Media::Animation;
using namespace Windows::UI::Xaml::Shapes;
using namespace Windows::Devices::Sensors;
using namespace Windows::UI::Input;
VideoPage::VideoPage()
{
InitializeComponent();
barFading = true;
Page::NavigationCacheMode = Navigation::NavigationCacheMode::Required;
VideoManager::instance->rendererManager()->writeVideoFrame +=
ref new WriteVideoFrame([this](String^ id, uint8_t* buf, int width, int height)
{
CoreApplication::MainView->CoreWindow->Dispatcher->RunAsync(CoreDispatcherPriority::High,
ref new DispatchedHandler([=]() {
try {
currentRendererWrapper = VideoManager::instance->rendererManager()->renderer(id);
if (!currentRendererWrapper) {
return;
}
else {
currentRendererWrapper->isRendering = true;
create_task(WriteFrameAsSoftwareBitmapAsync(id, buf, width, height))
.then([=](task<void> previousTask) {
try {
previousTask.get();
}
catch (Platform::Exception^ e) {
EXC_(e);
}
});
}
}
catch(Platform::COMException^ e) {
EXC_(e);
}
}));
});
VideoManager::instance->captureManager()->startPreviewing +=
ref new StartPreviewing([this]()
{
_PreviewImage_->Visibility = Windows::UI::Xaml::Visibility::Visible;
_PreviewImageResizer_->Visibility = Windows::UI::Xaml::Visibility::Visible;
_PreviewImage_->FlowDirection = VideoManager::instance->captureManager()->mirroringPreview ?
Windows::UI::Xaml::FlowDirection::RightToLeft :
Windows::UI::Xaml::FlowDirection::LeftToRight;
double aspectRatio = VideoManager::instance->captureManager()->aspectRatio();
_PreviewImage_->Height = ( _videoContent_->ActualHeight / 4 );
_PreviewImage_->Width = _PreviewImage_->Height * aspectRatio;
_PreviewImageRect_->Width = _PreviewImage_->Width;
_PreviewImageRect_->Height = _PreviewImage_->Height;
});
VideoManager::instance->captureManager()->stopPreviewing +=
ref new StopPreviewing([this]()
{
_PreviewImage_->Source = nullptr;
_PreviewImage_->Visibility = Windows::UI::Xaml::Visibility::Collapsed;
_PreviewImageResizer_->Visibility = Windows::UI::Xaml::Visibility::Collapsed;
});
VideoManager::instance->captureManager()->getSink +=
ref new GetSink([this]()
{
return _PreviewImage_;
});
VideoManager::instance->rendererManager()->clearRenderTarget +=
ref new ClearRenderTarget([this]()
{
_IncomingVideoImage_->Source = nullptr;
});
RingD::instance->windowResized +=
ref new WindowResized([=](float width, float height)
{
});
RingD::instance->incomingAccountMessage +=
ref new IncomingAccountMessage([&](String^ accountId, String^ from, String^ payload)
{
scrollDown();
});
RingD::instance->vCardUpdated += ref new VCardUpdated([&](Contact^ contact)
{
Utils::runOnUIThread([this, contact]() {
SmartPanelItemsViewModel::instance->update({ "_bestName2", "_avatarImage" });
updatePageContent();
});
});
RingD::instance->stateChange +=
ref new StateChange([&](String^ callId, CallStatus state, int code)
{
switch (state) {
case CallStatus::IN_PROGRESS:
{
for (auto it : SmartPanelItemsViewModel::instance->itemsList)
if (it->_callStatus != CallStatus::IN_PROGRESS && it->_callId != callId)
RingD::instance->pauseCall(Utils::toString(it->_callId));
_callPaused_->Visibility = Windows::UI::Xaml::Visibility::Collapsed;
_IncomingVideoImage_->Visibility = Windows::UI::Xaml::Visibility::Visible;
callTimerUpdater->Start();
updateCallTimer(nullptr, nullptr);
RingD::instance->startSmartInfo(500);
videoPage_InputHandlerToken = Window::Current->CoreWindow->KeyDown +=
ref new TypedEventHandler<CoreWindow^, KeyEventArgs^>(this, &VideoPage::_corewindow__KeyDown);
break;
}
case CallStatus::ENDED:
{
Video::VideoManager::instance->rendererManager()->raiseClearRenderTarget();
if (RingD::instance->isFullScreen)
RingD::instance->setWindowedMode();
/* "close" the chat panel */
closeChatPanel();
callTimerUpdater->Stop();
//RingD::instance->stopSmartInfo();
Window::Current->CoreWindow->KeyDown -= videoPage_InputHandlerToken;
_smartInfoBorder_->Visibility = VIS::Collapsed;
break;
}
case CallStatus::PEER_PAUSED:
case CallStatus::PAUSED:
_callPaused_->Visibility = Windows::UI::Xaml::Visibility::Visible;
_IncomingVideoImage_->Visibility = Windows::UI::Xaml::Visibility::Collapsed;
break;
}
});
RingD::instance->messageDataLoaded += ref new MessageDataLoaded([&]() { scrollDown(); });
RingD::instance->updateSmartInfo += ref new RingClientUWP::UpdateSmartInfo(this, &RingClientUWP::Views::VideoPage::OnsmartInfoUpdated);
RingD::instance->incomingMessage += ref new RingClientUWP::IncomingMessage(this, &RingClientUWP::Views::VideoPage::OnincomingMessage);
RingD::instance->incomingVideoMuted += ref new RingClientUWP::IncomingVideoMuted(this, &RingClientUWP::Views::VideoPage::OnincomingVideoMuted);
VideoManager::instance->captureManager()->startPreviewing += ref new RingClientUWP::StartPreviewing(this, &RingClientUWP::Views::VideoPage::OnstartPreviewing);
VideoManager::instance->captureManager()->stopPreviewing += ref new RingClientUWP::StopPreviewing(this, &RingClientUWP::Views::VideoPage::OnstopPreviewing);
RingD::instance->audioMuted += ref new RingClientUWP::AudioMuted(this, &RingClientUWP::Views::VideoPage::OnaudioMuted);
RingD::instance->videoMuted += ref new RingClientUWP::VideoMuted(this, &RingClientUWP::Views::VideoPage::OnvideoMuted);
InitManipulationTransforms();
_PreviewImage_->ManipulationDelta += ref new ManipulationDeltaEventHandler(this, &VideoPage::PreviewImage_ManipulationDelta);
_PreviewImage_->ManipulationCompleted += ref new ManipulationCompletedEventHandler(this, &VideoPage::PreviewImage_ManipulationCompleted);
_PreviewImage_->ManipulationMode =
ManipulationModes::TranslateX |
ManipulationModes::TranslateY;
_PreviewImageResizer_->ManipulationDelta += ref new ManipulationDeltaEventHandler(this, &VideoPage::PreviewImageResizer_ManipulationDelta);
_PreviewImageResizer_->ManipulationCompleted += ref new ManipulationCompletedEventHandler(this, &VideoPage::PreviewImageResizer_ManipulationCompleted);
_PreviewImageResizer_->ManipulationMode = ManipulationModes::TranslateY;
_chatPanelResizeBarGrid_->ManipulationDelta += ref new ManipulationDeltaEventHandler(this, &VideoPage::_chatPanelResizeBarGrid__ManipulationDelta);
_chatPanelResizeBarGrid_->ManipulationCompleted += ref new ManipulationCompletedEventHandler(this, &VideoPage::_chatPanelResizeBarGrid__ManipulationCompleted);
_chatPanelResizeBarGrid_->ManipulationMode =
ManipulationModes::TranslateX |
ManipulationModes::TranslateY;
TimeSpan timeSpan;
timeSpan.Duration = static_cast<long long>(1e7);
callTimerUpdater = ref new DispatcherTimer;
callTimerUpdater->Interval = timeSpan;
callTimerUpdater->Tick += ref new Windows::Foundation::EventHandler<Object^>(this, &VideoPage::updateCallTimer);
showSmartInfo = false;
}
void
VideoPage::OnsmartInfoUpdated(const std::map<std::string, std::string>& info)
{
auto smartInfo = Utils::convertMap(info);
if (auto selectedItem = SmartPanelItemsViewModel::instance->_selectedItem)
_si_CallId_->Text = "CallID: " + selectedItem->_callId;
if (smartInfo->HasKey("local FPS"))
_si_fps1_->Text = smartInfo->Lookup("local FPS");
if (smartInfo->HasKey("local video codec"))
_si_vc1_->Text = smartInfo->Lookup("local video codec");
if (smartInfo->HasKey("local audio codec"))
_si_ac1_->Text = smartInfo->Lookup("local audio codec");
auto localResolution = VideoManager::instance->captureManager()->activeDevice->currentResolution();
_si_res1_->Text = localResolution->getFriendlyName();
if (smartInfo->HasKey("remote FPS"))
_si_fps2_->Text = smartInfo->Lookup("remote FPS") ;
if (smartInfo->HasKey("remote video codec"))
_si_vc2_->Text = smartInfo->Lookup("remote video codec");
if (smartInfo->HasKey("remote audio codec"))
_si_ac2_->Text = smartInfo->Lookup("remote audio codec");
if (currentRendererWrapper) {
auto remoteResolution = currentRendererWrapper->renderer->width.ToString() +
"x" +
currentRendererWrapper->renderer->height.ToString();
_si_res2_->Text = remoteResolution;
}
}
void
VideoPage::updateCallTimer(Object^ sender, Object^ e)
{
if(auto item = SmartPanelItemsViewModel::instance->_selectedItem)
_callTime_->Text = item->_callTime;
}
void
VideoPage::OnNavigatedTo(Windows::UI::Xaml::Navigation::NavigationEventArgs^ e)
{
updatePageContent();
closeChatPanel();
}
void RingClientUWP::Views::VideoPage::updatePageContent()
{
auto item = SmartPanelItemsViewModel::instance->_selectedItem;
auto contact = (item) ? item->_contact : nullptr;
if (!contact)
return;
_contactName_->Text = contact->_bestName;
//_contactBarAvatar_->ImageSource = RingClientUWP::ResourceMananger::instance->imageFromRelativePath(contact->_avatarImage);
if (contact->_avatarImage != " ") {
auto avatarImageUri = ref new Windows::Foundation::Uri(contact->_avatarImage);
_contactBarAvatar_->ImageSource = ref new BitmapImage(avatarImageUri);
_defaultContactBarAvatarGrid_->Visibility = VIS::Collapsed;
_contactBarAvatarGrid_->Visibility = VIS::Visible;
}
else {
_defaultContactBarAvatarGrid_->Visibility = VIS::Visible;
_contactBarAvatarGrid_->Visibility = VIS::Collapsed;
_defaultAvatar_->Fill = contact->_avatarColorBrush;
_defaultAvatarInitial_->Text = Utils::getUpperInitial(contact->_bestName2);
}
_messagesList_->ItemsSource = contact->_conversation->_messages;
scrollDown();
}
void
VideoPage::updatePreviewFrameDimensions()
{
double aspectRatio = VideoManager::instance->captureManager()->aspectRatio();
TransformGroup^ transforms = ref new TransformGroup();
double scaleValue = 1 + userPreviewHeightModifier / _PreviewImage_->Height;
scaleValue = std::max(std::min(1.75, scaleValue), 0.5);
userPreviewHeightModifier = _PreviewImage_->Height * (scaleValue - 1);
ScaleTransform^ scale = ref new ScaleTransform();
scale->ScaleX = scaleValue;
scale->ScaleY = scaleValue;
TranslateTransform^ translate = ref new TranslateTransform();
switch (quadrant)
{
case Quadrant::SE:
translate->Y = -userPreviewHeightModifier;
translate->X = translate->Y * aspectRatio;
break;
case Quadrant::SW:
translate->Y = -userPreviewHeightModifier;
translate->X = 0;
break;
case Quadrant::NW:
translate->Y = 0;
translate->X = 0;
break;
case Quadrant::NE:
translate->Y = 0;
translate->X = -userPreviewHeightModifier * aspectRatio;
break;
default:
break;
}
transforms->Children->Append(scale);
transforms->Children->Append(translate);
_PreviewImage_->RenderTransform = transforms;
_PreviewImageResizer_->RenderTransform = translate;
arrangeResizer();
}
void RingClientUWP::Views::VideoPage::scrollDown()
{
_scrollView_->UpdateLayout();
_scrollView_->ScrollToVerticalOffset(_scrollView_->ScrollableHeight);
}
void RingClientUWP::Views::VideoPage::screenVideo(bool state)
{
if (state) {
Video::VideoManager::instance->rendererManager()->raiseClearRenderTarget();
_callPaused_->Visibility = Windows::UI::Xaml::Visibility::Collapsed;
_IncomingVideoImage_->Visibility = Windows::UI::Xaml::Visibility::Visible;
_PreviewImage_->Visibility = Windows::UI::Xaml::Visibility::Visible;
_PreviewImageResizer_->Visibility = Windows::UI::Xaml::Visibility::Visible;
} else {
_callPaused_->Visibility = Windows::UI::Xaml::Visibility::Visible;
_IncomingVideoImage_->Visibility = Windows::UI::Xaml::Visibility::Collapsed;
_PreviewImage_->Visibility = Windows::UI::Xaml::Visibility::Collapsed;
_PreviewImageResizer_->Visibility = Windows::UI::Xaml::Visibility::Collapsed;
}
}
void
RingClientUWP::Views::VideoPage::_sendBtn__Click(Platform::Object^ sender, Windows::UI::Xaml::RoutedEventArgs^ e)
{
sendMessage();
}
void
RingClientUWP::Views::VideoPage::sendMessage()
{
if (auto item = SmartPanelItemsViewModel::instance->_selectedItem) {
auto contact = item->_contact;
auto txt = _messageTextBox_->Text;
/* empty the textbox */
_messageTextBox_->Text = "";
if (!contact || txt->IsEmpty())
return;
RingD::instance->sendSIPTextMessage(txt);
scrollDown();
}
}
void RingClientUWP::Views::VideoPage::Button_Click(Platform::Object^ sender, Windows::UI::Xaml::RoutedEventArgs^ e)
{
}
void RingClientUWP::Views::VideoPage::_btnCancel__Click(Platform::Object^ sender, Windows::UI::Xaml::RoutedEventArgs^ e)
{
}
void RingClientUWP::Views::VideoPage::_btnHangUp__Tapped(Platform::Object^ sender, Windows::UI::Xaml::Input::TappedRoutedEventArgs^ e)
{
if (auto item = SmartPanelItemsViewModel::instance->_selectedItem) {
_PreviewImage_->Visibility = Windows::UI::Xaml::Visibility::Collapsed;
_PreviewImageResizer_->Visibility = Windows::UI::Xaml::Visibility::Collapsed;
RingD::instance->hangUpCall2(item->_callId);
pressHangUpCall();
}
else
WNG_("item not found, cannot hang up");
}
void RingClientUWP::Views::VideoPage::_btnPause__Tapped(Platform::Object^ sender, Windows::UI::Xaml::Input::TappedRoutedEventArgs^ e)
{
auto item = SmartPanelItemsViewModel::instance->_selectedItem;
if (item->_callStatus == CallStatus::IN_PROGRESS)
RingD::instance->pauseCall(Utils::toString(item->_callId));
else if (item->_callStatus == CallStatus::PAUSED)
RingD::instance->unPauseCall(Utils::toString(item->_callId));
pauseCall();
}
void RingClientUWP::Views::VideoPage::_btnChat__Tapped(Platform::Object^ sender, Windows::UI::Xaml::Input::TappedRoutedEventArgs^ e)
{
if (!isChatPanelOpen) {
openChatPanel();
}
else {
closeChatPanel();
}
}
void RingClientUWP::Views::VideoPage::_btnAddFriend__Tapped(Platform::Object^ sender, Windows::UI::Xaml::Input::TappedRoutedEventArgs^ e)
{
addContactCall();
}
void RingClientUWP::Views::VideoPage::_btnSwitch__Tapped(Platform::Object^ sender, Windows::UI::Xaml::Input::TappedRoutedEventArgs^ e)
{
transferCall();
}
void RingClientUWP::Views::VideoPage::_btnMicrophone__Tapped(Platform::Object^ sender, Windows::UI::Xaml::Input::TappedRoutedEventArgs^ e)
{
switchMicrophoneStateCall();
auto item = SmartPanelItemsViewModel::instance->_selectedItem;
auto state = !item->_audioMuted;
item->_audioMuted = state;
// refacto : compare how video and audios are muted, then decide which solution is best.
RingD::instance->muteAudio(Utils::toString(item->_callId), state); // nb : muteAudio == setMuteAudio
}
void RingClientUWP::Views::VideoPage::_btnMemo__Tapped(Platform::Object^ sender, Windows::UI::Xaml::Input::TappedRoutedEventArgs^ e)
{
reccordVideoCall();
}
void RingClientUWP::Views::VideoPage::_btnHQ__Tapped(Platform::Object^ sender, Windows::UI::Xaml::Input::TappedRoutedEventArgs^ e)
{
qualityVideoLevelCall();
}
void RingClientUWP::Views::VideoPage::_btnVideo__Tapped(Platform::Object^ sender, Windows::UI::Xaml::Input::TappedRoutedEventArgs^ e)
{
switchVideoStateCall();
}
void RingClientUWP::Views::VideoPage::_videoControl__PointerMoved(Platform::Object^ sender, Windows::UI::Xaml::Input::PointerRoutedEventArgs^ e)
{
if (barFading)
fadeVideoControlsStoryboard->Begin();
}
void RingClientUWP::Views::VideoPage::btnAny_entered(Platform::Object^ sender, Windows::UI::Xaml::Input::PointerRoutedEventArgs^ e)
{
barFading = false;
fadeVideoControlsStoryboard->Stop();
}
void RingClientUWP::Views::VideoPage::btnAny_exited(Platform::Object^ sender, Windows::UI::Xaml::Input::PointerRoutedEventArgs^ e)
{
barFading = true;
}
void
VideoPage::SetBuffer(uint8_t* buf, int width, int height)
{
videoFrame = ref new VideoFrame(BitmapPixelFormat::Bgra8, width, height);
softwareBitmap = videoFrame->SoftwareBitmap;
BitmapBuffer^ bitmapBuffer = softwareBitmap->LockBuffer(BitmapBufferAccessMode::Write);
IMemoryBufferReference^ memoryBufferReference = bitmapBuffer->CreateReference();
Microsoft::WRL::ComPtr<IMemoryBufferByteAccess> byteAccess;
if (SUCCEEDED(reinterpret_cast<IUnknown*>(memoryBufferReference)->QueryInterface(IID_PPV_ARGS(&byteAccess))))
{
byte* data;
unsigned capacity;
byteAccess->GetBuffer(&data, &capacity);
std::memcpy(data, buf, static_cast<size_t>(capacity));
}
delete memoryBufferReference;
delete bitmapBuffer;
}
task<void>
VideoPage::WriteFrameAsSoftwareBitmapAsync(String^ id, uint8_t* buf, int width, int height)
{
SetBuffer(buf, width, height);
VideoManager::instance->rendererManager()->renderer(id)->isRendering = false;
auto sbSource = ref new Media::Imaging::SoftwareBitmapSource();
return create_task(sbSource->SetBitmapAsync(softwareBitmap))
.then([this, sbSource]()
{
try {
_IncomingVideoImage_->Source = sbSource;
}
catch (Exception^ e) {
EXC_(e);
}
});
}
void RingClientUWP::Views::VideoPage::OnincomingMessage(Platform::String ^callId, Platform::String ^payload)
{
openChatPanel();
scrollDown();
}
void RingClientUWP::Views::VideoPage::_btnVideo__Click(Platform::Object^ sender, Windows::UI::Xaml::RoutedEventArgs^ e)
{
auto item = SmartPanelItemsViewModel::instance->_selectedItem;
item->muteVideo(!item->_videoMuted);
}
void RingClientUWP::Views::VideoPage::OnincomingVideoMuted(Platform::String ^callId, bool state)
{
/*_callPaused_->Visibility = (state)
? Windows::UI::Xaml::Visibility::Visible
: Windows::UI::Xaml::Visibility::Collapsed;*/
_IncomingVideoImage_->Visibility = (state)
? Windows::UI::Xaml::Visibility::Collapsed
: Windows::UI::Xaml::Visibility::Visible;
}
void RingClientUWP::Views::VideoPage::OnstartPreviewing()
{
_PreviewImage_->Visibility = Windows::UI::Xaml::Visibility::Visible;
_PreviewImageResizer_->Visibility = Windows::UI::Xaml::Visibility::Visible;
}
void RingClientUWP::Views::VideoPage::OnstopPreviewing()
{
_PreviewImage_->Visibility = Windows::UI::Xaml::Visibility::Collapsed;
_PreviewImageResizer_->Visibility = Windows::UI::Xaml::Visibility::Collapsed;
}
void RingClientUWP::Views::VideoPage::_btnMicrophone__Click(Platform::Object^ sender, Windows::UI::Xaml::RoutedEventArgs^ e)
{
switchMicrophoneStateCall();
auto item = SmartPanelItemsViewModel::instance->_selectedItem;
auto state = !item->_audioMuted;
item->_audioMuted = state;
// refacto : compare how video and audios are muted, then decide which solution is best.
RingD::instance->muteAudio(Utils::toString(item->_callId), state); // nb : muteAudio == setMuteAudio
}
void RingClientUWP::Views::VideoPage::OnaudioMuted(const std::string &callId, bool state)
{
_txbkMicrophoneMuted_->Visibility = (state) ? Windows::UI::Xaml::Visibility::Visible
: Windows::UI::Xaml::Visibility::Collapsed;
}
void RingClientUWP::Views::VideoPage::OnvideoMuted(const std::string &callId, bool state)
{
_txbkVideoMuted_->Visibility = (state) ? Windows::UI::Xaml::Visibility::Visible
: Windows::UI::Xaml::Visibility::Collapsed;
}
void RingClientUWP::Views::VideoPage::IncomingVideoImage_DoubleTapped(Platform::Object^ sender, Windows::UI::Xaml::Input::DoubleTappedRoutedEventArgs^ e)
{
RingD::instance->toggleFullScreen();
anchorPreview();
}
void RingClientUWP::Views::VideoPage::InitManipulationTransforms()
{
PreviewImage_transforms = ref new TransformGroup();
PreviewImage_previousTransform = ref new MatrixTransform();
PreviewImage_previousTransform->Matrix = Matrix::Identity;
PreviewImage_deltaTransform = ref new CompositeTransform();
PreviewImage_transforms->Children->Append(PreviewImage_previousTransform);
PreviewImage_transforms->Children->Append(PreviewImage_deltaTransform);
_PreviewImageRect_->RenderTransform = PreviewImage_transforms;
}
void RingClientUWP::Views::VideoPage::PreviewImage_ManipulationDelta(Platform::Object^ sender, ManipulationDeltaRoutedEventArgs^ e)
{
if (!isMovingPreview)
isMovingPreview = true;
_PreviewImageRect_->RenderTransform = PreviewImage_transforms;
PreviewImage_previousTransform->Matrix = PreviewImage_transforms->Value;
PreviewImage_deltaTransform->TranslateX = e->Delta.Translation.X;
PreviewImage_deltaTransform->TranslateY = e->Delta.Translation.Y;
computeQuadrant();
}
void
RingClientUWP::Views::VideoPage::computeQuadrant()
{
// Compute center coordinate of _videoContent_
Point centerOfVideoFrame = Point( static_cast<float>(_videoContent_->ActualWidth - _colChatBx_->ActualWidth) / 2,
static_cast<float>(_videoContent_->ActualHeight - _rowChatBx_->ActualHeight) / 2 );
// Compute the center coordinate of _PreviewImage_ relative to _videoContent_
Point centerOfPreview = Point( static_cast<float>(_PreviewImage_->ActualWidth) / 2,
static_cast<float>(_PreviewImage_->ActualHeight) / 2 );
UIElement^ container = dynamic_cast<UIElement^>(VisualTreeHelper::GetParent(_videoContent_));
GeneralTransform^ transform = _PreviewImage_->TransformToVisual(container);
Point relativeCenterOfPreview = transform->TransformPoint(centerOfPreview);
// Compute the difference between the center of _videoContent_
// and the relative scaled center of _PreviewImageRect_
Point diff = Point( centerOfVideoFrame.X - relativeCenterOfPreview.X,
centerOfVideoFrame.Y - relativeCenterOfPreview.Y );
lastQuadrant = quadrant;
if (diff.X > 0)
quadrant = diff.Y > 0 ? Quadrant::NW : Quadrant::SW;
else
quadrant = diff.Y > 0 ? Quadrant::NE : Quadrant::SE;
if (lastQuadrant != quadrant) {
arrangeResizer();
}
}
void
RingClientUWP::Views::VideoPage::arrangeResizer()
{
double scaleValue = (userPreviewHeightModifier + _PreviewImage_->Height) / _PreviewImage_->Height;
float scaledWidth = static_cast<float>(scaleValue * _PreviewImage_->ActualWidth);
float scaledHeight = static_cast<float>(scaleValue * _PreviewImage_->ActualHeight);
float rSize = 20; // the size of the square UIElement used to resize the preview
float xOffset, yOffset;
PointCollection^ resizeTrianglePoints = ref new PointCollection();
switch (quadrant)
{
case Quadrant::SE:
xOffset = 0;
yOffset = 0;
resizeTrianglePoints->Append(Point(xOffset, yOffset));
resizeTrianglePoints->Append(Point(xOffset + rSize, yOffset));
resizeTrianglePoints->Append(Point(xOffset, yOffset + rSize));
break;
case Quadrant::SW:
xOffset = scaledWidth - rSize;
yOffset = 0;
resizeTrianglePoints->Append(Point(xOffset, yOffset));
resizeTrianglePoints->Append(Point(xOffset + rSize, yOffset));
resizeTrianglePoints->Append(Point(xOffset + rSize, yOffset + rSize));
break;
case Quadrant::NW:
xOffset = scaledWidth - rSize;
yOffset = scaledHeight - rSize;
resizeTrianglePoints->Append(Point(xOffset + rSize, yOffset));
resizeTrianglePoints->Append(Point(xOffset + rSize, yOffset + rSize));
resizeTrianglePoints->Append(Point(xOffset, yOffset + rSize));
break;
case Quadrant::NE:
xOffset = 0;
yOffset = scaledHeight - rSize;
resizeTrianglePoints->Append(Point(xOffset, yOffset + rSize));
resizeTrianglePoints->Append(Point(xOffset + rSize, yOffset + rSize));
resizeTrianglePoints->Append(Point(xOffset, yOffset));
break;
default:
break;
}
_PreviewImageResizer_->Points = resizeTrianglePoints;
}
void RingClientUWP::Views::VideoPage::PreviewImage_ManipulationCompleted(Platform::Object^ sender, ManipulationCompletedRoutedEventArgs^ e)
{
isMovingPreview = false;
anchorPreview();
updatePreviewFrameDimensions();
}
void
VideoPage::PreviewImageResizer_Pressed(Object^ sender, PointerRoutedEventArgs^ e)
{
isResizingPreview = true;
lastUserPreviewHeightModifier = userPreviewHeightModifier;
}
void RingClientUWP::Views::VideoPage::anchorPreview()
{
PreviewImage_previousTransform->Matrix = Matrix::Identity;
_PreviewImageRect_->RenderTransform = nullptr;
switch (quadrant)
{
case Quadrant::SE:
_PreviewImageRect_->HorizontalAlignment = Windows::UI::Xaml::HorizontalAlignment::Right;
_PreviewImageRect_->VerticalAlignment = Windows::UI::Xaml::VerticalAlignment::Bottom;
break;
case Quadrant::SW:
_PreviewImageRect_->HorizontalAlignment = Windows::UI::Xaml::HorizontalAlignment::Left;
_PreviewImageRect_->VerticalAlignment = Windows::UI::Xaml::VerticalAlignment::Bottom;
break;
case Quadrant::NW:
_PreviewImageRect_->HorizontalAlignment = Windows::UI::Xaml::HorizontalAlignment::Left;
_PreviewImageRect_->VerticalAlignment = Windows::UI::Xaml::VerticalAlignment::Top;
break;
case Quadrant::NE:
_PreviewImageRect_->HorizontalAlignment = Windows::UI::Xaml::HorizontalAlignment::Right;
_PreviewImageRect_->VerticalAlignment = Windows::UI::Xaml::VerticalAlignment::Top;
break;
default:
break;
};
}
void
VideoPage::PreviewImageResizer_ManipulationDelta(Platform::Object^ sender, ManipulationDeltaRoutedEventArgs^ e)
{
if (!isResizingPreview)
isResizingPreview = true;
if (quadrant == Quadrant::NW || quadrant == Quadrant::NE) {
userPreviewHeightModifier = lastUserPreviewHeightModifier + e->Cumulative.Translation.Y;
}
else {
userPreviewHeightModifier = lastUserPreviewHeightModifier - (e->Cumulative.Translation.Y);
}
updatePreviewFrameDimensions();
}
void
VideoPage::PreviewImageResizer_ManipulationCompleted(Platform::Object^ sender, ManipulationCompletedRoutedEventArgs^ e)
{
isResizingPreview = false;
if (!isHoveringOnResizer) {
CoreApplication::MainView->CoreWindow->PointerCursor = ref new Windows::UI::Core::CoreCursor(Windows::UI::Core::CoreCursorType::Arrow, 0);
}
}
void
VideoPage::PreviewImage_PointerReleased(Platform::Object^ sender, Windows::UI::Xaml::Input::PointerRoutedEventArgs^ e)
{
// For some reason, PreviewImage_ManipulationCompleted doesn't always fired when the mouse is released
anchorPreview();
updatePreviewFrameDimensions();
}
void
VideoPage::PreviewImageResizer_PointerEntered(Platform::Object^ sender, Windows::UI::Xaml::Input::PointerRoutedEventArgs^ e)
{
isHoveringOnResizer = true;
if (!isMovingPreview) {
switch (quadrant)
{
case Quadrant::SE:
case Quadrant::NW:
CoreApplication::MainView->CoreWindow->PointerCursor = ref new Windows::UI::Core::CoreCursor(Windows::UI::Core::CoreCursorType::SizeNorthwestSoutheast, 0);
break;
case Quadrant::SW:
case Quadrant::NE:
CoreApplication::MainView->CoreWindow->PointerCursor = ref new Windows::UI::Core::CoreCursor(Windows::UI::Core::CoreCursorType::SizeNortheastSouthwest, 0);
break;
default:
break;
}
}
}
void
VideoPage::PreviewImageResizer_PointerExited(Platform::Object^ sender, Windows::UI::Xaml::Input::PointerRoutedEventArgs^ e)
{
isHoveringOnResizer = false;
if (!isResizingPreview) {
CoreApplication::MainView->CoreWindow->PointerCursor = ref new Windows::UI::Core::CoreCursor(Windows::UI::Core::CoreCursorType::Arrow, 0);
}
}
void
VideoPage::PreviewImageResizer_PointerReleased(Platform::Object^ sender, Windows::UI::Xaml::Input::PointerRoutedEventArgs^ e)
{
if (!isHoveringOnResizer) {
CoreApplication::MainView->CoreWindow->PointerCursor = ref new Windows::UI::Core::CoreCursor(Windows::UI::Core::CoreCursorType::Arrow, 0);
}
}
void
VideoPage::_chatPanelResizeBarGrid__PointerEntered(Platform::Object^ sender, Windows::UI::Xaml::Input::PointerRoutedEventArgs^ e)
{
if (chtBoxOrientation == Orientation::Horizontal) {
CoreApplication::MainView->CoreWindow->PointerCursor = ref new Windows::UI::Core::CoreCursor(Windows::UI::Core::CoreCursorType::SizeWestEast, 0);
}
else {
CoreApplication::MainView->CoreWindow->PointerCursor = ref new Windows::UI::Core::CoreCursor(Windows::UI::Core::CoreCursorType::SizeNorthSouth, 0);
}
}
void
VideoPage::_chatPanelResizeBarGrid__PointerExited(Platform::Object^ sender, Windows::UI::Xaml::Input::PointerRoutedEventArgs^ e)
{
if (!isResizingChatPanel) {
CoreApplication::MainView->CoreWindow->PointerCursor = ref new Windows::UI::Core::CoreCursor(Windows::UI::Core::CoreCursorType::Arrow, 0);
}
}
void
VideoPage::_chatPanelResizeBarGrid__PointerPressed(Platform::Object^ sender, Windows::UI::Xaml::Input::PointerRoutedEventArgs^ e)
{
isResizingChatPanel = true;
lastchatPanelSize = chatPanelSize;
}
void
VideoPage::_chatPanelResizeBarGrid__PointerReleased(Platform::Object^ sender, Windows::UI::Xaml::Input::PointerRoutedEventArgs^ e)
{
isResizingChatPanel = false;
}
void
VideoPage::_chatPanelResizeBarGrid__ManipulationDelta(Platform::Object^ sender, ManipulationDeltaRoutedEventArgs^ e)
{
if (chtBoxOrientation == Orientation::Horizontal) {
chatPanelSize = lastchatPanelSize - e->Cumulative.Translation.X;
}
else {
chatPanelSize = lastchatPanelSize - e->Cumulative.Translation.Y;
}
resizeChatPanel();
}
void
VideoPage::_chatPanelResizeBarGrid__ManipulationCompleted(Platform::Object^ sender, ManipulationCompletedRoutedEventArgs^ e)
{
isResizingChatPanel = false;
CoreApplication::MainView->CoreWindow->PointerCursor = ref new Windows::UI::Core::CoreCursor(Windows::UI::Core::CoreCursorType::Arrow, 0);
}
void
VideoPage::openChatPanel()
{
resizeChatPanel();
isChatPanelOpen = true;
SmartPanelItemsViewModel::instance->_selectedItem->_contact->_unreadMessages = 0;
}
void
VideoPage::closeChatPanel()
{
_colChatBx_->Width = 0;
_rowChatBx_->Height = 0;
isChatPanelOpen = false;
}
void
VideoPage::resizeChatPanel()
{
// clamp chatPanelSize
double minChatPanelSize = 176;
double maxChatPanelSize = chtBoxOrientation == Orientation::Horizontal ?
_videoContent_->ActualWidth * .5 :
_videoContent_->ActualHeight * .5;
chatPanelSize = std::max(minChatPanelSize, std::min(chatPanelSize, maxChatPanelSize));
if (chtBoxOrientation == Orientation::Horizontal) {
_colChatBx_->Width = GridLength(chatPanelSize, GridUnitType::Pixel);
_rowChatBx_->Height = 0;
}
else {
_colChatBx_->Width = 0;
_rowChatBx_->Height = GridLength(chatPanelSize, GridUnitType::Pixel);
}
}
void
VideoPage::_btnToggleOrientation__Tapped(Platform::Object^ sender, Windows::UI::Xaml::Input::TappedRoutedEventArgs^ e)
{
bool wasChatPanelOpen = isChatPanelOpen;
closeChatPanel();
if (chtBoxOrientation == Orientation::Horizontal) {
chtBoxOrientation = Orientation::Vertical;
_btnToggleOrientation_->Content = L"\uE90D";
_chatPanelResizeBarGrid_->HorizontalAlignment = Windows::UI::Xaml::HorizontalAlignment::Stretch;
_chatPanelResizeBarGrid_->VerticalAlignment = Windows::UI::Xaml::VerticalAlignment::Top;
_chatPanelResizeBarGrid_->Width = std::numeric_limits<double>::quiet_NaN();
_chatPanelResizeBarGrid_->Height = 4;
Grid::SetColumn(_chatPanelGrid_, 0);
Grid::SetRow(_chatPanelGrid_, 2);
}
else {
chtBoxOrientation = Orientation::Horizontal;
_btnToggleOrientation_->Content = L"\uE90E";
_chatPanelResizeBarGrid_->HorizontalAlignment = Windows::UI::Xaml::HorizontalAlignment::Left;
_chatPanelResizeBarGrid_->VerticalAlignment = Windows::UI::Xaml::VerticalAlignment::Stretch;
_chatPanelResizeBarGrid_->Width = 4;
_chatPanelResizeBarGrid_->Height = std::numeric_limits<double>::quiet_NaN();
Grid::SetColumn(_chatPanelGrid_, 2);
Grid::SetRow(_chatPanelGrid_, 0);
}
if (wasChatPanelOpen)
openChatPanel();
}
void
VideoPage::_videoContent__SizeChanged(Platform::Object^ sender, Windows::UI::Xaml::SizeChangedEventArgs^ e)
{
if (isChatPanelOpen)
resizeChatPanel();
}
void
VideoPage::_messageTextBox__TextChanged(Platform::Object^ sender, TextChangedEventArgs^ e)
{
bool carriageReturnPressed = false;
if (_messageTextBox_->Text->Length() == (lastMessageText->Length() + 1) &&
_messageTextBox_->Text != "\r") {
unsigned cursorPos = 0;
auto strMessage = Utils::toString(_messageTextBox_->Text);
auto strLastMessage = Utils::toString(lastMessageText);
for (std::string::size_type i = 0; i < strLastMessage.size(); ++i) {
if (strMessage[i] != strLastMessage[i]) {
auto changed = strMessage.substr(i, 1);
if (changed == "\r") {
carriageReturnPressed = true;
MSG_("CR inside");
}
break;
}
}
if (strMessage.substr(strMessage.length() - 1) == "\r") {
if (lastMessageText->Length() != 0) {
carriageReturnPressed = true;
MSG_("CR at end");
}
}
}
if (carriageReturnPressed && !(RingD::instance->isCtrlPressed || RingD::instance->isShiftPressed)) {
_messageTextBox_->Text = lastMessageText;
sendMessage();
lastMessageText = "";
}
DependencyObject^ child = VisualTreeHelper::GetChild(_messageTextBox_, 0);
auto sendBtnElement = Utils::xaml::FindVisualChildByName(child, "_sendBtn_");
auto sendBtn = dynamic_cast<Button^>(sendBtnElement);
if (_messageTextBox_->Text != "") {
sendBtn->IsEnabled = true;
}
else {
sendBtn->IsEnabled = false;
}
lastMessageText = _messageTextBox_->Text;
}
void
VideoPage::_corewindow__KeyDown(CoreWindow^ sender, KeyEventArgs^ e)
{
auto ctrlState = CoreWindow::GetForCurrentThread()->GetKeyState(VirtualKey::Control);
auto isCtrlDown = ctrlState != CoreVirtualKeyStates::None;
if (isCtrlDown && e->VirtualKey == Windows::System::VirtualKey::I) {
_smartInfoBorder_->Visibility = _smartInfoBorder_->Visibility == VIS::Collapsed ? VIS::Visible : VIS::Collapsed;
}
}