diff -r 000000000000 -r 4f2f89ce4247 WebKit/win/FullscreenVideoController.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/WebKit/win/FullscreenVideoController.cpp Fri Sep 17 09:02:29 2010 +0300 @@ -0,0 +1,702 @@ +/* + * Copyright (C) 2010 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" + +#if ENABLE(VIDEO) + +#include "FullscreenVideoController.h" + +#include "WebKitDLL.h" +#include "WebView.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace std; +using namespace WebCore; + +static const float timerInterval = 0.033; + +// HUD Size +static const int windowHeight = 59; +static const int windowWidth = 438; + +// Margins and button sizes +static const int margin = 9; +static const int marginTop = 9; +static const int buttonSize = 25; +static const int buttonMiniSize = 16; +static const int volumeSliderWidth = 50; +static const int timeSliderWidth = 310; +static const int sliderHeight = 8; +static const int volumeSliderButtonSize = 10; +static const int timeSliderButtonSize = 8; +static const int textSize = 11; +static const float initialHUDPositionY = 0.9; // Initial Y position of HUD in percentage from top of screen + +// Background values +static const int borderRadius = 12; +static const int borderThickness = 2; + +// Colors +static const unsigned int backgroundColor = 0xA0202020; +static const unsigned int borderColor = 0xFFA0A0A0; +static const unsigned int sliderGutterColor = 0xFF141414; +static const unsigned int sliderButtonColor = 0xFF808080; +static const unsigned int textColor = 0xFFFFFFFF; + +HUDButton::HUDButton(HUDButtonType type, const IntPoint& position) + : HUDWidget(IntRect(position, IntSize())) + , m_type(type) + , m_showAltButton(false) +{ + const char* buttonResource = 0; + const char* buttonResourceAlt = 0; + switch (m_type) { + case PlayPauseButton: + buttonResource = "fsVideoPlay"; + buttonResourceAlt = "fsVideoPause"; + break; + case TimeSliderButton: + break; + case VolumeUpButton: + buttonResource = "fsVideoAudioVolumeHigh"; + break; + case VolumeSliderButton: + break; + case VolumeDownButton: + buttonResource = "fsVideoAudioVolumeLow"; + break; + case ExitFullscreenButton: + buttonResource = "fsVideoExitFullscreen"; + break; + } + + if (buttonResource) { + m_buttonImage = Image::loadPlatformResource(buttonResource); + m_rect.setWidth(m_buttonImage->width()); + m_rect.setHeight(m_buttonImage->height()); + } + if (buttonResourceAlt) + m_buttonImageAlt = Image::loadPlatformResource(buttonResourceAlt); +} + +void HUDButton::draw(GraphicsContext& context) +{ + Image* image = (m_showAltButton && m_buttonImageAlt) ? m_buttonImageAlt.get() : m_buttonImage.get(); + context.drawImage(image, DeviceColorSpace, m_rect.location()); +} + +HUDSlider::HUDSlider(HUDSliderButtonShape shape, int buttonSize, const IntRect& rect) + : HUDWidget(rect) + , m_buttonShape(shape) + , m_buttonSize(buttonSize) + , m_buttonPosition(0) + , m_dragStartOffset(0) +{ +} + +void HUDSlider::draw(GraphicsContext& context) +{ + // Draw gutter + IntSize radius(m_rect.height() / 2, m_rect.height() / 2); + context.fillRoundedRect(m_rect, radius, radius, radius, radius, Color(sliderGutterColor), DeviceColorSpace); + + // Draw button + context.setStrokeColor(Color(sliderButtonColor), DeviceColorSpace); + context.setFillColor(Color(sliderButtonColor), DeviceColorSpace); + + if (m_buttonShape == RoundButton) { + context.drawEllipse(IntRect(m_rect.location().x() + m_buttonPosition, m_rect.location().y() - (m_buttonSize - m_rect.height()) / 2, m_buttonSize, m_buttonSize)); + return; + } + + // Draw a diamond + FloatPoint points[4]; + float half = static_cast(m_buttonSize) / 2; + points[0].setX(m_rect.location().x() + m_buttonPosition + half); + points[0].setY(m_rect.location().y()); + points[1].setX(m_rect.location().x() + m_buttonPosition + m_buttonSize); + points[1].setY(m_rect.location().y() + half); + points[2].setX(m_rect.location().x() + m_buttonPosition + half); + points[2].setY(m_rect.location().y() + m_buttonSize); + points[3].setX(m_rect.location().x() + m_buttonPosition); + points[3].setY(m_rect.location().y() + half); + context.drawConvexPolygon(4, points, true); +} + +void HUDSlider::drag(const IntPoint& point, bool start) +{ + if (start) { + // When we start, we need to snap the slider position to the x position if we clicked the gutter. + // But if we click the button, we need to drag relative to where we clicked down. We only need + // to check X because we would not even get here unless Y were already inside. + int relativeX = point.x() - m_rect.location().x(); + if (relativeX >= m_buttonPosition && relativeX <= m_buttonPosition + m_buttonSize) + m_dragStartOffset = point.x() - m_buttonPosition; + else + m_dragStartOffset = m_rect.location().x() + m_buttonSize / 2; + } + + m_buttonPosition = max(0, min(m_rect.width() - m_buttonSize, point.x() - m_dragStartOffset)); +} + +#if USE(ACCELERATED_COMPOSITING) +class FullscreenVideoController::LayoutClient : public WKCACFLayerLayoutClient { +public: + LayoutClient(FullscreenVideoController* parent); + void layoutSublayersOfLayer(WKCACFLayer* layer); + + FullscreenVideoController* m_parent; +}; + +FullscreenVideoController::LayoutClient::LayoutClient(FullscreenVideoController* parent) + : m_parent(parent) +{ +} + +void FullscreenVideoController::LayoutClient::layoutSublayersOfLayer(WKCACFLayer* layer) +{ + ASSERT_ARG(layer, layer == m_parent->m_rootChild); + + HTMLMediaElement* mediaElement = m_parent->m_mediaElement.get(); + if (!mediaElement) + return; + + WKCACFLayer* videoLayer = mediaElement->platformLayer(); + if (!videoLayer || videoLayer->superlayer() != layer) + return; + + FloatRect layerBounds = layer->bounds(); + + FloatSize videoSize = mediaElement->player()->naturalSize(); + float scaleFactor; + if (videoSize.aspectRatio() > layerBounds.size().aspectRatio()) + scaleFactor = layerBounds.width() / videoSize.width(); + else + scaleFactor = layerBounds.height() / videoSize.height(); + videoSize.scale(scaleFactor); + + // Calculate the centered position based on the videoBounds and layerBounds: + FloatPoint videoPosition; + FloatPoint videoOrigin; + videoOrigin.setX((layerBounds.width() - videoSize.width()) * 0.5); + videoOrigin.setY((layerBounds.height() - videoSize.height()) * 0.5); + videoLayer->setFrame(FloatRect(videoOrigin, videoSize)); +} +#endif + +FullscreenVideoController::FullscreenVideoController() + : m_hudWindow(0) + , m_playPauseButton(HUDButton::PlayPauseButton, IntPoint((windowWidth - buttonSize) / 2, marginTop)) + , m_timeSliderButton(HUDButton::TimeSliderButton, IntPoint(0, 0)) + , m_volumeUpButton(HUDButton::VolumeUpButton, IntPoint(margin + buttonMiniSize + volumeSliderWidth + buttonMiniSize / 2, marginTop + (buttonSize - buttonMiniSize) / 2)) + , m_volumeSliderButton(HUDButton::VolumeSliderButton, IntPoint(0, 0)) + , m_volumeDownButton(HUDButton::VolumeDownButton, IntPoint(margin, marginTop + (buttonSize - buttonMiniSize) / 2)) + , m_exitFullscreenButton(HUDButton::ExitFullscreenButton, IntPoint(windowWidth - 2 * margin - buttonMiniSize, marginTop + (buttonSize - buttonMiniSize) / 2)) + , m_volumeSlider(HUDSlider::RoundButton, volumeSliderButtonSize, IntRect(IntPoint(margin + buttonMiniSize, marginTop + (buttonSize - buttonMiniSize) / 2 + buttonMiniSize / 2 - sliderHeight / 2), IntSize(volumeSliderWidth, sliderHeight))) + , m_timeSlider(HUDSlider::DiamondButton, timeSliderButtonSize, IntRect(IntPoint(windowWidth / 2 - timeSliderWidth / 2, windowHeight - margin - sliderHeight), IntSize(timeSliderWidth, sliderHeight))) + , m_hitWidget(0) + , m_movingWindow(false) + , m_timer(this, &FullscreenVideoController::timerFired) +#if USE(ACCELERATED_COMPOSITING) + , m_rootChild(WKCACFLayer::create(WKCACFLayer::Layer)) + , m_layoutClient(new LayoutClient(this)) +#endif + , m_fullscreenWindow(new MediaPlayerPrivateFullscreenWindow(this)) +{ +#if USE(ACCELERATED_COMPOSITING) + m_rootChild->setLayoutClient(m_layoutClient.get()); +#endif +} + +FullscreenVideoController::~FullscreenVideoController() +{ +#if USE(ACCELERATED_COMPOSITING) + m_rootChild->setLayoutClient(0); +#endif +} + +void FullscreenVideoController::setMediaElement(HTMLMediaElement* mediaElement) +{ + if (mediaElement == m_mediaElement) + return; + + m_mediaElement = mediaElement; + if (!m_mediaElement) { + // Can't do full-screen, just get out + exitFullscreen(); + } +} + +void FullscreenVideoController::enterFullscreen() +{ + if (!m_mediaElement) + return; + + WebView* webView = kit(m_mediaElement->document()->page()); + HWND parentHwnd = webView ? webView->viewWindow() : 0; + + m_fullscreenWindow->createWindow(parentHwnd); +#if USE(ACCELERATED_COMPOSITING) + m_fullscreenWindow->setRootChildLayer(m_rootChild); + + WKCACFLayer* videoLayer = m_mediaElement->player()->platformLayer(); + m_rootChild->addSublayer(videoLayer); + m_rootChild->setNeedsLayout(); +#endif + + RECT windowRect; + GetClientRect(m_fullscreenWindow->hwnd(), &windowRect); + m_fullscreenSize.setWidth(windowRect.right - windowRect.left); + m_fullscreenSize.setHeight(windowRect.bottom - windowRect.top); + + createHUDWindow(); +} + +void FullscreenVideoController::exitFullscreen() +{ + SetWindowLongPtr(m_hudWindow, 0, 0); + + if (m_fullscreenWindow) + m_fullscreenWindow = 0; + + ASSERT(!IsWindow(m_hudWindow)); + m_hudWindow = 0; + + // We previously ripped the mediaElement's platform layer out + // of its orginial layer tree to display it in our fullscreen + // window. Now, we need to get the layer back in its original + // tree. + // + // As a side effect of setting the player to invisible/visible, + // the player's layer will be recreated, and will be picked up + // the next time the layer tree is synched. + m_mediaElement->player()->setVisible(0); + m_mediaElement->player()->setVisible(1); +} + +bool FullscreenVideoController::canPlay() const +{ + return m_mediaElement && m_mediaElement->canPlay(); +} + +void FullscreenVideoController::play() +{ + if (m_mediaElement) + m_mediaElement->play(m_mediaElement->processingUserGesture()); +} + +void FullscreenVideoController::pause() +{ + if (m_mediaElement) + m_mediaElement->pause(m_mediaElement->processingUserGesture()); +} + +float FullscreenVideoController::volume() const +{ + return m_mediaElement ? m_mediaElement->volume() : 0; +} + +void FullscreenVideoController::setVolume(float volume) +{ + if (m_mediaElement) { + ExceptionCode ec; + m_mediaElement->setVolume(volume, ec); + } +} + +float FullscreenVideoController::currentTime() const +{ + return m_mediaElement ? m_mediaElement->currentTime() : 0; +} + +void FullscreenVideoController::setCurrentTime(float value) +{ + if (m_mediaElement) { + ExceptionCode ec; + m_mediaElement->setCurrentTime(value, ec); + } +} + +float FullscreenVideoController::duration() const +{ + return m_mediaElement ? m_mediaElement->duration() : 0; +} + +void FullscreenVideoController::beginScrubbing() +{ + if (m_mediaElement) + m_mediaElement->beginScrubbing(); +} + +void FullscreenVideoController::endScrubbing() +{ + if (m_mediaElement) + m_mediaElement->endScrubbing(); +} + +LRESULT FullscreenVideoController::fullscreenClientWndProc(HWND wnd, UINT message, WPARAM wParam, LPARAM lParam) +{ + switch (message) { + case WM_CHAR: + onChar(wParam); + break; + case WM_KEYDOWN: + onKeyDown(wParam); + break; + case WM_LBUTTONDOWN: + onMouseDown(IntPoint(GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam))); + break; + case WM_MOUSEMOVE: + onMouseMove(IntPoint(GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam))); + break; + case WM_LBUTTONUP: + onMouseUp(IntPoint(GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam))); + break; + } + + return DefWindowProc(wnd, message, wParam, lParam); +} + +static const LPCWSTR fullscreenVideeoHUDWindowClassName = L"fullscreenVideeoHUDWindowClass"; + +void FullscreenVideoController::registerHUDWindowClass() +{ + static bool haveRegisteredHUDWindowClass; + if (haveRegisteredHUDWindowClass) + return; + + haveRegisteredHUDWindowClass = true; + + WNDCLASSEX wcex; + + wcex.cbSize = sizeof(WNDCLASSEX); + + wcex.style = CS_HREDRAW | CS_VREDRAW; + wcex.lpfnWndProc = hudWndProc; + wcex.cbClsExtra = 0; + wcex.cbWndExtra = 4; + wcex.hInstance = gInstance; + wcex.hIcon = 0; + wcex.hCursor = LoadCursor(0, IDC_ARROW); + wcex.hbrBackground = 0; + wcex.lpszMenuName = 0; + wcex.lpszClassName = fullscreenVideeoHUDWindowClassName; + wcex.hIconSm = 0; + + RegisterClassEx(&wcex); +} + +void FullscreenVideoController::createHUDWindow() +{ + m_hudPosition.setX((m_fullscreenSize.width() - windowWidth) / 2); + m_hudPosition.setY(m_fullscreenSize.height() * initialHUDPositionY - windowHeight / 2); + + // Local variable that will hold the returned pixels. No need to cleanup this value. It + // will get cleaned up when m_bitmap is destroyed in the dtor + void* pixels; + BitmapInfo bitmapInfo = BitmapInfo::createBottomUp(IntSize(windowWidth, windowHeight)); + m_bitmap.set(::CreateDIBSection(0, &bitmapInfo, DIB_RGB_COLORS, &pixels, 0, 0)); + + // Dirty the window so the HUD draws + RECT clearRect = { m_hudPosition.x(), m_hudPosition.y(), m_hudPosition.x() + windowWidth, m_hudPosition.y() + windowHeight }; + InvalidateRect(m_fullscreenWindow->hwnd(), &clearRect, true); + + m_playPauseButton.setShowAltButton(!canPlay()); + m_volumeSlider.setValue(volume()); + m_timeSlider.setValue(currentTime() / duration()); + + if (!canPlay()) + m_timer.startRepeating(timerInterval); + + registerHUDWindowClass(); + + m_hudWindow = CreateWindowEx(WS_EX_LAYERED | WS_EX_TRANSPARENT | WS_EX_TOOLWINDOW, + fullscreenVideeoHUDWindowClassName, 0, WS_POPUP | WS_VISIBLE, + m_hudPosition.x(), m_hudPosition.y(), 0, 0, m_fullscreenWindow->hwnd(), 0, gInstance, 0); + ASSERT(::IsWindow(m_hudWindow)); + SetWindowLongPtr(m_hudWindow, 0, reinterpret_cast(this)); + + draw(); +} + +static String timeToString(float time) +{ + if (!isfinite(time)) + time = 0; + int seconds = fabsf(time); + int hours = seconds / (60 * 60); + int minutes = (seconds / 60) % 60; + seconds %= 60; + + if (hours) { + if (hours > 9) + return String::format("%s%02d:%02d:%02d", (time < 0 ? "-" : ""), hours, minutes, seconds); + return String::format("%s%01d:%02d:%02d", (time < 0 ? "-" : ""), hours, minutes, seconds); + } + + return String::format("%s%02d:%02d", (time < 0 ? "-" : ""), minutes, seconds); +} + +void FullscreenVideoController::draw() +{ + HDC windowDC = GetDC(m_hudWindow); + HDC bitmapDC = CreateCompatibleDC(windowDC); + ::ReleaseDC(m_hudWindow, windowDC); + SelectObject(bitmapDC, m_bitmap.get()); + + GraphicsContext context(bitmapDC, true); + + context.save(); + + // Draw the background + IntSize outerRadius(borderRadius, borderRadius); + IntRect outerRect(0, 0, windowWidth, windowHeight); + IntSize innerRadius(borderRadius - borderThickness, borderRadius - borderThickness); + IntRect innerRect(borderThickness, borderThickness, windowWidth - borderThickness * 2, windowHeight - borderThickness * 2); + + context.fillRoundedRect(outerRect, outerRadius, outerRadius, outerRadius, outerRadius, Color(borderColor), DeviceColorSpace); + context.setCompositeOperation(CompositeCopy); + context.fillRoundedRect(innerRect, innerRadius, innerRadius, innerRadius, innerRadius, Color(backgroundColor), DeviceColorSpace); + + // Draw the widgets + m_playPauseButton.draw(context); + m_volumeUpButton.draw(context); + m_volumeSliderButton.draw(context); + m_volumeDownButton.draw(context); + m_timeSliderButton.draw(context); + m_exitFullscreenButton.draw(context); + m_volumeSlider.draw(context); + m_timeSlider.draw(context); + + // Draw the text strings + FontDescription desc; + + NONCLIENTMETRICS metrics; + metrics.cbSize = sizeof(metrics); + SystemParametersInfo(SPI_GETNONCLIENTMETRICS, metrics.cbSize, &metrics, 0); + FontFamily family; + family.setFamily(metrics.lfSmCaptionFont.lfFaceName); + desc.setFamily(family); + + desc.setComputedSize(textSize); + Font font = Font(desc, 0, 0); + font.update(0); + + String s; + + // The y positioning of these two text strings is tricky because they are so small. They + // are currently positioned relative to the center of the slider and then down the font + // height / 4 (which is actually half of font height /2), which positions the center of + // the text at the center of the slider. + // Left string + s = timeToString(currentTime()); + TextRun leftText(s); + context.setFillColor(Color(textColor), DeviceColorSpace); + context.drawText(font, leftText, IntPoint(windowWidth / 2 - timeSliderWidth / 2 - margin - font.width(leftText), windowHeight - margin - sliderHeight / 2 + font.height() / 4)); + + // Right string + s = timeToString(currentTime() - duration()); + TextRun rightText(s); + context.setFillColor(Color(textColor), DeviceColorSpace); + context.drawText(font, rightText, IntPoint(windowWidth / 2 + timeSliderWidth / 2 + margin, windowHeight - margin - sliderHeight / 2 + font.height() / 4)); + + // Copy to the window + BLENDFUNCTION blendFunction = {AC_SRC_OVER, 0, 255, AC_SRC_ALPHA}; + SIZE size = { windowWidth, windowHeight }; + POINT sourcePoint = {0, 0}; + POINT destPoint = { m_hudPosition.x(), m_hudPosition.y() }; + BOOL result = UpdateLayeredWindow(m_hudWindow, 0, &destPoint, &size, bitmapDC, &sourcePoint, 0, &blendFunction, ULW_ALPHA); + + context.restore(); + + ::DeleteDC(bitmapDC); +} + +LRESULT FullscreenVideoController::hudWndProc(HWND wnd, UINT message, WPARAM wParam, LPARAM lParam) +{ + LONG_PTR longPtr = GetWindowLongPtr(wnd, 0); + FullscreenVideoController* controller = reinterpret_cast(longPtr); + if (!controller) + return DefWindowProc(wnd, message, wParam, lParam); + + switch (message) { + case WM_CHAR: + controller->onChar(wParam); + break; + case WM_KEYDOWN: + controller->onKeyDown(wParam); + break; + case WM_LBUTTONDOWN: + controller->onMouseDown(IntPoint(GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam))); + break; + case WM_MOUSEMOVE: + controller->onMouseMove(IntPoint(GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam))); + break; + case WM_LBUTTONUP: + controller->onMouseUp(IntPoint(GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam))); + break; + } + + return DefWindowProc(wnd, message, wParam, lParam); +} + +void FullscreenVideoController::onChar(int c) +{ + if (c == VK_ESCAPE) { + if (m_mediaElement) + m_mediaElement->exitFullscreen(); + } else if (c == VK_SPACE) + togglePlay(); +} + +void FullscreenVideoController::onKeyDown(int virtualKey) +{ + if (virtualKey == VK_ESCAPE) { + if (m_mediaElement) + m_mediaElement->exitFullscreen(); + } +} + +void FullscreenVideoController::timerFired(Timer*) +{ + // Update the time slider + m_timeSlider.setValue(currentTime() / duration()); + draw(); +} + +void FullscreenVideoController::onMouseDown(const IntPoint& point) +{ + IntPoint convertedPoint(fullscreenToHUDCoordinates(point)); + + // Don't bother hit testing if we're outside the bounds of the window + if (convertedPoint.x() < 0 || convertedPoint.x() >= windowWidth || convertedPoint.y() < 0 || convertedPoint.y() >= windowHeight) + return; + + m_hitWidget = 0; + m_movingWindow = false; + + if (m_playPauseButton.hitTest(convertedPoint)) + m_hitWidget = &m_playPauseButton; + else if (m_exitFullscreenButton.hitTest(convertedPoint)) + m_hitWidget = &m_exitFullscreenButton; + else if (m_volumeUpButton.hitTest(convertedPoint)) + m_hitWidget = &m_volumeUpButton; + else if (m_volumeDownButton.hitTest(convertedPoint)) + m_hitWidget = &m_volumeDownButton; + else if (m_volumeSlider.hitTest(convertedPoint)) { + m_hitWidget = &m_volumeSlider; + m_volumeSlider.drag(convertedPoint, true); + setVolume(m_volumeSlider.value()); + } else if (m_timeSlider.hitTest(convertedPoint)) { + m_hitWidget = &m_timeSlider; + m_timeSlider.drag(convertedPoint, true); + beginScrubbing(); + setCurrentTime(m_timeSlider.value() * duration()); + } + + // If we did not pick any of our widgets we are starting a window move + if (!m_hitWidget) { + m_moveOffset = convertedPoint; + m_movingWindow = true; + } + + draw(); +} + +void FullscreenVideoController::onMouseMove(const IntPoint& point) +{ + IntPoint convertedPoint(fullscreenToHUDCoordinates(point)); + + if (m_hitWidget) { + m_hitWidget->drag(convertedPoint, false); + if (m_hitWidget == &m_volumeSlider) + setVolume(m_volumeSlider.value()); + else if (m_hitWidget == &m_timeSlider) + setCurrentTime(m_timeSlider.value() * duration()); + draw(); + } else if (m_movingWindow) + m_hudPosition.move(convertedPoint.x() - m_moveOffset.x(), convertedPoint.y() - m_moveOffset.y()); +} + +void FullscreenVideoController::onMouseUp(const IntPoint& point) +{ + IntPoint convertedPoint(fullscreenToHUDCoordinates(point)); + m_movingWindow = false; + + if (m_hitWidget) { + if (m_hitWidget == &m_playPauseButton && m_playPauseButton.hitTest(convertedPoint)) + togglePlay(); + else if (m_hitWidget == &m_volumeUpButton && m_volumeUpButton.hitTest(convertedPoint)) { + setVolume(1); + m_volumeSlider.setValue(1); + } else if (m_hitWidget == &m_volumeDownButton && m_volumeDownButton.hitTest(convertedPoint)) { + setVolume(0); + m_volumeSlider.setValue(0); + } else if (m_hitWidget == &m_timeSlider) + endScrubbing(); + else if (m_hitWidget == &m_exitFullscreenButton && m_exitFullscreenButton.hitTest(convertedPoint)) { + m_hitWidget = 0; + if (m_mediaElement) + m_mediaElement->exitFullscreen(); + return; + } + } + + m_hitWidget = 0; + draw(); +} + +void FullscreenVideoController::togglePlay() +{ + if (canPlay()) + play(); + else + pause(); + + m_playPauseButton.setShowAltButton(!canPlay()); + + // Run a timer while the video is playing so we can keep the time + // slider and time values up to date. + if (!canPlay()) + m_timer.startRepeating(timerInterval); + else + m_timer.stop(); + + draw(); +} + +#endif