Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Refactor] Simplify Splitter Related Code #21

Merged
merged 1 commit into from
May 2, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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() }
}
}