Skip to content

Commit

Permalink
Merge pull request #15 from marad/file_config
Browse files Browse the repository at this point in the history
Make loading YAML config a default.
  • Loading branch information
marad authored Nov 19, 2023
2 parents bd46668 + 0c2b206 commit 765a48b
Show file tree
Hide file tree
Showing 11 changed files with 173 additions and 27 deletions.
4 changes: 3 additions & 1 deletion doc/releasing.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
This project uses [axion-release-plugin](https://github.com/allegro/axion-release-plugin) to create releases. It automatically creates patch semantic versions on `./gradlew release`. To bump the major or minor version you need to create new GIT tag which denotes newer version:
This project uses [axion-release-plugin](https://github.com/allegro/axion-release-plugin) to create releases. It
automatically creates patch semantic versions on `./gradlew release`. To bump the major or minor version you need to
create new GIT tag which denotes newer version:

```bash
./gradlew tag v0.1.0
Expand Down
70 changes: 70 additions & 0 deletions example-config.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
editor: "C:\\Program Files\\Neovim\\bin\\nvim-qt.exe"

layout:
gap: 0
ratio: 0.55

rules:
- exeName: "nvim-qt.exe"
should: manage

hotkeys:
# Switch windows
- key: S-C-A-Y
action: SwitchView
value: 0
- key: S-C-A-U
action: SwitchView
value: 1
- key: S-C-A-I
action: SwitchView
value: 2
- key: S-C-A-O
action: SwitchView
value: 3
- key: S-C-A-P
action: SwitchView
value: 4

# Move window to view
- key: S-A-Y
action: MoveActiveWindowToView
value: 0
- key: S-A-U
action: MoveActiveWindowToView
value: 1
- key: S-A-I
action: MoveActiveWindowToView
value: 3
- key: S-A-O
action: MoveActiveWindowToView
value: 4
- key: S-A-P
action: MoveActiveWindowToView
value: 5

# Switch to previous window
- key: S-C-A-F
action: SwitchToPreviousView

# Window navigation
- key: S-C-A-H
action: MoveWindowLeft
- key: S-C-A-J
action: MoveWindowDown
- key: S-C-A-K
action: MoveWindowUp
- key: S-C-A-L
action: MoveWindowRight

# Layout ratio change
- key: S-A-L
action: LayoutIncrease
value: 0.05
- key: S-A-H
action: LayoutDecrease
value: 0.05

# Reload config
- key: S-C-A-R
action: ReloadConfig
3 changes: 2 additions & 1 deletion src/main/kotlin/gh/marad/tiler/Main.kt
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package gh.marad.tiler
import gh.marad.tiler.app.AppFacade
import gh.marad.tiler.config.ConfigException
import gh.marad.tiler.config.ConfigFacade
import gh.marad.tiler.os.OsFacade
import org.docopt.Docopt
import org.slf4j.LoggerFactory

Expand Down Expand Up @@ -42,6 +43,6 @@ fun getConfig(data: Map<String, Any>): ConfigFacade {
ConfigFacade.loadYamlConfig(configPath)
} else {
logger.info("Loading default configuration")
ConfigFacade.createConfig()
ConfigFacade.createConfig(OsFacade.createWindowsFacade())
}
}
19 changes: 16 additions & 3 deletions src/main/kotlin/gh/marad/tiler/app/internal/App.kt
Original file line number Diff line number Diff line change
@@ -1,13 +1,15 @@
package gh.marad.tiler.app.internal

import gh.marad.tiler.actions.ActionsFacade
import gh.marad.tiler.actions.ReloadConfig
import gh.marad.tiler.app.AppFacade
import gh.marad.tiler.common.BroadcastingEventHandler
import gh.marad.tiler.config.ConfigFacade
import gh.marad.tiler.config.Hotkey
import gh.marad.tiler.os.OsFacade
import gh.marad.tiler.tiler.TilerFacade
import org.slf4j.LoggerFactory
import java.awt.MenuItem
import java.awt.SystemTray
import java.awt.Toolkit
import java.awt.TrayIcon
Expand All @@ -17,7 +19,7 @@ class App(val config: ConfigFacade, val os: OsFacade, val tiler: TilerFacade, va

@Suppress("UNUSED_VARIABLE")
override fun start() {
val trayIcon = createTrayIcon(os, tiler)
val trayIcon = createTrayIcon(os, tiler, actions)
setupHotkeys(config.getHotkeys())
actions.registerActionListener(ActionHandler(this, os, tiler))
val executor = TilerCommandsExecutorAndWatcher(os, config.getFilteringRules())
Expand Down Expand Up @@ -48,7 +50,7 @@ class App(val config: ConfigFacade, val os: OsFacade, val tiler: TilerFacade, va
}
}

private fun createTrayIcon(os: OsFacade, tiler: TilerFacade): TrayIcon {
private fun createTrayIcon(os: OsFacade, tiler: TilerFacade, actions: ActionsFacade): TrayIcon {
val icon = Toolkit.getDefaultToolkit().getImage(AppFacade::class.java.getResource("/icon.png"))
val stopped = Toolkit.getDefaultToolkit().getImage(AppFacade::class.java.getResource("/stopped_icon.png"))
val trayIcon = TrayIcon(icon, "Tiler")
Expand All @@ -59,6 +61,7 @@ class App(val config: ConfigFacade, val os: OsFacade, val tiler: TilerFacade, va

trayIcon.addMouseListener(object : java.awt.event.MouseAdapter() {
override fun mouseClicked(e: java.awt.event.MouseEvent?) {
// TODO: This should be extracted to actions
if (e?.button == java.awt.event.MouseEvent.BUTTON1) {
if (trayIcon.image == stopped) {
trayIcon.image = icon
Expand All @@ -73,7 +76,17 @@ class App(val config: ConfigFacade, val os: OsFacade, val tiler: TilerFacade, va
})

val popupMenu = java.awt.PopupMenu()
popupMenu.add(java.awt.MenuItem("Exit")).addActionListener {
if (config.getConfigPath() != null) {
popupMenu.add(MenuItem("Edit config")).addActionListener {
val editor = ProcessBuilder(config.configEditorPath(), config.getConfigPath())
editor.start()
}
popupMenu.add(MenuItem("Reload config")).addActionListener {
actions.invokeAction(ReloadConfig)
}
popupMenu.addSeparator()
}
popupMenu.add(MenuItem("Exit")).addActionListener {
System.exit(0)
}
trayIcon.popupMenu = popupMenu
Expand Down
16 changes: 14 additions & 2 deletions src/main/kotlin/gh/marad/tiler/config/ConfigFacade.kt
Original file line number Diff line number Diff line change
Expand Up @@ -3,18 +3,30 @@ package gh.marad.tiler.config
import gh.marad.tiler.common.assignments.WindowAssignments
import gh.marad.tiler.common.filteringrules.FilteringRules
import gh.marad.tiler.common.layout.Layout
import gh.marad.tiler.config.internal.SimpleConfig
import gh.marad.tiler.config.internal.YamlConfig
import gh.marad.tiler.os.OsFacade
import java.nio.file.Paths
import kotlin.io.path.exists

interface ConfigFacade {
fun reload()
fun createLayout(): Layout
fun getHotkeys(): List<Hotkey>
fun getFilteringRules(): FilteringRules
fun getAssignments(): WindowAssignments
fun getConfigPath(): String?
fun configEditorPath(): String

companion object {
fun createConfig(): ConfigFacade = SimpleConfig()
fun createConfig(osFacade: OsFacade): ConfigFacade {
val defaultConfigPath = Paths.get(osFacade.userHome(), ".config", "tiler", "config.yaml")
if (!defaultConfigPath.exists()) {
defaultConfigPath.parent.toFile().mkdirs()
defaultConfigPath.toFile().createNewFile()
}
return loadYamlConfig(defaultConfigPath.toString())
}

fun loadYamlConfig(path: String): ConfigFacade =
YamlConfig(path)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -73,4 +73,9 @@ class SimpleConfig : ConfigFacade {
override fun getFilteringRules(): FilteringRules = filteringRules

override fun getAssignments(): WindowAssignments = assignments

override fun getConfigPath(): String? = null
override fun configEditorPath(): String {
TODO("Not yet implemented")
}
}
64 changes: 48 additions & 16 deletions src/main/kotlin/gh/marad/tiler/config/internal/YamlConfig.kt
Original file line number Diff line number Diff line change
Expand Up @@ -25,12 +25,12 @@ class YamlConfig(configPath: String) : ConfigFacade {
private var layoutCreator: () -> Layout = { TwoColumnLayout(0.55f) }
private val filteringRules: FilteringRules = FilteringRules()
private val hotkeys = mutableListOf<Hotkey>()
private var configEditorPath: String = "notepad.exe"

init {
loadConfig()
}

@Suppress("UNCHECKED_CAST")
fun loadConfig() {
logger.info("Loading config from $configPath...")
if (Files.notExists(configPath)) {
Expand All @@ -39,9 +39,12 @@ class YamlConfig(configPath: String) : ConfigFacade {
val fileStream = Files.newInputStream(configPath, StandardOpenOption.READ)
val yaml = Yaml()
val data = yaml.load<Map<String, Any>>(fileStream)
readLayout(data["layout"] as Map<String, Any>)
readFilteringRules(data["rules"] as List<Map<String, Any>>)
readHotkeys(data["hotkeys"] as List<Map<String, Any>>)
if (data != null) {
configEditorPath = data["editor"]?.toString() ?: configEditorPath
readLayout(data["layout"])
readFilteringRules(data["rules"])
readHotkeys(data["hotkeys"])
}
}

override fun reload() {
Expand All @@ -61,14 +64,28 @@ class YamlConfig(configPath: String) : ConfigFacade {
}

override fun getAssignments(): WindowAssignments {
TODO("Not yet implemented")
return WindowAssignments()
}

override fun getConfigPath(): String {
return configPath.toAbsolutePath().toString()
}

override fun configEditorPath(): String {
return configEditorPath
}

@Suppress("UNCHECKED_CAST")
private fun readLayout(data: Map<String, Any>) {
val name = data["name"].toString()
val gap = data["gap"].toString().toInt()
val ratio = data["ratio"].toString().toFloat()
private fun readLayout(section: Any?) {
val data = if (section != null) {
section as Map<String, Any>
} else {
return
}

val name = data["name"]?.toString() ?: "TwoColumnLayout"
val gap = data["gap"]?.toString()?.toInt() ?: 0
val ratio = data["ratio"]?.toString()?.toFloat() ?: 0.55f
val minSize = data["minSize"] as Map<String, Any>?

layoutCreator = when (name) {
Expand All @@ -78,11 +95,13 @@ class YamlConfig(configPath: String) : ConfigFacade {

if (minSize != null) {
val wrappedLayout = layoutCreator
layoutCreator = { MinWindowSizeLayoutDecorator(
minimumWidth = minSize["width"]?.toString()?.toInt() ?: 1,
minimumHeight = minSize["height"]?.toString()?.toInt() ?: 1,
wrappedLayout = wrappedLayout()
) }
layoutCreator = {
MinWindowSizeLayoutDecorator(
minimumWidth = minSize["width"]?.toString()?.toInt() ?: 1,
minimumHeight = minSize["height"]?.toString()?.toInt() ?: 1,
wrappedLayout = wrappedLayout()
)
}
}

if (gap != 0) {
Expand All @@ -91,7 +110,14 @@ class YamlConfig(configPath: String) : ConfigFacade {
}
}

private fun readFilteringRules(rules: List<Map<String, Any>>) {
private fun readFilteringRules(section: Any?) {
val rules = if (section != null) {
@Suppress("UNCHECKED_CAST")
section as List<Map<String, Any>>
} else {
return
}

filteringRules.clear()
rules.forEach { ruleSpec ->
val title = ruleSpec["title"]?.toString()
Expand All @@ -115,7 +141,13 @@ class YamlConfig(configPath: String) : ConfigFacade {

}

private fun readHotkeys(hotkeysConfig: List<Map<String, Any>>) {
private fun readHotkeys(section: Any?) {
val hotkeysConfig = if (section != null) {
@Suppress("UNCHECKED_CAST")
section as List<Map<String, Any>>
} else {
return
}
hotkeys.clear()
hotkeysConfig.forEach { map ->
val key = map["key"].toString()
Expand Down
1 change: 1 addition & 0 deletions src/main/kotlin/gh/marad/tiler/os/OsFacade.kt
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ interface OsFacade {
fun execute(commands: List<TilerCommand>)
fun startEventHandling(handler: WindowEventHandler)
fun isWindowAtPosition(windowId: WindowId, position: WindowPosition): Boolean
fun userHome(): String
fun windowDebugInfo(window: Window): String

companion object {
Expand Down
10 changes: 8 additions & 2 deletions src/main/kotlin/gh/marad/tiler/os/internal/WindowsOs.kt
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package gh.marad.tiler.os.internal

import com.sun.jna.platform.win32.User32
import com.sun.jna.platform.win32.WinDef
import com.sun.jna.platform.win32.WinDef.RECT
import com.sun.jna.platform.win32.WinUser.WINDOWPLACEMENT
import gh.marad.tiler.common.*
Expand All @@ -11,7 +12,9 @@ import gh.marad.tiler.os.internal.winapi.*
import gh.marad.tiler.os.internal.winapi.Window as OsWindow

class WindowsOs : OsFacade {
private val hotkeys = Hotkeys()
companion object {
private val hotkeys = Hotkeys()
}
// private val myU32 = Native.load("user32", MyUser32::class.java, W32APIOptions.DEFAULT_OPTIONS)

override fun getDesktopState(): DesktopState {
Expand Down Expand Up @@ -76,7 +79,7 @@ class WindowsOs : OsFacade {
is ShowWindow -> {
val hwnd = (command.windowId as WID).handle
User32.INSTANCE.ShowWindow(hwnd, User32.SW_SHOWNOACTIVATE)
// User32.INSTANCE.RedrawWindow(hwnd, null, null, WinDef.DWORD(User32.RDW_INVALIDATE.toLong()))
User32.INSTANCE.RedrawWindow(hwnd, null, null, WinDef.DWORD(User32.RDW_INVALIDATE.toLong()))
}

is ActivateWindow -> {
Expand Down Expand Up @@ -124,6 +127,9 @@ class WindowsOs : OsFacade {
targetPosition.top == actualPosition.top
}

override fun userHome(): String =
System.getenv("userprofile")

override fun windowDebugInfo(window: Window): String {
val wid = window.id as WID
val osWindow = OsWindow(wid.handle)
Expand Down
2 changes: 1 addition & 1 deletion src/main/kotlin/gh/marad/tiler/tiler/internal/Tiler.kt
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ class Tiler(
override fun initializeWithOpenWindows(): List<TilerCommand> {
if (!enabled) return emptyList()
viewManager.changeCurrentView(0)
os.getDesktopState().getManagableWindows(filteringRules).forEach {
os.getDesktopState().getManagableWindows(filteringRules).reversed().forEach {
if (!it.isPopup && !it.isMinimized && it.isVisible) {
viewManager.currentView().addWindow(it.id)
}
Expand Down
6 changes: 5 additions & 1 deletion src/test/kotlin/gr/marad/tiler/GraphicalTest.kt
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,10 @@ val os = object : OsFacade {
TODO("Not yet implemented")
}

override fun userHome(): String {
TODO("Not yet implemented")
}

override fun execute(command: TilerCommand) {
TODO("Not yet implemented")
}
Expand Down Expand Up @@ -99,7 +103,7 @@ val os = object : OsFacade {
}

}
val config = ConfigFacade.createConfig()
val config = ConfigFacade.createConfig(os)
val tiler = TilerFacade.createTiler(config, os)
val executor = TilerCommandsExecutorAndWatcher(os, filteringRules)
val eventHandler = TilerWindowEventHandler(tiler, filteringRules, os, executor)
Expand Down

0 comments on commit 765a48b

Please sign in to comment.