Custom.kt 7.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259
  1. package eu.mjdev.desktop.extensions
  2. import androidx.compose.runtime.Composable
  3. import androidx.compose.runtime.MutableState
  4. import androidx.compose.runtime.State
  5. import androidx.compose.runtime.collectAsState
  6. import androidx.compose.ui.graphics.NativePaint
  7. import androidx.compose.ui.unit.Dp
  8. import androidx.compose.ui.unit.DpSize
  9. import com.google.gson.Gson
  10. import eu.mjdev.desktop.data.DesktopFile
  11. import eu.mjdev.desktop.extensions.Locale.toLocale
  12. import eu.mjdev.desktop.helpers.streams.ResourceStream
  13. import kotlinx.coroutines.delay
  14. import kotlinx.coroutines.flow.Flow
  15. import kotlinx.coroutines.flow.channelFlow
  16. import kotlinx.coroutines.flow.flow
  17. import kotlinx.coroutines.launch
  18. import java.io.BufferedReader
  19. import java.io.File
  20. import java.text.DateFormat
  21. import java.util.*
  22. import java.util.Locale
  23. import kotlin.jvm.optionals.getOrNull
  24. @Suppress("MemberVisibilityCanBePrivate", "FunctionName")
  25. object Custom {
  26. val ProcessHandle.command: String
  27. get() = info().command().getOrNull().orEmpty()
  28. // val ProcessHandle.commandLine: String
  29. // get() = info().commandLine().getOrNull().orEmpty()
  30. val Process.consoleOutput: String
  31. get() {
  32. val sj = StringJoiner(System.lineSeparator())
  33. val bfr = BufferedReader(inputReader())
  34. bfr.lines().iterator().forEachRemaining { s: String? -> sj.add(s) }
  35. return sj.toString()
  36. }
  37. inline fun <reified T> Any?.orElse(
  38. block: () -> T
  39. ) = if (this == null) block() else this as T
  40. val Char.isPrintable: Boolean
  41. get() {
  42. val block = Character.UnicodeBlock.of(this)
  43. return (!Character.isISOControl(this)) &&
  44. this != java.awt.event.KeyEvent.CHAR_UNDEFINED &&
  45. block != null &&
  46. block != Character.UnicodeBlock.SPECIALS
  47. }
  48. fun File.listFiles(
  49. ext: String? = null
  50. ): List<File> = if (exists()) listFiles().let { list ->
  51. when {
  52. list == null -> emptyList()
  53. ext != null -> list.filter { f -> f.extension == ext }
  54. else -> list.toList()
  55. }
  56. } else emptyList()
  57. fun File.listFilesFiltered(
  58. ext: String? = null,
  59. predicate: (File) -> Boolean
  60. ) = listFiles(ext).filter(predicate)
  61. fun File.listFilesOnly(
  62. ext: String? = null,
  63. ) = listFilesFiltered(ext) { f -> !f.isDirectory }
  64. fun List<File>.sortedByName() =
  65. sortedBy { f -> f.name }
  66. // fun List<File>.sortedByNameDescending() =
  67. // sortedByDescending { f -> f.name }
  68. inline fun <R> File.listFiles(
  69. ext: String? = null,
  70. mapper: (File) -> R
  71. ): List<R> = listFiles(ext).map(mapper)
  72. // inline fun <R : Comparable<R>> File.listFilesSortedBy(
  73. // ext: String? = null,
  74. // crossinline selector: (File) -> R?
  75. // ): List<File> = listFiles(ext).sortedBy(selector)
  76. fun File.listDesktopFiles(
  77. ext: String = DesktopFile.EXTENSION
  78. ): List<DesktopFile> = listFiles(ext) { f -> DesktopFile(f) }
  79. // inline fun <reified T : Any> MutableList<T>.addIfNotExists(
  80. // element: T,
  81. // equal: (e1: T, e2: T) -> Boolean = { e1, e2 -> e1 == e2 }
  82. // ) = any { e -> equal(e, element) }.also { contains -> if (!contains) add(element) }
  83. operator fun File.get(name: String) =
  84. resolve(name)
  85. fun File.readTextAsLocale(): Locale =
  86. if (exists()) text.toLocale() else Locale.ENGLISH
  87. val File.all
  88. get() = listFiles()?.toList() ?: emptyList<File>()
  89. val File.filesOnly
  90. get() = listFiles()?.filter { !it.isDirectory }?.toList() ?: emptyList<File>()
  91. val File.dirsOnly
  92. get() = listFiles()?.filter { it.isDirectory }?.toList() ?: emptyList<File>()
  93. val File.lines
  94. get() = runCatching { if (exists()) readLines() else null }.getOrNull() ?: emptyList()
  95. val File.text
  96. get() = runCatching { if (exists()) readText() else null }.getOrNull() ?: ""
  97. val File.textAsLocale: Locale
  98. get() = if (exists()) readTextAsLocale() else Locale.ENGLISH
  99. val File.desktopFiles
  100. get() = listDesktopFiles()
  101. // inline fun <T, K> distinctList(
  102. // vararg lists: List<T>,
  103. // selector: (T) -> K
  104. // ): List<T> = mutableListOf<T>().apply {
  105. // lists.forEach { l -> addAll(l) }
  106. // }.distinctBy(selector)
  107. fun String.notStartsWith(
  108. prefix: String,
  109. ignoreCase: Boolean = false
  110. ) = !startsWith(prefix, ignoreCase)
  111. // fun String.trimIsEmpty() =
  112. // trim().isEmpty()
  113. // fun String.trimIsNotEmpty() =
  114. // trim().isNotEmpty()
  115. // fun State<String>.trimIsEmpty() =
  116. // value.trim().isEmpty()
  117. fun State<String>.trimIsNotEmpty() =
  118. value.trim().isNotEmpty()
  119. // fun String.trimStartsWith(
  120. // prefix: String,
  121. // ignoreCase: Boolean = false
  122. // ) = trim().startsWith(prefix, ignoreCase)
  123. // fun String.trimNotStartsWith(
  124. // prefix: String,
  125. // ignoreCase: Boolean = false
  126. // ) = !trim().startsWith(prefix, ignoreCase)
  127. fun ParsedList(
  128. value: String?,
  129. delimiter: String = ";"
  130. ): MutableList<String> = value?.split(delimiter)?.toMutableList() ?: mutableListOf()
  131. fun ParsedBoolean(
  132. value: String?,
  133. defaultValue: Boolean = false
  134. ) = value.orEmpty().trim().lowercase().let { b ->
  135. when (b) {
  136. "true" -> true
  137. "1" -> true
  138. else -> defaultValue
  139. }
  140. }
  141. fun ParsedString(
  142. value: Any?,
  143. defaultValue: String = ""
  144. ) = value?.toString() ?: defaultValue
  145. fun <T> flowBlock(
  146. block: suspend () -> T
  147. ): Flow<T> = flow {
  148. emit(block())
  149. }
  150. fun NativePaint.setMaskFilter(
  151. blurRadius: Float
  152. ) {
  153. this.maskFilter =
  154. org.jetbrains.skia.MaskFilter.makeBlur(org.jetbrains.skia.FilterBlurMode.NORMAL, blurRadius / 2, true)
  155. }
  156. val dateFlow
  157. @Composable
  158. get() = channelFlow {
  159. launch {
  160. var date = "1.1.1970"
  161. do {
  162. DateFormat.getDateInstance().format(Date()).also { t ->
  163. if (date != t) {
  164. date = t
  165. send(date)
  166. }
  167. }
  168. delay(5000L)
  169. } while (true)
  170. }
  171. }.collectAsState(initial = "")
  172. val timeFlow
  173. @Composable
  174. get() = channelFlow {
  175. launch {
  176. var time = "00:00:00"
  177. do {
  178. DateFormat.getTimeInstance().format(Date()).also { t ->
  179. if (time != t) {
  180. time = t
  181. send(time)
  182. }
  183. }
  184. delay(200L)
  185. } while (true)
  186. }
  187. }.collectAsState(initial = "")
  188. fun loadKey(key: String): String = runCatching {
  189. ResourceStream("keys/$key.key").string
  190. }.getOrNull().orEmpty()
  191. inline fun <reified T> String.jsonToList(): List<T> = runCatching {
  192. Gson().fromJson(this, List::class.java).mapNotNull { item -> item as? T }
  193. }.getOrNull() ?: emptyList()
  194. inline fun <reified T> String.to(): T? = runCatching {
  195. Gson().fromJson(this, T::class.java)
  196. }.getOrNull()
  197. val Process.command: String?
  198. get() = info().command().getOrNull()
  199. operator fun DpSize.plus(dp: Dp) =
  200. copy(width = width + dp, height = height + dp)
  201. operator fun DpSize.minus(dp: Dp) =
  202. copy(width = width - dp, height = height - dp)
  203. // operator fun PaddingValues.plus(dp: Dp) =
  204. // copy(left = width + dp, height = height + dp)
  205. // operator fun PaddingValues.minus(dp: Dp) =
  206. // copy(width = width - dp, height = height - dp)
  207. fun MutableState<Boolean>.toggle() {
  208. value = !value
  209. }
  210. }