diff options
author | Jaakko Keränen <jaakko.keranen@iki.fi> | 2020-10-08 08:22:50 +0300 |
---|---|---|
committer | Jaakko Keränen <jaakko.keranen@iki.fi> | 2020-10-08 08:22:50 +0300 |
commit | cf713705fbc53e262b440f128b3a6dc65bea3b78 (patch) | |
tree | 12504042b00009f45199e5e9e4310fa4546b8b5a /src/ui/documentwidget.c | |
parent | c4aa47eddd21c51173d4400e44e06f32d2080d2d (diff) |
Player: Play/pause button in the UI
Diffstat (limited to 'src/ui/documentwidget.c')
-rw-r--r-- | src/ui/documentwidget.c | 243 |
1 files changed, 139 insertions, 104 deletions
diff --git a/src/ui/documentwidget.c b/src/ui/documentwidget.c index 8b769cf8..81d7e879 100644 --- a/src/ui/documentwidget.c +++ b/src/ui/documentwidget.c | |||
@@ -1110,6 +1110,7 @@ static const char *sourceLoc_DocumentWidget_(const iDocumentWidget *d, iInt2 pos | |||
1110 | } | 1110 | } |
1111 | 1111 | ||
1112 | iDeclareType(MiddleRunParams) | 1112 | iDeclareType(MiddleRunParams) |
1113 | |||
1113 | struct Impl_MiddleRunParams { | 1114 | struct Impl_MiddleRunParams { |
1114 | int midY; | 1115 | int midY; |
1115 | const iGmRun *closest; | 1116 | const iGmRun *closest; |
@@ -1591,6 +1592,140 @@ static size_t visibleLinkOrdinal_DocumentWidget_(const iDocumentWidget *d, iGmLi | |||
1591 | return iInvalidPos; | 1592 | return iInvalidPos; |
1592 | } | 1593 | } |
1593 | 1594 | ||
1595 | iDeclareType(PlayerUI) | ||
1596 | |||
1597 | struct Impl_PlayerUI { | ||
1598 | const iPlayer *player; | ||
1599 | iRect bounds; | ||
1600 | iRect playPauseRect; | ||
1601 | iRect rewindRect; | ||
1602 | iRect scrubberRect; | ||
1603 | iRect menuRect; | ||
1604 | }; | ||
1605 | |||
1606 | static void init_PlayerUI(iPlayerUI *d, const iPlayer *player, iRect bounds) { | ||
1607 | d->player = player; | ||
1608 | d->bounds = bounds; | ||
1609 | const int height = height_Rect(bounds); | ||
1610 | d->playPauseRect = (iRect){ addX_I2(topLeft_Rect(bounds), gap_UI / 2), init1_I2(height) }; | ||
1611 | d->rewindRect = (iRect){ topRight_Rect(d->playPauseRect), init1_I2(height) }; | ||
1612 | d->menuRect = (iRect){ addX_I2(topRight_Rect(bounds), -height - gap_UI / 2), init1_I2(height) }; | ||
1613 | d->scrubberRect = initCorners_Rect(topRight_Rect(d->rewindRect), bottomLeft_Rect(d->menuRect)); | ||
1614 | } | ||
1615 | |||
1616 | static void drawPlayerButton_(iPaint *p, iRect rect, const char *label) { | ||
1617 | const iInt2 mouse = mouseCoord_Window(get_Window()); | ||
1618 | const iBool isHover = contains_Rect(rect, mouse); | ||
1619 | const iBool isPressed = isHover && (SDL_GetMouseState(NULL, NULL) & SDL_BUTTON_LEFT) != 0; | ||
1620 | const int frame = (isPressed ? uiTextCaution_ColorId : isHover ? uiHeading_ColorId : uiAnnotation_ColorId); | ||
1621 | iRect frameRect = shrunk_Rect(rect, init_I2(gap_UI / 2, gap_UI)); | ||
1622 | drawRect_Paint(p, frameRect, frame); | ||
1623 | if (isPressed) { | ||
1624 | fillRect_Paint( | ||
1625 | p, | ||
1626 | adjusted_Rect(shrunk_Rect(frameRect, divi_I2(gap2_UI, 2)), zero_I2(), one_I2()), | ||
1627 | frame); | ||
1628 | } | ||
1629 | const int fg = isPressed ? uiBackground_ColorId : frame; | ||
1630 | drawCentered_Text(uiContent_FontId, frameRect, iTrue, fg, "%s", label); | ||
1631 | } | ||
1632 | |||
1633 | static int drawSevenSegmentTime_(iInt2 pos, int color, int align, int seconds) { /* returns width */ | ||
1634 | const uint32_t sevenSegmentDigit = 0x1fbf0; | ||
1635 | const int hours = seconds / 3600; | ||
1636 | const int mins = (seconds / 60) % 60; | ||
1637 | const int secs = seconds % 60; | ||
1638 | const int font = uiLabel_FontId; | ||
1639 | iString num; | ||
1640 | init_String(&num); | ||
1641 | if (hours) { | ||
1642 | appendChar_String(&num, sevenSegmentDigit + (hours % 10)); | ||
1643 | appendChar_String(&num, ':'); | ||
1644 | } | ||
1645 | appendChar_String(&num, sevenSegmentDigit + (mins / 10) % 10); | ||
1646 | appendChar_String(&num, sevenSegmentDigit + (mins % 10)); | ||
1647 | appendChar_String(&num, ':'); | ||
1648 | appendChar_String(&num, sevenSegmentDigit + (secs / 10) % 10); | ||
1649 | appendChar_String(&num, sevenSegmentDigit + (secs % 10)); | ||
1650 | iInt2 size = advanceRange_Text(font, range_String(&num)); | ||
1651 | if (align == right_Alignment) { | ||
1652 | pos.x -= size.x; | ||
1653 | } | ||
1654 | drawRange_Text(font, pos, color, range_String(&num)); | ||
1655 | deinit_String(&num); | ||
1656 | return size.x; | ||
1657 | } | ||
1658 | |||
1659 | static void draw_PlayerUI(iPlayerUI *d, iPaint *p) { | ||
1660 | const int playerBackground_ColorId = uiBackground_ColorId; | ||
1661 | const int playerFrame_ColorId = uiSeparator_ColorId; | ||
1662 | fillRect_Paint(p, d->bounds, playerBackground_ColorId); | ||
1663 | drawRect_Paint(p, d->bounds, playerFrame_ColorId); | ||
1664 | drawPlayerButton_(p, d->playPauseRect, isPaused_Player(d->player) ? "\U0001f782" : "\u23f8"); | ||
1665 | drawPlayerButton_(p, d->rewindRect, "\u23ee"); | ||
1666 | drawPlayerButton_(p, d->menuRect, "\U0001d362"); | ||
1667 | const int hgt = lineHeight_Text(uiLabel_FontId); | ||
1668 | const int yMid = mid_Rect(d->scrubberRect).y; | ||
1669 | const float playTime = time_Player(d->player); | ||
1670 | const float totalTime = duration_Player(d->player); | ||
1671 | const int bright = uiHeading_ColorId; | ||
1672 | const int dim = uiAnnotation_ColorId; | ||
1673 | int leftWidth = drawSevenSegmentTime_( | ||
1674 | init_I2(left_Rect(d->scrubberRect) + 2 * gap_UI, yMid - hgt / 2), | ||
1675 | isPaused_Player(d->player) ? dim : bright, | ||
1676 | left_Alignment, | ||
1677 | iRound(playTime)); | ||
1678 | int rightWidth = drawSevenSegmentTime_( | ||
1679 | init_I2(right_Rect(d->scrubberRect) - 2 * gap_UI, yMid - hgt / 2), | ||
1680 | dim, | ||
1681 | right_Alignment, | ||
1682 | iRound(totalTime)); | ||
1683 | /* Scrubber. */ | ||
1684 | const int s1 = left_Rect(d->scrubberRect) + leftWidth + 6 * gap_UI; | ||
1685 | const int s2 = right_Rect(d->scrubberRect) - rightWidth - 6 * gap_UI; | ||
1686 | const float normPos = totalTime > 0 ? playTime / totalTime : 0.0f; | ||
1687 | const int part = (s2 - s1) * normPos; | ||
1688 | const int scrubMax = (s2 - s1) * streamProgress_Player(d->player); | ||
1689 | drawHLine_Paint(p, init_I2(s1, yMid), part, bright); | ||
1690 | drawHLine_Paint(p, init_I2(s1 + part, yMid), scrubMax - part, dim); | ||
1691 | draw_Text(uiLabel_FontId, | ||
1692 | init_I2(s1 * (1.0f - normPos) + s2 * normPos - hgt / 3, yMid - hgt / 2), | ||
1693 | bright, | ||
1694 | "\u23fa"); | ||
1695 | } | ||
1696 | |||
1697 | static iRect audioPlayerRect_DocumentWidget_(const iDocumentWidget *d, const iGmRun *run) { | ||
1698 | const iRect docBounds = documentBounds_DocumentWidget_(d); | ||
1699 | return moved_Rect(run->bounds, addY_I2(topLeft_Rect(docBounds), -d->scrollY)); | ||
1700 | } | ||
1701 | |||
1702 | static iBool processAudioPlayerEvents_DocumentWidget_(iDocumentWidget *d, const SDL_Event *ev) { | ||
1703 | if (ev->type != SDL_MOUSEBUTTONDOWN && ev->type != SDL_MOUSEBUTTONUP && | ||
1704 | ev->type != SDL_MOUSEMOTION) { | ||
1705 | return iFalse; | ||
1706 | } | ||
1707 | const iInt2 mouse = init_I2(ev->button.x, ev->button.y); | ||
1708 | iConstForEach(PtrArray, i, &d->visiblePlayers) { | ||
1709 | const iGmRun *run = i.ptr; | ||
1710 | const iRect rect = audioPlayerRect_DocumentWidget_(d, run); | ||
1711 | if (contains_Rect(rect, mouse)) { | ||
1712 | if (ev->type == SDL_MOUSEBUTTONDOWN || ev->type == SDL_MOUSEMOTION) { | ||
1713 | refresh_Widget(d); | ||
1714 | return iTrue; | ||
1715 | } | ||
1716 | iPlayer *plr = audioPlayer_Media(media_GmDocument(d->doc), run->audioId); | ||
1717 | iPlayerUI ui; | ||
1718 | init_PlayerUI(&ui, plr, rect); | ||
1719 | if (contains_Rect(ui.playPauseRect, mouse)) { | ||
1720 | setPaused_Player(plr, !isPaused_Player(plr)); | ||
1721 | animatePlayingAudio_DocumentWidget_(d); | ||
1722 | return iTrue; | ||
1723 | } | ||
1724 | } | ||
1725 | } | ||
1726 | return iFalse; | ||
1727 | } | ||
1728 | |||
1594 | static iBool processEvent_DocumentWidget_(iDocumentWidget *d, const SDL_Event *ev) { | 1729 | static iBool processEvent_DocumentWidget_(iDocumentWidget *d, const SDL_Event *ev) { |
1595 | iWidget *w = as_Widget(d); | 1730 | iWidget *w = as_Widget(d); |
1596 | if (ev->type == SDL_USEREVENT && ev->user.code == command_UserEventCode) { | 1731 | if (ev->type == SDL_USEREVENT && ev->user.code == command_UserEventCode) { |
@@ -1813,6 +1948,9 @@ static iBool processEvent_DocumentWidget_(iDocumentWidget *d, const SDL_Event *e | |||
1813 | processContextMenuEvent_Widget(d->menu, ev, d->hoverLink = NULL); | 1948 | processContextMenuEvent_Widget(d->menu, ev, d->hoverLink = NULL); |
1814 | } | 1949 | } |
1815 | } | 1950 | } |
1951 | if (processAudioPlayerEvents_DocumentWidget_(d, ev)) { | ||
1952 | return iTrue; | ||
1953 | } | ||
1816 | switch (processEvent_Click(&d->click, ev)) { | 1954 | switch (processEvent_Click(&d->click, ev)) { |
1817 | case started_ClickResult: | 1955 | case started_ClickResult: |
1818 | d->selecting = iFalse; | 1956 | d->selecting = iFalse; |
@@ -2292,114 +2430,11 @@ static void drawSideElements_DocumentWidget_(const iDocumentWidget *d) { | |||
2292 | unsetClip_Paint(&p); | 2430 | unsetClip_Paint(&p); |
2293 | } | 2431 | } |
2294 | 2432 | ||
2295 | iDeclareType(PlayerUI) | ||
2296 | |||
2297 | struct Impl_PlayerUI { | ||
2298 | const iPlayer *player; | ||
2299 | iRect bounds; | ||
2300 | iRect playPauseRect; | ||
2301 | iRect rewindRect; | ||
2302 | iRect scrubberRect; | ||
2303 | iRect menuRect; | ||
2304 | }; | ||
2305 | |||
2306 | static void init_PlayerUI(iPlayerUI *d, const iPlayer *player, iRect bounds) { | ||
2307 | d->player = player; | ||
2308 | d->bounds = bounds; | ||
2309 | const int height = height_Rect(bounds); | ||
2310 | d->playPauseRect = (iRect){ addX_I2(topLeft_Rect(bounds), gap_UI / 2), init1_I2(height) }; | ||
2311 | d->rewindRect = (iRect){ topRight_Rect(d->playPauseRect), init1_I2(height) }; | ||
2312 | d->menuRect = (iRect){ addX_I2(topRight_Rect(bounds), -height - gap_UI / 2), init1_I2(height) }; | ||
2313 | d->scrubberRect = initCorners_Rect(topRight_Rect(d->rewindRect), bottomLeft_Rect(d->menuRect)); | ||
2314 | } | ||
2315 | |||
2316 | static void drawPlayerButton_(iPaint *p, iRect rect, const char *label) { | ||
2317 | const iInt2 mouse = mouseCoord_Window(get_Window()); | ||
2318 | const iBool isHover = contains_Rect(rect, mouse); | ||
2319 | const iBool isPressed = isHover && (SDL_GetMouseState(NULL, NULL) & SDL_BUTTON_LEFT) != 0; | ||
2320 | const int frame = (isPressed ? uiTextCaution_ColorId : isHover ? uiHeading_ColorId : uiAnnotation_ColorId); | ||
2321 | iRect frameRect = shrunk_Rect(rect, init_I2(gap_UI / 2, gap_UI)); | ||
2322 | drawRect_Paint(p, frameRect, frame); | ||
2323 | if (isPressed) { | ||
2324 | fillRect_Paint( | ||
2325 | p, | ||
2326 | adjusted_Rect(shrunk_Rect(frameRect, divi_I2(gap2_UI, 2)), zero_I2(), one_I2()), | ||
2327 | frame); | ||
2328 | } | ||
2329 | const int fg = isPressed ? uiBackground_ColorId : frame; | ||
2330 | drawCentered_Text(uiContent_FontId, frameRect, iTrue, fg, "%s", label); | ||
2331 | } | ||
2332 | |||
2333 | static int drawSevenSegmentTime_(iInt2 pos, int color, int align, int seconds) { /* returns width */ | ||
2334 | const uint32_t sevenSegmentDigit = 0x1fbf0; | ||
2335 | const int hours = seconds / 3600; | ||
2336 | const int mins = (seconds / 60) % 60; | ||
2337 | const int secs = seconds % 60; | ||
2338 | const int font = uiLabel_FontId; | ||
2339 | iString num; | ||
2340 | init_String(&num); | ||
2341 | if (hours) { | ||
2342 | appendChar_String(&num, sevenSegmentDigit + (hours % 10)); | ||
2343 | appendChar_String(&num, ':'); | ||
2344 | } | ||
2345 | appendChar_String(&num, sevenSegmentDigit + (mins / 10) % 10); | ||
2346 | appendChar_String(&num, sevenSegmentDigit + (mins % 10)); | ||
2347 | appendChar_String(&num, ':'); | ||
2348 | appendChar_String(&num, sevenSegmentDigit + (secs / 10) % 10); | ||
2349 | appendChar_String(&num, sevenSegmentDigit + (secs % 10)); | ||
2350 | iInt2 size = advanceRange_Text(font, range_String(&num)); | ||
2351 | if (align == right_Alignment) { | ||
2352 | pos.x -= size.x; | ||
2353 | } | ||
2354 | drawRange_Text(font, pos, color, range_String(&num)); | ||
2355 | deinit_String(&num); | ||
2356 | return size.x; | ||
2357 | } | ||
2358 | |||
2359 | static void draw_PlayerUI(iPlayerUI *d, iPaint *p) { | ||
2360 | const int playerBackground_ColorId = uiBackground_ColorId; | ||
2361 | const int playerFrame_ColorId = uiSeparator_ColorId; | ||
2362 | fillRect_Paint(p, d->bounds, playerBackground_ColorId); | ||
2363 | drawRect_Paint(p, d->bounds, playerFrame_ColorId); | ||
2364 | drawPlayerButton_(p, d->playPauseRect, isPaused_Player(d->player) ? "\U0001f782" : "\u23f8"); | ||
2365 | drawPlayerButton_(p, d->rewindRect, "\u23ee"); | ||
2366 | drawPlayerButton_(p, d->menuRect, "\U0001d362"); | ||
2367 | const int hgt = lineHeight_Text(uiLabel_FontId); | ||
2368 | const int yMid = mid_Rect(d->scrubberRect).y; | ||
2369 | const float playTime = time_Player(d->player); | ||
2370 | const float totalTime = duration_Player(d->player); | ||
2371 | const int bright = uiHeading_ColorId; | ||
2372 | const int dim = uiAnnotation_ColorId; | ||
2373 | int leftWidth = drawSevenSegmentTime_( | ||
2374 | init_I2(left_Rect(d->scrubberRect) + 2 * gap_UI, yMid - hgt / 2), | ||
2375 | isPaused_Player(d->player) ? dim : bright, | ||
2376 | left_Alignment, | ||
2377 | iRound(playTime)); | ||
2378 | int rightWidth = drawSevenSegmentTime_( | ||
2379 | init_I2(right_Rect(d->scrubberRect) - 2 * gap_UI, yMid - hgt / 2), | ||
2380 | dim, | ||
2381 | right_Alignment, | ||
2382 | iRound(totalTime)); | ||
2383 | /* Scrubber. */ | ||
2384 | const int s1 = left_Rect(d->scrubberRect) + leftWidth + 6 * gap_UI; | ||
2385 | const int s2 = right_Rect(d->scrubberRect) - rightWidth - 6 * gap_UI; | ||
2386 | const float normPos = totalTime > 0 ? playTime / totalTime : 0.0f; | ||
2387 | const int part = (s2 - s1) * normPos; | ||
2388 | const int scrubMax = (s2 - s1) * streamProgress_Player(d->player); | ||
2389 | drawHLine_Paint(p, init_I2(s1, yMid), part, bright); | ||
2390 | drawHLine_Paint(p, init_I2(s1 + part, yMid), scrubMax - part, dim); | ||
2391 | draw_Text(uiLabel_FontId, | ||
2392 | init_I2(s1 * (1.0f - normPos) + s2 * normPos - hgt / 3, yMid - hgt / 2), | ||
2393 | bright, | ||
2394 | "\u23fa"); | ||
2395 | } | ||
2396 | |||
2397 | static void drawAudioPlayers_DocumentWidget_(const iDocumentWidget *d, iPaint *p) { | 2433 | static void drawAudioPlayers_DocumentWidget_(const iDocumentWidget *d, iPaint *p) { |
2398 | const iRect docBounds = documentBounds_DocumentWidget_(d); | ||
2399 | iConstForEach(PtrArray, i, &d->visiblePlayers) { | 2434 | iConstForEach(PtrArray, i, &d->visiblePlayers) { |
2400 | const iGmRun * run = i.ptr; | 2435 | const iGmRun * run = i.ptr; |
2401 | const iPlayer *plr = audioPlayer_Media(media_GmDocument(d->doc), run->audioId); | 2436 | const iPlayer *plr = audioPlayer_Media(media_GmDocument(d->doc), run->audioId); |
2402 | const iRect rect = moved_Rect(run->bounds, addY_I2(topLeft_Rect(docBounds), -d->scrollY)); | 2437 | const iRect rect = audioPlayerRect_DocumentWidget_(d, run); |
2403 | iPlayerUI ui; | 2438 | iPlayerUI ui; |
2404 | init_PlayerUI(&ui, plr, rect); | 2439 | init_PlayerUI(&ui, plr, rect); |
2405 | draw_PlayerUI(&ui, p); | 2440 | draw_PlayerUI(&ui, p); |