diff options
author | Jaakko Keränen <jaakko.keranen@iki.fi> | 2020-10-09 07:04:11 +0300 |
---|---|---|
committer | Jaakko Keränen <jaakko.keranen@iki.fi> | 2020-10-09 07:04:11 +0300 |
commit | bb7bc6fac4fec804846d11c7d77e1b553ba2be6a (patch) | |
tree | c170f134b5ac01c29195589cc77ae4598a524eb3 | |
parent | 5b58cc76bd08d0e061c5f14506d97ee8dc3b7174 (diff) |
Fixed memory leak on tab close
The DocumentWidget was not actually deleted when a tab was closed, only hidden.
-rw-r--r-- | src/app.c | 10 | ||||
-rw-r--r-- | src/app.h | 5 | ||||
-rw-r--r-- | src/audio/player.c | 6 | ||||
-rw-r--r-- | src/audio/player.h | 2 | ||||
-rw-r--r-- | src/media.c | 4 | ||||
-rw-r--r-- | src/ui/documentwidget.c | 40 | ||||
-rw-r--r-- | src/ui/util.c | 3 | ||||
-rw-r--r-- | src/ui/widget.c | 4 |
8 files changed, 62 insertions, 12 deletions
@@ -649,12 +649,17 @@ iAny *findWidget_App(const char *id) { | |||
649 | return findChild_Widget(app_.window->root, id); | 649 | return findChild_Widget(app_.window->root, id); |
650 | } | 650 | } |
651 | 651 | ||
652 | void addTicker_App(void (*ticker)(iAny *), iAny *context) { | 652 | void addTicker_App(iTickerFunc ticker, iAny *context) { |
653 | iApp *d = &app_; | 653 | iApp *d = &app_; |
654 | insert_SortedArray(&d->tickers, &(iTicker){ context, ticker }); | 654 | insert_SortedArray(&d->tickers, &(iTicker){ context, ticker }); |
655 | postRefresh_App(); | 655 | postRefresh_App(); |
656 | } | 656 | } |
657 | 657 | ||
658 | void removeTicker_App(iTickerFunc ticker, iAny *context) { | ||
659 | iApp *d = &app_; | ||
660 | remove_SortedArray(&d->tickers, &(iTicker){ context, ticker }); | ||
661 | } | ||
662 | |||
658 | iGmCerts *certs_App(void) { | 663 | iGmCerts *certs_App(void) { |
659 | return app_.certs; | 664 | return app_.certs; |
660 | } | 665 | } |
@@ -737,7 +742,8 @@ iDocumentWidget *newTab_App(const iDocumentWidget *duplicateOf, iBool switchToNe | |||
737 | doc = new_DocumentWidget(); | 742 | doc = new_DocumentWidget(); |
738 | } | 743 | } |
739 | setId_Widget(as_Widget(doc), format_CStr("document%03d", ++d->tabEnum)); | 744 | setId_Widget(as_Widget(doc), format_CStr("document%03d", ++d->tabEnum)); |
740 | appendTabPage_Widget(tabs, iClob(doc), "", 0, 0); | 745 | appendTabPage_Widget(tabs, as_Widget(doc), "", 0, 0); |
746 | iRelease(doc); /* now owned by the tabs */ | ||
741 | addChild_Widget(findChild_Widget(tabs, "tabs.buttons"), iClob(newTabButton)); | 747 | addChild_Widget(findChild_Widget(tabs, "tabs.buttons"), iClob(newTabButton)); |
742 | if (switchToNew) { | 748 | if (switchToNew) { |
743 | postCommandf_App("tabs.switch page:%p", doc); | 749 | postCommandf_App("tabs.switch page:%p", doc); |
@@ -73,8 +73,11 @@ iObjectList * listDocuments_App (void); | |||
73 | iDocumentWidget * document_Command (const char *cmd); | 73 | iDocumentWidget * document_Command (const char *cmd); |
74 | iDocumentWidget * newTab_App (const iDocumentWidget *duplicateOf, iBool switchToNew); | 74 | iDocumentWidget * newTab_App (const iDocumentWidget *duplicateOf, iBool switchToNew); |
75 | 75 | ||
76 | typedef void (*iTickerFunc)(iAny *); | ||
77 | |||
76 | iAny * findWidget_App (const char *id); | 78 | iAny * findWidget_App (const char *id); |
77 | void addTicker_App (void (*ticker)(iAny *), iAny *context); | 79 | void addTicker_App (iTickerFunc ticker, iAny *context); |
80 | void removeTicker_App (iTickerFunc ticker, iAny *context); | ||
78 | void postRefresh_App (void); | 81 | void postRefresh_App (void); |
79 | void postCommand_App (const char *command); | 82 | void postCommand_App (const char *command); |
80 | void postCommandf_App (const char *command, ...); | 83 | void postCommandf_App (const char *command, ...); |
diff --git a/src/audio/player.c b/src/audio/player.c index 5b9d0103..07f41f01 100644 --- a/src/audio/player.c +++ b/src/audio/player.c | |||
@@ -563,3 +563,9 @@ float streamProgress_Player(const iPlayer *d) { | |||
563 | } | 563 | } |
564 | return 0; | 564 | return 0; |
565 | } | 565 | } |
566 | |||
567 | iString *metadataLabel_Player(const iPlayer *d) { | ||
568 | return newFormat_String("%d-bit %s %d Hz", SDL_AUDIO_BITSIZE(d->decoder->inputFormat), | ||
569 | SDL_AUDIO_ISFLOAT(d->decoder->inputFormat) ? "float" : "integer", | ||
570 | d->spec.freq); | ||
571 | } | ||
diff --git a/src/audio/player.h b/src/audio/player.h index c3552640..fe6717b0 100644 --- a/src/audio/player.h +++ b/src/audio/player.h | |||
@@ -45,3 +45,5 @@ iBool isPaused_Player (const iPlayer *); | |||
45 | float time_Player (const iPlayer *); | 45 | float time_Player (const iPlayer *); |
46 | float duration_Player (const iPlayer *); | 46 | float duration_Player (const iPlayer *); |
47 | float streamProgress_Player (const iPlayer *); /* normalized 0...1 */ | 47 | float streamProgress_Player (const iPlayer *); /* normalized 0...1 */ |
48 | |||
49 | iString * metadataLabel_Player (const iPlayer *); | ||
diff --git a/src/media.c b/src/media.c index 253893fc..c447704b 100644 --- a/src/media.c +++ b/src/media.c | |||
@@ -75,8 +75,8 @@ void deinit_GmImage(iGmImage *d) { | |||
75 | } | 75 | } |
76 | 76 | ||
77 | void makeTexture_GmImage(iGmImage *d) { | 77 | void makeTexture_GmImage(iGmImage *d) { |
78 | iBlock *data = &d->partialData; | 78 | iBlock *data = &d->partialData; |
79 | d->numBytes = size_Block(data); | 79 | d->numBytes = size_Block(data); |
80 | uint8_t *imgData = stbi_load_from_memory( | 80 | uint8_t *imgData = stbi_load_from_memory( |
81 | constData_Block(data), size_Block(data), &d->size.x, &d->size.y, NULL, 4); | 81 | constData_Block(data), size_Block(data), &d->size.x, &d->size.y, NULL, 4); |
82 | if (!imgData) { | 82 | if (!imgData) { |
diff --git a/src/ui/documentwidget.c b/src/ui/documentwidget.c index 9d94f200..bbe5ccba 100644 --- a/src/ui/documentwidget.c +++ b/src/ui/documentwidget.c | |||
@@ -191,6 +191,7 @@ struct Impl_DocumentWidget { | |||
191 | iAnim outlineOpacity; | 191 | iAnim outlineOpacity; |
192 | iArray outline; | 192 | iArray outline; |
193 | iWidget * menu; | 193 | iWidget * menu; |
194 | iWidget * playerMenu; | ||
194 | iVisBuf * visBuf; | 195 | iVisBuf * visBuf; |
195 | iPtrSet * invalidRuns; | 196 | iPtrSet * invalidRuns; |
196 | }; | 197 | }; |
@@ -241,6 +242,7 @@ void init_DocumentWidget(iDocumentWidget *d) { | |||
241 | init_Click(&d->click, d, SDL_BUTTON_LEFT); | 242 | init_Click(&d->click, d, SDL_BUTTON_LEFT); |
242 | addChild_Widget(w, iClob(d->scroll = new_ScrollWidget())); | 243 | addChild_Widget(w, iClob(d->scroll = new_ScrollWidget())); |
243 | d->menu = NULL; /* created when clicking */ | 244 | d->menu = NULL; /* created when clicking */ |
245 | d->playerMenu = NULL; | ||
244 | #if !defined (iPlatformApple) /* in system menu */ | 246 | #if !defined (iPlatformApple) /* in system menu */ |
245 | addAction_Widget(w, reload_KeyShortcut, "navigate.reload"); | 247 | addAction_Widget(w, reload_KeyShortcut, "navigate.reload"); |
246 | addAction_Widget(w, SDLK_w, KMOD_PRIMARY, "tabs.close"); | 248 | addAction_Widget(w, SDLK_w, KMOD_PRIMARY, "tabs.close"); |
@@ -249,7 +251,10 @@ void init_DocumentWidget(iDocumentWidget *d) { | |||
249 | addAction_Widget(w, navigateForward_KeyShortcut, "navigate.forward"); | 251 | addAction_Widget(w, navigateForward_KeyShortcut, "navigate.forward"); |
250 | } | 252 | } |
251 | 253 | ||
254 | static void animatePlayingAudio_DocumentWidget_(void *); | ||
255 | |||
252 | void deinit_DocumentWidget(iDocumentWidget *d) { | 256 | void deinit_DocumentWidget(iDocumentWidget *d) { |
257 | removeTicker_App(animatePlayingAudio_DocumentWidget_, d); | ||
253 | delete_VisBuf(d->visBuf); | 258 | delete_VisBuf(d->visBuf); |
254 | delete_PtrSet(d->invalidRuns); | 259 | delete_PtrSet(d->invalidRuns); |
255 | deinit_Array(&d->outline); | 260 | deinit_Array(&d->outline); |
@@ -1626,7 +1631,7 @@ static void drawPlayerButton_(iPaint *p, iRect rect, const char *label) { | |||
1626 | adjusted_Rect(shrunk_Rect(frameRect, divi_I2(gap2_UI, 2)), zero_I2(), one_I2()), | 1631 | adjusted_Rect(shrunk_Rect(frameRect, divi_I2(gap2_UI, 2)), zero_I2(), one_I2()), |
1627 | frame); | 1632 | frame); |
1628 | } | 1633 | } |
1629 | const int fg = isPressed ? uiBackground_ColorId : frame; | 1634 | const int fg = isPressed ? (permanent_ColorId | uiBackground_ColorId) : uiHeading_ColorId; |
1630 | drawCentered_Text(uiContent_FontId, frameRect, iTrue, fg, "%s", label); | 1635 | drawCentered_Text(uiContent_FontId, frameRect, iTrue, fg, "%s", label); |
1631 | } | 1636 | } |
1632 | 1637 | ||
@@ -1704,6 +1709,11 @@ static iBool processAudioPlayerEvents_DocumentWidget_(iDocumentWidget *d, const | |||
1704 | ev->type != SDL_MOUSEMOTION) { | 1709 | ev->type != SDL_MOUSEMOTION) { |
1705 | return iFalse; | 1710 | return iFalse; |
1706 | } | 1711 | } |
1712 | if (ev->type == SDL_MOUSEBUTTONDOWN || ev->type == SDL_MOUSEBUTTONUP) { | ||
1713 | if (ev->button.button != SDL_BUTTON_LEFT) { | ||
1714 | return iFalse; | ||
1715 | } | ||
1716 | } | ||
1707 | const iInt2 mouse = init_I2(ev->button.x, ev->button.y); | 1717 | const iInt2 mouse = init_I2(ev->button.x, ev->button.y); |
1708 | iConstForEach(PtrArray, i, &d->visiblePlayers) { | 1718 | iConstForEach(PtrArray, i, &d->visiblePlayers) { |
1709 | const iGmRun *run = i.ptr; | 1719 | const iGmRun *run = i.ptr; |
@@ -1722,12 +1732,34 @@ static iBool processAudioPlayerEvents_DocumentWidget_(iDocumentWidget *d, const | |||
1722 | return iTrue; | 1732 | return iTrue; |
1723 | } | 1733 | } |
1724 | else if (contains_Rect(ui.rewindRect, mouse)) { | 1734 | else if (contains_Rect(ui.rewindRect, mouse)) { |
1725 | stop_Player(plr); | 1735 | if (isStarted_Player(plr) && time_Player(plr) > 0.5f) { |
1726 | start_Player(plr); | 1736 | stop_Player(plr); |
1727 | setPaused_Player(plr, iTrue); | 1737 | start_Player(plr); |
1738 | setPaused_Player(plr, iTrue); | ||
1739 | } | ||
1728 | refresh_Widget(d); | 1740 | refresh_Widget(d); |
1729 | return iTrue; | 1741 | return iTrue; |
1730 | } | 1742 | } |
1743 | else if (contains_Rect(ui.menuRect, mouse)) { | ||
1744 | /* TODO: Add menu items for: | ||
1745 | - output device | ||
1746 | - Save to Downloads | ||
1747 | */ | ||
1748 | if (d->playerMenu) { | ||
1749 | destroy_Widget(d->playerMenu); | ||
1750 | d->playerMenu = NULL; | ||
1751 | return iTrue; | ||
1752 | } | ||
1753 | d->playerMenu = makeMenu_Widget( | ||
1754 | as_Widget(d), | ||
1755 | (iMenuItem[]){ | ||
1756 | { cstrCollect_String(metadataLabel_Player(plr)), 0, 0, NULL }, | ||
1757 | }, | ||
1758 | 1); | ||
1759 | openMenu_Widget(d->playerMenu, | ||
1760 | localCoord_Widget(constAs_Widget(d), bottomLeft_Rect(ui.menuRect))); | ||
1761 | return iTrue; | ||
1762 | } | ||
1731 | } | 1763 | } |
1732 | } | 1764 | } |
1733 | return iFalse; | 1765 | return iFalse; |
diff --git a/src/ui/util.c b/src/ui/util.c index 13a7a7a2..89f71da2 100644 --- a/src/ui/util.c +++ b/src/ui/util.c | |||
@@ -498,9 +498,8 @@ iWidget *removeTabPage_Widget(iWidget *tabs, size_t index) { | |||
498 | iWidget *button = removeChild_Widget(buttons, child_Widget(buttons, index)); | 498 | iWidget *button = removeChild_Widget(buttons, child_Widget(buttons, index)); |
499 | iRelease(button); | 499 | iRelease(button); |
500 | iWidget *page = child_Widget(pages, index); | 500 | iWidget *page = child_Widget(pages, index); |
501 | ref_Object(page); | ||
502 | setFlags_Widget(page, hidden_WidgetFlag | disabled_WidgetFlag, iFalse); | 501 | setFlags_Widget(page, hidden_WidgetFlag | disabled_WidgetFlag, iFalse); |
503 | removeChild_Widget(pages, page); | 502 | removeChild_Widget(pages, page); /* `page` is now ours */ |
504 | if (tabCount_Widget(tabs) <= 1 && flags_Widget(buttons) & collapse_WidgetFlag) { | 503 | if (tabCount_Widget(tabs) <= 1 && flags_Widget(buttons) & collapse_WidgetFlag) { |
505 | setFlags_Widget(buttons, hidden_WidgetFlag, iTrue); | 504 | setFlags_Widget(buttons, hidden_WidgetFlag, iTrue); |
506 | } | 505 | } |
diff --git a/src/ui/widget.c b/src/ui/widget.c index 459c2ae1..ea2e3fe2 100644 --- a/src/ui/widget.c +++ b/src/ui/widget.c | |||
@@ -55,7 +55,6 @@ iPtrArray *onTop_RootData_(void) { | |||
55 | void destroyPending_Widget(void) { | 55 | void destroyPending_Widget(void) { |
56 | iForEach(PtrSet, i, rootData_.pendingDestruction) { | 56 | iForEach(PtrSet, i, rootData_.pendingDestruction) { |
57 | iWidget *widget = *i.value; | 57 | iWidget *widget = *i.value; |
58 | removeOne_PtrArray(onTop_RootData_(), widget); | ||
59 | if (widget->parent) { | 58 | if (widget->parent) { |
60 | iRelease(removeChild_Widget(widget->parent, widget)); | 59 | iRelease(removeChild_Widget(widget->parent, widget)); |
61 | } | 60 | } |
@@ -90,6 +89,9 @@ static void aboutToBeDestroyed_Widget_(iWidget *d) { | |||
90 | setFocus_Widget(NULL); | 89 | setFocus_Widget(NULL); |
91 | return; | 90 | return; |
92 | } | 91 | } |
92 | if (flags_Widget(d) & keepOnTop_WidgetFlag) { | ||
93 | removeOne_PtrArray(onTop_RootData_(), d); | ||
94 | } | ||
93 | if (isHover_Widget(d)) { | 95 | if (isHover_Widget(d)) { |
94 | rootData_.hover = NULL; | 96 | rootData_.hover = NULL; |
95 | } | 97 | } |