diff options
author | Alon Albert <aalbert@google.com> | 2022-07-06 13:19:09 -0700 |
---|---|---|
committer | Alon Albert <aalbert@google.com> | 2022-07-07 01:59:49 +0000 |
commit | 211789ed67a635ac93030b075f6e2e407e517889 (patch) | |
tree | afb1b0579e08793eccdb9eb3716d4455af0401fb /logcat | |
parent | 19bcf2fc10abab258adaa22fc6c9536f5e1b2248 (diff) | |
download | idea-211789ed67a635ac93030b075f6e2e407e517889.tar.gz |
Allow Toggling Filter Favorite State By Clicking the Favorite Icon in the Dropdown
Fixes: 238228752
Test: Manually
Change-Id: I90dd63b06f3b6e83919949c7af385ee85ae75b37
Diffstat (limited to 'logcat')
-rw-r--r-- | logcat/src/com/android/tools/idea/logcat/filters/FilterTextField.kt | 73 | ||||
-rw-r--r-- | logcat/testSrc/com/android/tools/idea/logcat/filters/FilterTextFieldTest.kt | 10 |
2 files changed, 72 insertions, 11 deletions
diff --git a/logcat/src/com/android/tools/idea/logcat/filters/FilterTextField.kt b/logcat/src/com/android/tools/idea/logcat/filters/FilterTextField.kt index 8f49cdfc93c..2fb8a6e2b49 100644 --- a/logcat/src/com/android/tools/idea/logcat/filters/FilterTextField.kt +++ b/logcat/src/com/android/tools/idea/logcat/filters/FilterTextField.kt @@ -63,12 +63,15 @@ import org.jetbrains.annotations.TestOnly import org.jetbrains.annotations.VisibleForTesting import java.awt.Component import java.awt.Font +import java.awt.Point +import java.awt.Rectangle import java.awt.event.FocusAdapter import java.awt.event.FocusEvent import java.awt.event.KeyAdapter import java.awt.event.KeyEvent import java.awt.event.MouseAdapter import java.awt.event.MouseEvent +import java.awt.event.MouseEvent.BUTTON1 import java.net.URL import javax.swing.BorderFactory import javax.swing.BoxLayout @@ -269,7 +272,7 @@ internal class FilterTextField( val popupDisposable = Disposer.newDisposable("popupDisposable") addToHistory() - val list: JList<FilterHistoryItem> = HistoryList(popupDisposable, logcatPresenter, filterHistory, filterParser) + val list: JList<FilterHistoryItem> = HistoryList(popupDisposable) JBPopupFactory.getInstance().createPopupChooserBuilder(listOf("foo", "bar")) val popup = PopupChooserBuilder(list) .setMovable(false) @@ -356,11 +359,8 @@ internal class FilterTextField( * rendering from the JBList (HistoryList) directly. */ @VisibleForTesting - internal class HistoryList( + internal inner class HistoryList( parentDisposable: Disposable, - logcatPresenter: LogcatPresenter, - filterHistory: AndroidLogcatFilterHistory, - filterParser: LogcatFilterParser, coroutineContext: CoroutineContext = EmptyCoroutineContext, ) : JBList<FilterHistoryItem>() { init { @@ -408,6 +408,61 @@ internal class FilterTextField( } } } + + addMouseListener(MouseListener()) + } + + /** + * Toggle the Favorite state of an item. + * + * This method does several things: + * 1. Toggle [Item.isFavorite] + * 2. Update [FilterTextField.filterHistory] by moving the [Item.filter] to its new collection + * 3. If the item happens to be the current, item in the [FilterTextField.text] also toggle [FilterTextField.isFavorite] + * 4. Force a paint + */ + private fun toggleFavoriteItem(index: Int, bounds: Rectangle) { + val item = model.getElementAt(index) as Item + if (item.isFavorite) { + item.isFavorite = false + filterHistory.favorites.remove(item.filter) + filterHistory.nonFavorites.add(item.filter) + } + else { + item.isFavorite = true + filterHistory.favorites.add(item.filter) + filterHistory.nonFavorites.remove(item.filter) + } + if (item.filter == text) { + isFavorite = item.isFavorite + } + paintImmediately(bounds) + } + + /** + * Track mouse events and manipulate the UI to reflect them. For example, toggling Favorite state. + * + * This allows us to detect mouse events on specific UI areas such as the favorite icon. + * + * We need this because the implementation of JList is such that the UI doesn't actually contain any components. Rather, a dummy + * component (provided by the [ListCellRenderer.getListCellRendererComponent]) is used to draw directly onto the list + * [java.awt.Graphics]. Mouse listeners on the item components are not actually triggered. + * + * When processing a mouse event, we map the event location in the list to the specific item. We use [JList.getSelectedIndex] to find + * the item. This works because the item corresponding to the mouse event must be the selected item since the mouse is hovering on it. + */ + inner class MouseListener : MouseAdapter() { + override fun mouseReleased(event: MouseEvent) { + if (event.button == BUTTON1 && event.modifiersEx == 0) { + val index = selectedIndex + val cellLocation = getCellBounds(index, index).location + val favoriteIconBounds = Item.getFavoriteIconBounds(cellLocation) + if (favoriteIconBounds.contains(event.point)) { + toggleFavoriteItem(index, favoriteIconBounds) + event.consume() + } + } + } } } @@ -428,7 +483,7 @@ internal class FilterTextField( */ @VisibleForTesting internal sealed class FilterHistoryItem { - class Item(val filter: String, val isFavorite: Boolean, val count: Int?, private val filterParser: LogcatFilterParser) + class Item(val filter: String, var isFavorite: Boolean, val count: Int?, private val filterParser: LogcatFilterParser) : FilterHistoryItem() { private val filterName = filterParser.parse(filter)?.getFilterName() @@ -486,6 +541,8 @@ internal class FilterTextField( // HistoryListCellRenderer will use this component's paint() to render the ue. The component itself is not inserted into the tree. // The common pattern is to reuse the same component for all the items rather than allocate a new one for each item. companion object { + fun getFavoriteIconBounds(offset: Point): Rectangle = favoriteLabel.bounds + offset + private val favoriteLabel = JLabel() private val filterLabel = SimpleColoredComponent().apply { border = HISTORY_ITEM_LABEL_BORDER @@ -538,3 +595,7 @@ internal class FilterTextField( abstract fun getComponent(isSelected: Boolean, list: JList<out FilterHistoryItem>): JComponent } } + +private operator fun Rectangle.plus(point: Point): Rectangle { + return Rectangle(x + point.x, y + point.y, width, height) +} diff --git a/logcat/testSrc/com/android/tools/idea/logcat/filters/FilterTextFieldTest.kt b/logcat/testSrc/com/android/tools/idea/logcat/filters/FilterTextFieldTest.kt index 6c776cab81c..19dc8db302b 100644 --- a/logcat/testSrc/com/android/tools/idea/logcat/filters/FilterTextFieldTest.kt +++ b/logcat/testSrc/com/android/tools/idea/logcat/filters/FilterTextFieldTest.kt @@ -235,7 +235,7 @@ class FilterTextFieldTest { filterHistory.add("bar", isFavorite = false) fakeLogcatPresenter.filterMatchesCount["foo"] = 1 fakeLogcatPresenter.filterMatchesCount["bar"] = 2 - val historyList = HistoryList(project, fakeLogcatPresenter, filterHistory, logcatFilterParser, coroutineContext) + val historyList = filterTextField().HistoryList(project, coroutineContext) historyList.waitForCounts(fakeLogcatPresenter) assertThat(historyList.renderToStrings()).containsExactly( @@ -255,7 +255,7 @@ class FilterTextFieldTest { filterHistory.add("bar", isFavorite = true) fakeLogcatPresenter.filterMatchesCount["foo"] = 1 fakeLogcatPresenter.filterMatchesCount["bar"] = 2 - val historyList = HistoryList(project, fakeLogcatPresenter, filterHistory, logcatFilterParser, coroutineContext) + val historyList = filterTextField().HistoryList(project, coroutineContext) historyList.waitForCounts(fakeLogcatPresenter) assertThat(historyList.renderToStrings()).containsExactly( @@ -274,7 +274,7 @@ class FilterTextFieldTest { filterHistory.add("bar", isFavorite = false) fakeLogcatPresenter.filterMatchesCount["foo"] = 1 fakeLogcatPresenter.filterMatchesCount["bar"] = 2 - val historyList = HistoryList(project, fakeLogcatPresenter, filterHistory,logcatFilterParser, coroutineContext) + val historyList = filterTextField().HistoryList(project, coroutineContext) historyList.waitForCounts(fakeLogcatPresenter) assertThat(historyList.renderToStrings()).containsExactly( @@ -291,7 +291,7 @@ class FilterTextFieldTest { fun historyList_renderNamedFilter() = runBlockingTest { filterHistory.add("name:Foo tag:Foo", isFavorite = false) fakeLogcatPresenter.filterMatchesCount["name:Foo tag:Foo"] = 1 - val historyList = HistoryList(project, fakeLogcatPresenter, filterHistory, logcatFilterParser, coroutineContext) + val historyList = filterTextField().HistoryList(project, coroutineContext) historyList.waitForCounts(fakeLogcatPresenter) assertThat(historyList.renderToStrings()).containsExactly( @@ -309,7 +309,7 @@ class FilterTextFieldTest { filterHistory.add("name:Foo tag:Foobar", isFavorite = false) fakeLogcatPresenter.filterMatchesCount["name:Foo tag:Foo"] = 1 fakeLogcatPresenter.filterMatchesCount["name:Foo tag:Foobar"] = 2 - val historyList = HistoryList(project, fakeLogcatPresenter, filterHistory, logcatFilterParser, coroutineContext) + val historyList = filterTextField().HistoryList(project, coroutineContext) historyList.waitForCounts(fakeLogcatPresenter) assertThat(historyList.renderToStrings()).containsExactly( |