Jetpack Compose Desktop switch to new window - kotlin

Hey I'm pretty new to Kotlin and am trying my hand at a GUI as my first small project.
For this I am using Jetpack Compose Desktop. I have already written a first small login window ( not the one in the GIF), and would like to open a new window with the "actual" content after logging in (not an external one, but in the same window).
Here is a video that may help you to understand what I mean:
(Not mine but thanks to Agon Mustafa - uplabs)
So that one continues with the registration in the same window and does not have to open a separate window for it. Hope you can help me:)

Here is an example of how to open and close multiple windows:
fun main() = application {
val applicationState = remember { MyApplicationState() }
for (window in applicationState.windows) {
key(window) {
MyWindow(window)
}
}
}
#Composable
private fun ApplicationScope.MyWindow(
state: MyWindowState
) = Window(onCloseRequest = state::close, title = state.title) {
MenuBar {
Menu("File") {
Item("New window", onClick = state.openNewWindow)
Item("Exit", onClick = state.exit)
}
}
}
private class MyApplicationState {
val windows = mutableStateListOf<MyWindowState>()
init {
windows += MyWindowState("Initial window")
}
fun openNewWindow() {
windows += MyWindowState("Window ${windows.size}")
}
fun exit() {
windows.clear()
}
private fun MyWindowState(
title: String
) = MyWindowState(
title,
openNewWindow = ::openNewWindow,
exit = ::exit,
windows::remove
)
}
private class MyWindowState(
val title: String,
val openNewWindow: () -> Unit,
val exit: () -> Unit,
private val close: (MyWindowState) -> Unit
) {
fun close() = close(this)
}
You can read more in the
tutorials
of Compose for desktop

Related

JetBrains Platform SDK textField property not updating

I'm getting into JetBrains plugin development and currently working on a tool window with Kotlin UI DSL. The UI renders fine but typing on the text field is not updating the property it is bound to, in this case regex. Here's my code.
class RegexTesterToolWindowFactory : ToolWindowFactory {
override fun createToolWindowContent(project: Project, toolWindow: ToolWindow) {
val regexTesterToolWindow = RegexTesterToolWindow(toolWindow)
val contentFactory = ContentFactory.SERVICE.getInstance()
val content = contentFactory.createContent(regexTesterToolWindow.content, "", false)
toolWindow.contentManager.addContent(content)
}
}
private val LOG = logger<RegexTesterToolWindow>()
class RegexTesterToolWindow(toolWindow: ToolWindow) {
private var regex: String = ".*"
private var testText: String = ""
var content: JPanel
init {
content = panel {
row("Regex:") {
textField(::regex)
}
row {
button("Run") {
LOG.info(regex)
LOG.info(testText)
}
}
}
}
}
When I hit the Run button, regex is always the default value .* even after I manually type in the text field.

How can I create an application that runs on my desktop toolbar? [duplicate]

i am new to the jetpack compose. I did a lot of research on that topic but i can't find anything useful. What i wanna achieve is that if I close my window, my application will stay in the background and can be opened again from the tray. I managed to create the tray, but when i close the application window it closes the whole application. How can i achieve that?
This application will be on Windows and MacOS only. I don't care about android right now
Edit: For version 0.4.0
I managed to figure it out. The main function should be an application, not the window
#OptIn(ExperimentalComposeUiApi::class)
fun main() = application {
}
And if the application contains both the Window and the Tray, it will continue to run in the background and does not close after the window is closed.
#OptIn(ExperimentalComposeUiApi::class)
fun main() = application {
Tray(
icon = BufferedImage(24, 24, 1),
menu = {
Item(
"Exit",
onClick = { exitProcess(1) }
)
}
)
Window{
Text("Hello World")
}
}
Edit: For version 1.0.0-beta5
Now you have to specify the onCloseRequest on the window object, if you leave it blank it won't close the window.
In the application, create a variable which will indicate whether the window is open or not.
Create the tray as before. The tray icon now requires a Painter object instead of a BufferedImage.
Than simply check if the window open state is true, show the window, otherwise do nothing.
#OptIn(ExperimentalComposeUiApi::class)
fun main() = application {
val isOpen = remember { mutableStateOf(true)}
Tray(
icon = TrayIcon,
menu = {
Item(
"Exit",
onClick = { exitApplication() }
)
}
)
if(isOpen.value){
Window(
onCloseRequest = { isOpen.value = false }
) {
MaterialTheme {
Text("Hello World")
}
}
}
}
object TrayIcon : Painter() {
override val intrinsicSize = Size(256f, 256f)
override fun DrawScope.onDraw() {
drawOval(Color(0xFFFFA500))
}
}

Jetpack compose for desktop: run application in background?

i am new to the jetpack compose. I did a lot of research on that topic but i can't find anything useful. What i wanna achieve is that if I close my window, my application will stay in the background and can be opened again from the tray. I managed to create the tray, but when i close the application window it closes the whole application. How can i achieve that?
This application will be on Windows and MacOS only. I don't care about android right now
Edit: For version 0.4.0
I managed to figure it out. The main function should be an application, not the window
#OptIn(ExperimentalComposeUiApi::class)
fun main() = application {
}
And if the application contains both the Window and the Tray, it will continue to run in the background and does not close after the window is closed.
#OptIn(ExperimentalComposeUiApi::class)
fun main() = application {
Tray(
icon = BufferedImage(24, 24, 1),
menu = {
Item(
"Exit",
onClick = { exitProcess(1) }
)
}
)
Window{
Text("Hello World")
}
}
Edit: For version 1.0.0-beta5
Now you have to specify the onCloseRequest on the window object, if you leave it blank it won't close the window.
In the application, create a variable which will indicate whether the window is open or not.
Create the tray as before. The tray icon now requires a Painter object instead of a BufferedImage.
Than simply check if the window open state is true, show the window, otherwise do nothing.
#OptIn(ExperimentalComposeUiApi::class)
fun main() = application {
val isOpen = remember { mutableStateOf(true)}
Tray(
icon = TrayIcon,
menu = {
Item(
"Exit",
onClick = { exitApplication() }
)
}
)
if(isOpen.value){
Window(
onCloseRequest = { isOpen.value = false }
) {
MaterialTheme {
Text("Hello World")
}
}
}
}
object TrayIcon : Painter() {
override val intrinsicSize = Size(256f, 256f)
override fun DrawScope.onDraw() {
drawOval(Color(0xFFFFA500))
}
}

How can i call an interface in kotlin?

I do not have a project in my work and they have asked me to give me a pass, but after passing the whole project, there is a part that has given me a code error at the moment. Clearly it's my first time in Kotlin and I have no idea, but I do have an idea. I tried to solve it and I have not succeeded. So I was asking for help. I get an error right at the beginning of the
= SpeechService.Lintener {
Here the code
private val mSpeechServiceListener = SpeechService.Listener { text: String?, isFinal: Boolean ->
if (isFinal) {
mVoiceRecorder!!.dismiss()
}
if (mText != null && !TextUtils.isEmpty(text)) {
runOnUiThread {
if (isFinal) {
if (mText!!.text.toString().equals("hola", ignoreCase = true) || b == true) {
if (b == true) {
mText!!.text = null
mTextMod!!.text = text
repro().onPostExecute(text)
random = 2
} else {
b = true
mText!!.text = null
val saludo = "Bienvenido, ¿que desea?"
mTextMod!!.text = saludo
repro().onPostExecute(saludo)
}
}
} else {
mText!!.text = text
}
}
}
}
and here the interface
interface Listener {
fun onSpeechRecognized(text: String?, isFinal: Boolean)
}
Please, help me. the error is "Interface Listener does not have constructor"
The SpeechService.Listener { } syntax for SAM interfaces is only possible when the interface is written i Java (see https://kotlinlang.org/docs/reference/java-interop.html#sam-conversions). Because the interface is written in Kotlin, you have to write it like this:
private val mSpeechServiceListener = object : SpeechService.Listener {
override fun onSpeechRecognized(text: String?, isFinal: Boolean) {
// Code here
}
}
You don't really need the SpeechService.Listener interface in Kotlin though. You could just use a lambda function. This depends on whether the interface comes from a library or if you've written it yourself though.
private val mSpeechServiceListener: (String?, Boolean) -> Unit = { text, isFinal ->
// Code here
}

Selenium multiple tabs at once

I'm working with Selenium, and am wondering if it's possible to use multiple TABS at once? I do not want to use multiple browser instances (i.e., 2 copies of IE pun). IF IT IS NOT possible, how would one go about switching between individual tabs, that are running sequentially?
Thanks!
If there is a link that opens up a new window/tab, then you can use driver.switchTo().window();
However, if you want to run something on multiple windows, then I recommend having multiple instances of webdriver. It is much easier to manage, and is supported (There are workarounds on opening a new tab/window, such as pressing a hotkey that opens a new window, but they aren't supported).
If you are wanting to have multiple threads all act on the same driver instance, but different tabs, that is NOT possible.
It is possible to switch between individual tabs without having multiple browser instances.
There is a difference how web driver handles different windows and how it handles different tabs.
Case 1:
In case there are multiple windows, then the following code can help:
//Get the current window handle
String windowHandle = driver.getWindowHandle();
//Get the list of window handles
ArrayList tabs = new ArrayList (driver.getWindowHandles());
System.out.println(tabs.size());
//Use the list of window handles to switch between windows
driver.switchTo().window(tabs.get(0));
//Switch back to original window
driver.switchTo().window(mainWindowHandle);
Case 2:
In case there are multiple tabs in the same window, then there is only one window handle. Hence switching between window handles keeps the control in the same tab. In this case using Ctrl + \t (Ctrl + Tab) to switch between tabs is more useful.
//Open a new tab using Ctrl + t
driver.findElement(By.cssSelector("body")).sendKeys(Keys.CONTROL +"t");
//Switch between tabs using Ctrl + \t
driver.findElement(By.cssSelector("body")).sendKeys(Keys.CONTROL +"\t");
Detailed sample code can be found here:
http://design-interviews.blogspot.com/2014/11/switching-between-tabs-in-same-browser-window.html
To open multiple tabs:
driver = new ChromeDriver();
IJavaScriptExecutor jscript = driver as IJavaScriptExecutor;
for (int i = 0; i < 10; i++)
{
driver.Navigate().GoToUrl(this.baseURL);
jscript.ExecuteScript("window.open('{0}', '_blank');", this.baseURL);
}
Swich between them:
for (int i = 0; i < driver.WindowHandles.Count; i++)
{
driver.SwitchTo().Window(driver.WindowHandles[i])]);
}
Try with below code.
String oldTab = driver.getWindowHandle();
driver.manage().timeouts().implicitlyWait(1, TimeUnit.SECONDS);
ArrayList<String> newTab = new ArrayList<String>(driver.getWindowHandles());
newTab.remove(oldTab);
driver.switchTo().window(newTab.get(0));
I recently implemented a simple multi-thread utility that allows running tests on separate tabs on separate threads WITH JUST ONE WEBDRIVER INSTANCE. The problem with WebDriver is that it can focus only one tab (window) at a time. So, to do tests in multiple tabs, WebDriver must be focused separately on each of them. I'm sure my implementation is not perfect, but here it is (implementation in Kotlin):
Usage:
fun test() {
val results = ParallelNavigator(webDriver,
listOf(
::test1,
::test2,
::test3
)
).start()
println(results)
// Output: [Success, Failure: java.lang.RuntimeException: Some error, Success]
}
fun test1(pn: ParallelNavigator) {
/* ... open url, find elements etc. so stuff */
pn.resumeNext() // transfer flow to another unfinished thread (test2 if not finished)
/* ... do more stuff */
pn.resumeNext() // again transfer flow to another thread
}
fun test2(pn: ParallelNavigator) { /* ... */ }
fun test3(pn: ParallelNavigator) { /* ... */ }
Implementation:
import org.openqa.selenium.JavascriptExecutor
import org.openqa.selenium.WebDriver
import org.openqa.selenium.support.ui.WebDriverWait
import java.util.concurrent.locks.Condition
import java.util.concurrent.locks.ReentrantLock
import kotlin.concurrent.thread
import kotlin.concurrent.withLock
class ParallelNavigator(private val webDriver: WebDriver, executions: List<(ParallelNavigator) -> Unit>) {
private val _executions: List<TabExecution> = executions.map { TabExecution(it) }
private var currentExecutionIndex: Int = -1
fun start(): List<Result> {
createTabs()
return runInternal()
}
fun resumeNext() {
if (_executions.isEmpty()) {
throw RuntimeException("No executions provided.")
}
val currentExecution: TabExecution? = if (currentExecutionIndex != -1) {
_executions[currentExecutionIndex]
} else null
val unfinished = _executions.filter { !it.finished }
if(unfinished.isEmpty()) {
return
}
val nextExecutionIndex = if (currentExecutionIndex >= unfinished.lastIndex || currentExecutionIndex <= -1) {
0
} else {
currentExecutionIndex + 1
}
val nextExecution = unfinished[nextExecutionIndex]
currentExecutionIndex = nextExecutionIndex
webDriver.switchTo().window(nextExecution.windowHandle)
nextExecution.lock.withLock {
nextExecution.condition.signal()
}
currentExecution?.lock?.withLock {
if (!currentExecution.finished) {
currentExecution.condition.await()
}
}
}
sealed class Result {
class Success : Result() {
override fun toString(): String {
return "Success"
}
}
class Failure(val ex: Throwable) : Result() {
override fun toString(): String {
return "Failure: ${ex.javaClass.name}: ${ex.message}"
}
}
class Unfinished : Result() {
override fun toString(): String {
return "Unfinished"
}
}
}
data class TabExecution(
val test: (ParallelNavigator) -> Unit,
val lock: ReentrantLock = ReentrantLock(),
var finished: Boolean = false
) {
lateinit var windowHandle: String
lateinit var condition: Condition
lateinit var thread: Thread
}
private fun createTabs() = with(webDriver) {
navigate().to("about:blank")
val homeWindowHandle = windowHandle
for (execution in _executions) {
execution.windowHandle = openNewTab()
}
webDriver.switchTo().window(homeWindowHandle)
}
private fun runInternal(): List<Result> {
val results = _executions.map { Result.Unfinished() as Result }.toMutableList()
for (index in _executions.indices) {
val execution = _executions[index]
val condition = execution.lock.newCondition()
execution.condition = condition
execution.thread = thread(start = false) {
execution.lock.withLock {
condition.await()
try {
execution.test(this)
results[index] = Result.Success()
} catch (ex: Throwable) {
ex.printStackTrace()
results[index] = Result.Failure(ex)
}
execution.finished = true
currentExecutionIndex--
resumeNext()
}
}
execution.thread.start()
}
resumeNext() // run first execution
for (execution in _executions) {
execution.thread.join()
}
return results
}
fun waitForNewTabToOpen(oldWindowHandles: Set<String>) = with(webDriver) {
waitForNewTabToOpen(oldWindowHandles, 10)
}
fun waitForNewTabToOpen(oldWindowHandles: Set<String>, seconds: Int) = with(webDriver) {
WebDriverWait(webDriver, seconds.toLong()).until<Boolean> { WebDriver -> availableWindowHandles().size > oldWindowHandles.size }
}
fun availableWindowHandles(): Set<String> = with(webDriver) {
return webDriver.getWindowHandles()
}
private fun getNewTabHandle(oldWindowHandles: Set<String>): String = with(webDriver) {
waitForNewTabToOpen(oldWindowHandles)
val newWindowHandles = availableWindowHandles().toMutableSet()
newWindowHandles.removeAll(oldWindowHandles)
return newWindowHandles.iterator().next()
}
fun openNewTab(): String = with(webDriver) {
val oldHandles = availableWindowHandles()
(webDriver as JavascriptExecutor).executeScript("Object.assign(document.createElement('a'), { target: '_blank', href: 'about:blank'}).click();")
waitForNewTabToOpen(oldHandles)
return getNewTabHandle(oldHandles)
}
}
This will solve your problem in case ur working with selenium and nodejs.
driver.get('https://www.google.com/')
.then(_ =>
driver.findElement(webdriver.By.tagName('body'))
)
.then(bodyElement => {
bodyElement.sendKeys(webdriver.Key.chord(webdriver.Key.CONTROL, 't'))
})
.catch(err => {
console.log(err);
})
if you are wishing to run the multıple wındows at the same time, use threading with multiple instances of IWebDriver
EX:
public void Work()
{
IWebDriver driver = new ChromeDriver("D:\\Drivers");
driver.Navigate().GoToUrl(URL);
\\Do the rest
}
public void Work2()
{
IWebDriver driver = new ChromeDriver("D:\\Drivers");
driver.Navigate().GoToUrl(URL2);
\\Do the rest
}
and call the function like this:
Thread thread1 = new Thread(new ThreadStart(Work));
thread1.Start();
Thread thread2 = new Thread(new ThreadStart(Work2));
thread2.Start();