mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
[github] Simplify pull requests list components structure
This commit is contained in:
+65
-54
@@ -9,7 +9,6 @@ import com.intellij.openapi.progress.ProgressManager
|
||||
import com.intellij.openapi.project.Project
|
||||
import com.intellij.openapi.util.Disposer
|
||||
import com.intellij.ui.OnePixelSplitter
|
||||
import com.intellij.ui.components.panels.Wrapper
|
||||
import git4idea.commands.Git
|
||||
import git4idea.repo.GitRemote
|
||||
import git4idea.repo.GitRepository
|
||||
@@ -19,9 +18,14 @@ import org.jetbrains.plugins.github.authentication.accounts.GithubAccount
|
||||
import org.jetbrains.plugins.github.pullrequest.action.GithubPullRequestKeys
|
||||
import org.jetbrains.plugins.github.pullrequest.avatars.CachingGithubAvatarIconsProvider
|
||||
import org.jetbrains.plugins.github.pullrequest.config.GithubPullRequestsUISettings
|
||||
import org.jetbrains.plugins.github.pullrequest.config.GithubPullRequestsUISettings.SettingsChangedListener
|
||||
import org.jetbrains.plugins.github.pullrequest.data.GithubPullRequestsDataLoader
|
||||
import org.jetbrains.plugins.github.pullrequest.data.GithubPullRequestsLoader
|
||||
import org.jetbrains.plugins.github.pullrequest.ui.*
|
||||
import org.jetbrains.plugins.github.pullrequest.ui.GithubPullRequestChangesComponent
|
||||
import org.jetbrains.plugins.github.pullrequest.ui.GithubPullRequestDetailsComponent
|
||||
import org.jetbrains.plugins.github.pullrequest.ui.GithubPullRequestPreviewComponent
|
||||
import org.jetbrains.plugins.github.pullrequest.ui.GithubPullRequestsListComponent
|
||||
import org.jetbrains.plugins.github.pullrequest.ui.GithubPullRequestsListSelectionModel.SelectionChangedListener
|
||||
import org.jetbrains.plugins.github.util.CachingGithubUserAvatarLoader
|
||||
import org.jetbrains.plugins.github.util.GithubImageResizer
|
||||
import javax.swing.JComponent
|
||||
@@ -40,69 +44,76 @@ class GithubPullRequestsComponentFactory(private val project: Project,
|
||||
repository: GitRepository, remote: GitRemote,
|
||||
repoDetails: GithubRepoDetailed,
|
||||
account: GithubAccount): JComponent? {
|
||||
val avatarIconsProviderFactory = CachingGithubAvatarIconsProvider.Factory(avatarLoader, imageResizer, requestExecutor)
|
||||
return GithubPullRequestsComponent(requestExecutor, avatarIconsProviderFactory, repository, remote, repoDetails, account)
|
||||
}
|
||||
|
||||
val listLoader = GithubPullRequestsLoader(progressManager, requestExecutor,
|
||||
account.server, repoDetails.fullPath)
|
||||
val selectionModel = GithubPullRequestsListSelectionModel()
|
||||
val list = GithubPullRequestsListComponent(project, actionManager, autoPopupController,
|
||||
selectionModel, listLoader,
|
||||
CachingGithubAvatarIconsProvider.Factory(avatarLoader, imageResizer, requestExecutor))
|
||||
requestExecutor.addListener(list) { list.refresh() }
|
||||
private inner class GithubPullRequestsComponent(requestExecutor: GithubApiRequestExecutor,
|
||||
avatarIconsProviderFactory: CachingGithubAvatarIconsProvider.Factory,
|
||||
private val repository: GitRepository, private val remote: GitRemote,
|
||||
private val repoDetails: GithubRepoDetailed,
|
||||
private val account: GithubAccount)
|
||||
: OnePixelSplitter("Github.PullRequests.Component", 0.6f), Disposable, DataProvider {
|
||||
|
||||
val dataLoader = GithubPullRequestsDataLoader(project, progressManager, git, requestExecutor, repository, remote)
|
||||
private val dataLoader = GithubPullRequestsDataLoader(project, progressManager, git, requestExecutor, repository, remote)
|
||||
|
||||
val changes = GithubPullRequestChangesComponent(project, selectionModel, dataLoader, actionManager)
|
||||
val details = GithubPullRequestDetailsComponent(project, selectionModel, dataLoader)
|
||||
private val changes = GithubPullRequestChangesComponent(project, actionManager).apply {
|
||||
diffAction.registerCustomShortcutSet(this@GithubPullRequestsComponent, this@GithubPullRequestsComponent)
|
||||
}
|
||||
private val details = GithubPullRequestDetailsComponent(project)
|
||||
private val preview = GithubPullRequestPreviewComponent(changes, details).apply {
|
||||
detailsVisible = uiSettings.showDetails
|
||||
}
|
||||
|
||||
val preview = GithubPullRequestPreviewComponent(uiSettings, changes, details)
|
||||
list.setToolbarHeightReferent(preview.toolbarComponent)
|
||||
private val listLoader = GithubPullRequestsLoader(progressManager, requestExecutor, account.server, repoDetails.fullPath)
|
||||
private val list = GithubPullRequestsListComponent(project, actionManager, autoPopupController,
|
||||
listLoader,
|
||||
avatarIconsProviderFactory).apply {
|
||||
requestExecutor.addListener(this) { this.refresh() }
|
||||
setToolbarHeightReferent(preview.toolbarComponent)
|
||||
}
|
||||
|
||||
val splitter = OnePixelSplitter("Github.PullRequests.Component", 0.6f)
|
||||
splitter.firstComponent = list
|
||||
splitter.secondComponent = preview
|
||||
|
||||
// disposed by content manager when tab is closed
|
||||
val wrapper = WrappingComponent(splitter,
|
||||
repository, remote, repoDetails, account,
|
||||
list,selectionModel, dataLoader)
|
||||
Disposer.register(wrapper, Disposable {
|
||||
init {
|
||||
firstComponent = list
|
||||
secondComponent = preview
|
||||
isFocusCycleRoot = true
|
||||
|
||||
uiSettings.addChangesListener(object : SettingsChangedListener {
|
||||
override fun settingsChanged() {
|
||||
preview.detailsVisible = uiSettings.showDetails
|
||||
}
|
||||
}, preview)
|
||||
|
||||
list.selectionModel.addChangesListener(object : SelectionChangedListener {
|
||||
override fun selectionChanged() {
|
||||
val dataProvider = list.selectionModel.current?.let(dataLoader::getDataProvider)
|
||||
preview.setPreviewDataProvider(dataProvider)
|
||||
}
|
||||
}, preview)
|
||||
}
|
||||
|
||||
override fun getData(dataId: String): Any? {
|
||||
return when {
|
||||
GithubPullRequestKeys.REPOSITORY.`is`(dataId) -> repository
|
||||
GithubPullRequestKeys.REMOTE.`is`(dataId) -> remote
|
||||
GithubPullRequestKeys.REPO_DETAILS.`is`(dataId) -> repoDetails
|
||||
GithubPullRequestKeys.SERVER_PATH.`is`(dataId) -> account.server
|
||||
GithubPullRequestKeys.PULL_REQUESTS_LIST_COMPONENT.`is`(dataId) -> list
|
||||
GithubPullRequestKeys.SELECTED_PULL_REQUEST_DATA_PROVIDER.`is`(dataId) ->
|
||||
list.selectionModel.current?.let(dataLoader::getDataProvider)
|
||||
else -> null
|
||||
}
|
||||
}
|
||||
|
||||
override fun dispose() {
|
||||
Disposer.dispose(list)
|
||||
Disposer.dispose(preview)
|
||||
Disposer.dispose(changes)
|
||||
Disposer.dispose(details)
|
||||
|
||||
Disposer.dispose(listLoader)
|
||||
Disposer.dispose(dataLoader)
|
||||
})
|
||||
changes.diffAction.registerCustomShortcutSet(wrapper, wrapper)
|
||||
return wrapper
|
||||
}
|
||||
|
||||
companion object {
|
||||
private class WrappingComponent(wrapped: JComponent,
|
||||
private val repository: GitRepository,
|
||||
private val remote: GitRemote,
|
||||
private val repoDetails: GithubRepoDetailed,
|
||||
private val account: GithubAccount,
|
||||
private val list: GithubPullRequestsListComponent,
|
||||
private val selectionModel: GithubPullRequestsListSelectionModel,
|
||||
private val dataLoader: GithubPullRequestsDataLoader)
|
||||
: Wrapper(wrapped), Disposable, DataProvider {
|
||||
init {
|
||||
isFocusCycleRoot = true
|
||||
}
|
||||
|
||||
override fun getData(dataId: String): Any? {
|
||||
return when {
|
||||
GithubPullRequestKeys.REPOSITORY.`is`(dataId) -> repository
|
||||
GithubPullRequestKeys.REMOTE.`is`(dataId) -> remote
|
||||
GithubPullRequestKeys.REPO_DETAILS.`is`(dataId) -> repoDetails
|
||||
GithubPullRequestKeys.SERVER_PATH.`is`(dataId) -> account.server
|
||||
GithubPullRequestKeys.PULL_REQUESTS_LIST_COMPONENT.`is`(dataId) -> list
|
||||
GithubPullRequestKeys.SELECTED_PULL_REQUEST_DATA_PROVIDER.`is`(dataId) -> selectionModel.current?.let(dataLoader::getDataProvider)
|
||||
else -> null
|
||||
}
|
||||
}
|
||||
|
||||
override fun dispose() {}
|
||||
}
|
||||
}
|
||||
}
|
||||
+44
@@ -0,0 +1,44 @@
|
||||
// Copyright 2000-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
|
||||
package org.jetbrains.plugins.github.pullrequest.ui
|
||||
|
||||
import com.intellij.ui.components.panels.Wrapper
|
||||
import org.jetbrains.annotations.CalledInAwt
|
||||
import org.jetbrains.plugins.github.util.GithubAsyncUtil
|
||||
import org.jetbrains.plugins.github.util.handleOnEdt
|
||||
import java.util.concurrent.CompletableFuture
|
||||
|
||||
abstract class GithubDataLoadingComponent<T> : Wrapper() {
|
||||
private var updateFuture: CompletableFuture<Unit>? = null
|
||||
|
||||
/**
|
||||
* This works because [handleOnEdt] basically forms a EDT-synchronized section and result/exception is acquired from [dataRequest] on EDT
|
||||
*
|
||||
* In pseudocode:
|
||||
* when (dataRequest.isDone) { runOnEdt { handler(getResult(), getException()) } }
|
||||
*/
|
||||
@CalledInAwt
|
||||
fun loadAndShow(dataRequest: CompletableFuture<T>?) {
|
||||
updateFuture?.cancel(true)
|
||||
reset()
|
||||
|
||||
if (dataRequest == null) {
|
||||
updateFuture = null
|
||||
setBusy(false)
|
||||
return
|
||||
}
|
||||
|
||||
setBusy(true)
|
||||
updateFuture = dataRequest.handleOnEdt { result, error ->
|
||||
when {
|
||||
error != null && !GithubAsyncUtil.isCancellation(error) -> handleError(error)
|
||||
result != null -> handleResult(result)
|
||||
}
|
||||
setBusy(false)
|
||||
}
|
||||
}
|
||||
|
||||
protected abstract fun reset()
|
||||
protected abstract fun handleResult(result: T)
|
||||
protected abstract fun handleError(error: Throwable)
|
||||
protected abstract fun setBusy(busy: Boolean)
|
||||
}
|
||||
+28
-48
@@ -15,73 +15,53 @@ import com.intellij.ui.IdeBorderFactory
|
||||
import com.intellij.ui.SideBorder
|
||||
import com.intellij.ui.SimpleTextAttributes
|
||||
import com.intellij.ui.components.JBLoadingPanel
|
||||
import com.intellij.ui.components.panels.Wrapper
|
||||
import com.intellij.util.ui.ComponentWithEmptyText
|
||||
import org.jetbrains.plugins.github.api.data.GithubSearchedIssue
|
||||
import org.jetbrains.plugins.github.pullrequest.data.GithubPullRequestsDataLoader
|
||||
import org.jetbrains.plugins.github.util.GithubAsyncUtil
|
||||
import org.jetbrains.plugins.github.util.handleOnEdt
|
||||
import java.awt.BorderLayout
|
||||
import java.util.concurrent.CompletableFuture
|
||||
import javax.swing.JComponent
|
||||
import javax.swing.border.Border
|
||||
import kotlin.properties.Delegates
|
||||
|
||||
class GithubPullRequestChangesComponent(project: Project,
|
||||
private val selectionModel: GithubPullRequestsListSelectionModel,
|
||||
private val dataLoader: GithubPullRequestsDataLoader,
|
||||
actionManager: ActionManager)
|
||||
: Wrapper(), Disposable, GithubPullRequestsListSelectionModel.SelectionChangedListener {
|
||||
class GithubPullRequestChangesComponent(project: Project, actionManager: ActionManager)
|
||||
: GithubDataLoadingComponent<List<Change>>(), Disposable {
|
||||
private val changesBrowser = PullRequestChangesBrowserWithError(project, actionManager)
|
||||
private val loadingPanel = JBLoadingPanel(BorderLayout(), this, ProgressWindow.DEFAULT_PROGRESS_DIALOG_POSTPONE_TIME_MILLIS)
|
||||
|
||||
val toolbarComponent: JComponent = changesBrowser.toolbar.component
|
||||
val diffAction = changesBrowser.diffAction
|
||||
private val changesLoadingPanel = JBLoadingPanel(BorderLayout(), this,
|
||||
ProgressWindow.DEFAULT_PROGRESS_DIALOG_POSTPONE_TIME_MILLIS)
|
||||
|
||||
private var updateFuture: CompletableFuture<Unit>? = null
|
||||
|
||||
init {
|
||||
selectionModel.addChangesListener(this, this)
|
||||
|
||||
changesLoadingPanel.add(changesBrowser, BorderLayout.CENTER)
|
||||
setContent(changesLoadingPanel)
|
||||
loadingPanel.add(changesBrowser, BorderLayout.CENTER)
|
||||
changesBrowser.emptyText.text = DEFAULT_EMPTY_TEXT
|
||||
setContent(loadingPanel)
|
||||
}
|
||||
|
||||
override fun selectionChanged() {
|
||||
reset()
|
||||
updateFuture = updateChanges(selectionModel.current)
|
||||
}
|
||||
|
||||
private fun updateChanges(item: GithubSearchedIssue?) =
|
||||
item?.let { selection ->
|
||||
changesBrowser.emptyText.clear()
|
||||
changesLoadingPanel.startLoading()
|
||||
|
||||
dataLoader.getDataProvider(selection).changesRequest
|
||||
.handleOnEdt { changes, error ->
|
||||
when {
|
||||
error != null && !GithubAsyncUtil.isCancellation(error) -> {
|
||||
changesBrowser.emptyText
|
||||
.appendText("Cannot load changes", SimpleTextAttributes.ERROR_ATTRIBUTES)
|
||||
.appendSecondaryText(error.message ?: "Unknown error", SimpleTextAttributes.ERROR_ATTRIBUTES, null)
|
||||
}
|
||||
changes != null -> {
|
||||
changesBrowser.emptyText.text = "Pull request does not contain any changes"
|
||||
changesBrowser.changes = changes
|
||||
}
|
||||
}
|
||||
changesLoadingPanel.stopLoading()
|
||||
}
|
||||
}
|
||||
|
||||
private fun reset() {
|
||||
updateFuture?.cancel(true)
|
||||
override fun reset() {
|
||||
changesBrowser.emptyText.text = DEFAULT_EMPTY_TEXT
|
||||
changesBrowser.changes = emptyList()
|
||||
}
|
||||
|
||||
override fun handleResult(result: List<Change>) {
|
||||
changesBrowser.emptyText.text = "Pull request does not contain any changes"
|
||||
changesBrowser.changes = result
|
||||
}
|
||||
|
||||
override fun handleError(error: Throwable) {
|
||||
changesBrowser.emptyText
|
||||
.clear()
|
||||
.appendText("Cannot load changes", SimpleTextAttributes.ERROR_ATTRIBUTES)
|
||||
.appendSecondaryText(error.message ?: "Unknown error", SimpleTextAttributes.ERROR_ATTRIBUTES, null)
|
||||
}
|
||||
|
||||
override fun setBusy(busy: Boolean) {
|
||||
if (busy) {
|
||||
changesBrowser.emptyText.clear()
|
||||
loadingPanel.startLoading()
|
||||
}
|
||||
else {
|
||||
loadingPanel.stopLoading()
|
||||
}
|
||||
}
|
||||
|
||||
override fun dispose() {}
|
||||
|
||||
companion object {
|
||||
|
||||
+16
-43
@@ -6,62 +6,35 @@ import com.intellij.openapi.progress.util.ProgressWindow
|
||||
import com.intellij.openapi.project.Project
|
||||
import com.intellij.ui.SimpleTextAttributes
|
||||
import com.intellij.ui.components.JBLoadingPanel
|
||||
import com.intellij.ui.components.panels.Wrapper
|
||||
import org.jetbrains.plugins.github.api.data.GithubSearchedIssue
|
||||
import org.jetbrains.plugins.github.pullrequest.data.GithubPullRequestsDataLoader
|
||||
import org.jetbrains.plugins.github.util.GithubAsyncUtil
|
||||
import org.jetbrains.plugins.github.util.handleOnEdt
|
||||
import org.jetbrains.plugins.github.api.data.GithubPullRequestDetailedWithHtml
|
||||
import java.awt.BorderLayout
|
||||
import java.util.concurrent.CompletableFuture
|
||||
|
||||
class GithubPullRequestDetailsComponent(project: Project,
|
||||
private val selectionModel: GithubPullRequestsListSelectionModel,
|
||||
private val dataLoader: GithubPullRequestsDataLoader)
|
||||
: Wrapper(), Disposable, GithubPullRequestsListSelectionModel.SelectionChangedListener {
|
||||
|
||||
class GithubPullRequestDetailsComponent(project: Project) : GithubDataLoadingComponent<GithubPullRequestDetailedWithHtml>(), Disposable {
|
||||
private val detailsPanel = GithubPullRequestDetailsPanel(project)
|
||||
|
||||
private val loadingPanel = JBLoadingPanel(BorderLayout(), this, ProgressWindow.DEFAULT_PROGRESS_DIALOG_POSTPONE_TIME_MILLIS)
|
||||
|
||||
private var updateFuture: CompletableFuture<Unit>? = null
|
||||
|
||||
init {
|
||||
selectionModel.addChangesListener(this, this)
|
||||
|
||||
loadingPanel.add(detailsPanel)
|
||||
setContent(loadingPanel)
|
||||
}
|
||||
|
||||
override fun selectionChanged() {
|
||||
reset()
|
||||
updateFuture = updateDetails(selectionModel.current)
|
||||
}
|
||||
|
||||
private fun updateDetails(item: GithubSearchedIssue?) =
|
||||
item?.let { selection ->
|
||||
loadingPanel.startLoading()
|
||||
|
||||
dataLoader.getDataProvider(selection).detailsRequest
|
||||
.handleOnEdt { details, error ->
|
||||
when {
|
||||
error != null && !GithubAsyncUtil.isCancellation(error) -> {
|
||||
detailsPanel.emptyText
|
||||
.appendText("Cannot load details", SimpleTextAttributes.ERROR_ATTRIBUTES)
|
||||
.appendSecondaryText(error.message ?: "Unknown error", SimpleTextAttributes.ERROR_ATTRIBUTES, null)
|
||||
}
|
||||
details != null -> {
|
||||
detailsPanel.details = details
|
||||
}
|
||||
}
|
||||
loadingPanel.stopLoading()
|
||||
}
|
||||
}
|
||||
|
||||
private fun reset() {
|
||||
updateFuture?.cancel(true)
|
||||
override fun reset() {
|
||||
detailsPanel.emptyText.clear()
|
||||
detailsPanel.details = null
|
||||
}
|
||||
|
||||
override fun handleResult(result: GithubPullRequestDetailedWithHtml) {
|
||||
detailsPanel.details = result
|
||||
}
|
||||
|
||||
override fun handleError(error: Throwable) {
|
||||
detailsPanel.emptyText
|
||||
.clear()
|
||||
.appendText("Cannot load details", SimpleTextAttributes.ERROR_ATTRIBUTES)
|
||||
.appendSecondaryText(error.message ?: "Unknown error", SimpleTextAttributes.ERROR_ATTRIBUTES, null)
|
||||
}
|
||||
|
||||
override fun setBusy(busy: Boolean) = if (busy) loadingPanel.startLoading() else loadingPanel.stopLoading()
|
||||
|
||||
override fun dispose() {}
|
||||
}
|
||||
|
||||
+11
-17
@@ -2,33 +2,27 @@
|
||||
package org.jetbrains.plugins.github.pullrequest.ui
|
||||
|
||||
import com.intellij.openapi.Disposable
|
||||
import com.intellij.openapi.util.Disposer
|
||||
import com.intellij.ui.OnePixelSplitter
|
||||
import org.jetbrains.plugins.github.pullrequest.config.GithubPullRequestsUISettings
|
||||
import org.jetbrains.plugins.github.pullrequest.data.GithubPullRequestsDataLoader
|
||||
|
||||
class GithubPullRequestPreviewComponent(private val uiSettings: GithubPullRequestsUISettings,
|
||||
changes: GithubPullRequestChangesComponent,
|
||||
class GithubPullRequestPreviewComponent(private val changes: GithubPullRequestChangesComponent,
|
||||
private val details: GithubPullRequestDetailsComponent)
|
||||
: OnePixelSplitter(true, "Github.PullRequest.Preview.Component", 0.6f),
|
||||
Disposable, GithubPullRequestsUISettings.SettingsChangedListener {
|
||||
: OnePixelSplitter(true, "Github.PullRequest.Preview.Component", 0.6f), Disposable {
|
||||
|
||||
val toolbarComponent = changes.toolbarComponent
|
||||
var detailsVisible: Boolean
|
||||
get() = secondComponent != null
|
||||
set(value) {
|
||||
secondComponent = if (value) details else null
|
||||
}
|
||||
|
||||
init {
|
||||
Disposer.register(this, changes)
|
||||
Disposer.register(this, details)
|
||||
|
||||
firstComponent = changes
|
||||
uiSettings.addChangesListener(this, this)
|
||||
updateDetails()
|
||||
}
|
||||
|
||||
override fun settingsChanged() {
|
||||
updateDetails()
|
||||
}
|
||||
|
||||
private fun updateDetails() {
|
||||
secondComponent = if (uiSettings.showDetails) details else null
|
||||
fun setPreviewDataProvider(provider: GithubPullRequestsDataLoader.DataProvider?) {
|
||||
changes.loadAndShow(provider?.changesRequest)
|
||||
details.loadAndShow(provider?.detailsRequest)
|
||||
}
|
||||
|
||||
override fun dispose() {}
|
||||
|
||||
+1
-1
@@ -34,11 +34,11 @@ import javax.swing.event.ListSelectionEvent
|
||||
class GithubPullRequestsListComponent internal constructor(project: Project,
|
||||
actionManager: ActionManager,
|
||||
autoPopupController: AutoPopupController,
|
||||
private val selectionModel: GithubPullRequestsListSelectionModel,
|
||||
private val loader: GithubPullRequestsLoader,
|
||||
avatarIconsProviderFactory: CachingGithubAvatarIconsProvider.Factory)
|
||||
: BorderLayoutPanel(), Disposable, DataProvider {
|
||||
|
||||
val selectionModel = GithubPullRequestsListSelectionModel()
|
||||
private val listModel = CollectionListModel<GithubSearchedIssue>()
|
||||
private val list = GithubPullRequestsList(avatarIconsProviderFactory, listModel)
|
||||
private val scrollPane = ScrollPaneFactory.createScrollPane(list,
|
||||
|
||||
Reference in New Issue
Block a user