Skip to content

Commit

Permalink
[Refactor] Simplify Splitter Related Code (#21)
Browse files Browse the repository at this point in the history
* Don't use a mutableStateOf for the RSyntaxTextArea.
* Extract code creating the various RSyntaxTextArea components to methods
* Extract the code for creating the text areas Composables to methods
* Extract a 3-way splitter method
* Add a DocumentListener that takes a single lambda
  • Loading branch information
alonalbert authored May 2, 2024
1 parent 48adafb commit 7491c9f
Show file tree
Hide file tree
Showing 3 changed files with 130 additions and 103 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
/*
* Copyright (C) 2023 Romain Guy
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package dev.romainguy.kotlin.explorer

import javax.swing.event.DocumentEvent
import javax.swing.event.DocumentListener

/**
* A [DocumentListener] that takes a single lambda that's invoked on any change
*/
class DocumentChangeListener(private val block: (DocumentEvent) -> Unit) : DocumentListener {
override fun insertUpdate(event: DocumentEvent) {
block(event)
}

override fun removeUpdate(event: DocumentEvent) {
block(event)
}

override fun changedUpdate(event: DocumentEvent) {
block(event)
}
}
166 changes: 63 additions & 103 deletions src/jvmMain/kotlin/dev/romainguy/kotlin/explorer/KotlinExplorer.kt
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,6 @@
* limitations under the License.
*/

@file:OptIn(ExperimentalSplitPaneApi::class)
@file:Suppress("FunctionName")

package dev.romainguy.kotlin.explorer
Expand All @@ -39,9 +38,6 @@ import org.fife.ui.rsyntaxtextarea.SyntaxConstants
import org.fife.ui.rsyntaxtextarea.Theme
import org.fife.ui.rtextarea.RTextScrollPane
import org.fife.ui.rtextarea.SearchEngine
import org.jetbrains.compose.splitpane.ExperimentalSplitPaneApi
import org.jetbrains.compose.splitpane.HorizontalSplitPane
import org.jetbrains.compose.splitpane.rememberSplitPaneState
import org.jetbrains.jewel.foundation.theme.JewelTheme
import org.jetbrains.jewel.intui.standalone.theme.IntUiTheme
import org.jetbrains.jewel.intui.standalone.theme.darkThemeDefinition
Expand All @@ -62,8 +58,6 @@ import java.awt.event.FocusEvent
import java.awt.event.FocusListener
import java.io.IOException
import javax.swing.SwingUtilities
import javax.swing.event.DocumentEvent
import javax.swing.event.DocumentListener

private const val FontSizeEditingMode = 12.0f
private const val FontSizePresentationMode = 20.0f
Expand All @@ -73,9 +67,6 @@ private fun FrameWindowScope.KotlinExplorer(
explorerState: ExplorerState
) {
// TODO: Move all those remembers to an internal private state object
var sourceTextArea by remember { mutableStateOf<RSyntaxTextArea?>(null) }
var dexTextArea by remember { mutableStateOf<DexTextArea?>(null) }
var oatTextArea by remember { mutableStateOf<RSyntaxTextArea?>(null) }
var activeTextArea by remember { mutableStateOf<RSyntaxTextArea?>(null) }
var status by remember { mutableStateOf("Ready") }

Expand Down Expand Up @@ -115,6 +106,11 @@ private fun FrameWindowScope.KotlinExplorer(
override fun focusLost(e: FocusEvent?) {
}
}}

val sourceTextArea = remember { sourceTextArea(focusTracker, explorerState) }
val dexTextArea = remember { dexTextArea(explorerState, focusTracker) }
val oatTextArea = remember { oatTextArea(focusTracker) }

val findDialog = remember { FindDialog(window, searchListener).apply { searchContext.searchWrap = true } }
var showSettings by remember { mutableStateOf(!explorerState.toolPaths.isValid) }

Expand All @@ -123,13 +119,13 @@ private fun FrameWindowScope.KotlinExplorer(
sourceTextArea,
{ dex ->
if (dex != null) {
updateTextArea(dexTextArea!!, dex)
updateTextArea(dexTextArea, dex)
} else {
dexTextArea?.refreshText()
dexTextArea.refreshText()
}

},
{ oat -> updateTextArea(oatTextArea!!, oat) },
{ oat -> updateTextArea(oatTextArea, oat) },
{ statusUpdate -> status = statusUpdate },
{ findDialog.isVisible = true },
{ SearchEngine.find(activeTextArea, findDialog.searchContext) },
Expand All @@ -145,98 +141,12 @@ private fun FrameWindowScope.KotlinExplorer(
Column(
modifier = Modifier.background(JewelTheme.globalColors.paneBackground)
) {
HorizontalSplitPane(
ThreeWaySplitter(
modifier = Modifier.weight(1.0f),
splitPaneState = rememberSplitPaneState(initialPositionPercentage = 0.3f)
) {
first {
SwingPanel(
modifier = Modifier.fillMaxSize(),
factory = {
sourceTextArea = RSyntaxTextArea().apply {
configureSyntaxTextArea(SyntaxConstants.SYNTAX_STYLE_KOTLIN)
addFocusListener(focusTracker)
SwingUtilities.invokeLater { requestFocusInWindow() }
document.addDocumentListener(object : DocumentListener {
override fun insertUpdate(e: DocumentEvent?) {
explorerState.sourceCode = text
}

override fun removeUpdate(e: DocumentEvent?) {
explorerState.sourceCode = text
}

override fun changedUpdate(e: DocumentEvent?) {
explorerState.sourceCode = text
}
})
}
RTextScrollPane(sourceTextArea)
},
update = {
sourceTextArea?.text = explorerState.sourceCode
sourceTextArea?.font = sourceTextArea?.font?.deriveFont(
if (explorerState.presentationMode) FontSizePresentationMode else FontSizeEditingMode
)
}
)
}
second {
HorizontalSplitPane(
modifier = Modifier.weight(1.0f),
splitPaneState = rememberSplitPaneState(initialPositionPercentage = 0.5f)
) {
first {
SwingPanel(
modifier = Modifier.fillMaxSize(),
factory = {
dexTextArea = DexTextArea(explorerState).apply {
configureSyntaxTextArea(SyntaxConstants.SYNTAX_STYLE_NONE)
addFocusListener(focusTracker)
}
RTextScrollPane(dexTextArea)
},
update = {
dexTextArea?.font = dexTextArea?.font?.deriveFont(
if (explorerState.presentationMode) {
FontSizePresentationMode
} else {
FontSizeEditingMode
}
)
}
)
}
second {
SwingPanel(
modifier = Modifier.fillMaxSize(),
factory = {
oatTextArea = RSyntaxTextArea().apply {
configureSyntaxTextArea(SyntaxConstants.SYNTAX_STYLE_NONE)
addFocusListener(focusTracker)
}
RTextScrollPane(oatTextArea)
},
update = {
oatTextArea?.font = oatTextArea?.font?.deriveFont(
if (explorerState.presentationMode) {
FontSizePresentationMode
} else {
FontSizeEditingMode
}
)
}
)
}
splitter {
HorizontalSplitter()
}
}
}
splitter {
HorizontalSplitter()
}
}
{ SourcePanel(sourceTextArea, explorerState) },
{ TextPanel(dexTextArea, explorerState) },
{ TextPanel(oatTextArea, explorerState) },
)
Row {
Text(
modifier = Modifier
Expand All @@ -250,6 +160,51 @@ private fun FrameWindowScope.KotlinExplorer(
}
}

@Composable
private fun SourcePanel(sourceTextArea: RSyntaxTextArea, explorerState: ExplorerState) {
SwingPanel(
modifier = Modifier.fillMaxSize(),
factory = {
RTextScrollPane(sourceTextArea)
},
update = {
sourceTextArea.text = explorerState.sourceCode
sourceTextArea.setFont(explorerState)
}
)
}

@Composable
private fun TextPanel(textArea: RSyntaxTextArea, explorerState: ExplorerState) {
SwingPanel(
modifier = Modifier.fillMaxSize(),
factory = { RTextScrollPane(textArea) },
update = { textArea.setFont(explorerState) })
}

private fun sourceTextArea(focusTracker: FocusListener, explorerState: ExplorerState): RSyntaxTextArea {
return RSyntaxTextArea().apply {
configureSyntaxTextArea(SyntaxConstants.SYNTAX_STYLE_KOTLIN)
addFocusListener(focusTracker)
SwingUtilities.invokeLater { requestFocusInWindow() }
document.addDocumentListener(DocumentChangeListener { explorerState.sourceCode = text })
}
}

private fun dexTextArea(explorerState: ExplorerState, focusTracker: FocusListener): DexTextArea {
return DexTextArea(explorerState).apply {
configureSyntaxTextArea(SyntaxConstants.SYNTAX_STYLE_NONE)
addFocusListener(focusTracker)
}
}

private fun oatTextArea(focusTracker: FocusListener): RSyntaxTextArea {
return RSyntaxTextArea().apply {
configureSyntaxTextArea(SyntaxConstants.SYNTAX_STYLE_NONE)
addFocusListener(focusTracker)
}
}

@Composable
private fun FrameWindowScope.MainMenu(
explorerState: ExplorerState,
Expand Down Expand Up @@ -459,6 +414,11 @@ private fun Settings(
}
}

private fun RSyntaxTextArea.setFont(explorerState: ExplorerState) {
val presentation = explorerState.presentationMode
font = font.deriveFont(if (presentation) FontSizePresentationMode else FontSizeEditingMode)
}

fun main() = application {
val explorerState = remember { ExplorerState() }

Expand Down
30 changes: 30 additions & 0 deletions src/jvmMain/kotlin/dev/romainguy/kotlin/explorer/Splitter.kt
Original file line number Diff line number Diff line change
Expand Up @@ -14,17 +14,21 @@
* limitations under the License.
*/

@file:Suppress("FunctionName", "OPT_IN_USAGE", "KotlinRedundantDiagnosticSuppress")
@file:OptIn(ExperimentalSplitPaneApi::class)

package dev.romainguy.kotlin.explorer

import androidx.compose.foundation.layout.*
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.input.pointer.PointerIcon
import androidx.compose.ui.input.pointer.pointerHoverIcon
import androidx.compose.ui.unit.dp
import org.jetbrains.compose.splitpane.ExperimentalSplitPaneApi
import org.jetbrains.compose.splitpane.HorizontalSplitPane
import org.jetbrains.compose.splitpane.SplitterScope
import org.jetbrains.compose.splitpane.rememberSplitPaneState
import java.awt.Cursor

private fun Modifier.cursorForHorizontalResize(): Modifier =
Expand All @@ -48,3 +52,29 @@ fun SplitterScope.HorizontalSplitter() {
)
}
}

@Composable
fun ThreeWaySplitter(
modifier: Modifier = Modifier,
panel1: @Composable () -> Unit,
panel2: @Composable () -> Unit,
panel3: @Composable () -> Unit,
) {
HorizontalSplitPane(
modifier = modifier,
splitPaneState = rememberSplitPaneState(initialPositionPercentage = 1.0f / 3)
) {
first { panel1() }
second {
HorizontalSplitPane(
modifier = modifier,
splitPaneState = rememberSplitPaneState(initialPositionPercentage = 1.0f / 2)
) {
first { panel2() }
second { panel3() }
splitter { HorizontalSplitter() }
}
}
splitter { HorizontalSplitter() }
}
}

0 comments on commit 7491c9f

Please sign in to comment.