Using java.util.Properties to drive Compose states - kotlin

I am designing a GUI for my desktop application using Compose for Desktop. Some GUI components need to be preserved if the application is closed and reopened—and some GUI components also need to change based on some state(s). To do this, I am using a simple java.util.Properties object to store information (I have already extended Properties to store its contents to a file whenever setProperty is called).
For example, I want to have the GUI components update when the locale is changed. When the user changes the locale, I set that property, something along the lines of:
launcherState = Properties() // This is being initialized before the launcher is rendered
...
launcherState.setProperty("locale", "en")
I then want then want a ResourceBundle—defined by a state—to update. I have tried something like this:
val locale by remember { derivedStateOf { launcherState.getProperty("locale") } }
val i18n by remember {
derivedStateOf {
ResourceBundle.getBundle("i18n.launcher", Locale.forLanguageTag(locale))
}
}
The text elements of the GUI use the i18n object to obtain their strings.
However, if I call launcherState.setProperty("locale", "fr"), locale and i18n do not update (therefore the GUI does not update)—which I believe they shouldn't anyways.
So my question is, how can I make launcherState "observable" so that when I make a change to it, the states update? Or perhaps, I should be taking a completely different approach.

Related

Is there such thing as a global variable? (GDscript)

I'm trying to make a game but I need something like a global variable for it to work. Is there a way in GDscript that you can make a variable that works across all nodes with scripts. I need a button that makes you buy a gun which would set a variable to true and with that variable being true, you could equip the gun.
This question doesn't really fit this section where it says: What did you try and what were you expecting? so I'm just gonna skip it.
Is there a way in GDscript that you can make a variable that works across all nodes with scripts.
You could use an autoload.
Go to the Project menu, the Project Settings option, in the Autoloads tab… And there you can set a script, and give it a name. It will be available for every node in the scene tree.
For example, you can have a "globals.gd" script that looks like this:
extends Node
var has_gun := false
Then you make it an autoload with the name "Globals". So in any other script you can use it. For example:
extends Node
func _ready() -> void:
print(Globals.has_gun)
And yes, autoloads will stay there even if you change the current scene.
You might also be interested in the Signal bus (Event bus) pattern, which involves defining signals in an autoload, such that other scripts emit them or connect to them.
Technically the autoload is not a true global. As I said, the autoload will be available for the nodes in the scene tree.
This also means the autoload will not be available for a script that is not a Node (e.g. a Resource), or otherwise not in the scene tree.
Unless you use a hacky workaround to get a reference to the autoload, which I will not go into.
Instead, I will give you an alternative: resource based communication.
This time you create a "global_resource.gd" that look like this:
class_name GlobalResource
extends Resource
var has_gun := false
And then you can use the context menu on the FileSystem panel to create a new resource. When Godot asks you for the type, you select GlobalResource, and give it a name such as "globals.tres".
Then you can use it like this:
extends Node
const Globals := preload("res://globals.tres")
func _ready() -> void:
print(Globals.has_gun)
Everywhere you preload that same resource file ("globals.tres") you will get the same Resource instance. It does not matter if it is in the same scene, or if you changed the current scene.
And yes, this does not depend on the scene tree. And yes, you can put signals in there too.
For completeness sake, I'll also mention that there is another hacky workaround which involves defining a Dictionary or Array as const in a script with a class name. Since in Godot 3 const does not imply inmutable (don't count on this on Godot 4). That is how some people currently work around the lack of static variables. However, I believe you won't find it necessary.

How can I refresh my composable when new data is being inserted?

My team and I have to make a license plate scanning app as a school project. With that, we have comments and pictures which can be added to cargo. Whenever the user scans a plate they also get the chance to change the checked info in case of a mistake. The problem is that whenever we delete data from the scanned plate it doesn't show on the screen that it has been deleted until we go to another screen. The same goes for the lazy column which we use for inserting new instances of comments and pictures. The data doesn't show on screen until we turn our screen or go back to another screen. private val pictureList = mutableListOf() is what we use for the pictures and for the text we use var countryCode by remember { mutableStateOf(CountryCodeText) }
var licenseNumber by remember { mutableStateOf(LicenseNumberText) }. pictureList is a global variable and the other ones are local variables which use global variables in the mutableStateOf. How can we make sure that the UI updates whenever the data changes? In advance I want to say thanks for the help! (Code is written in Kotlin and jetpack compose)
Just replace the mutableListOf() with a mutableStateListOf(...), and prefer to keep all the state logic confined to a ViewModel. The viewmodel should preferably be only one for the entire app, and the entire app should have only one activity.
The viewmodel should act as a single source of truth for the entire activity's UI state, while also handling all the updates to the UI efficiently.
The #Composables should only be incharge of displaying the state, and sending events to the viewmodel to update the state, for example, an onClick event may be sent up to the viewmodel by a button too trigger a state change in another part of the app.
Take this codelab to learn all about state in Compose (Well, not all, really, but good starter).
Also, changing screens destroys all the #Composables of the current screen, and so when you ce back there, all the #Composables are re-created, and the correct data is fetched. If you wish to trigger "recompositions" upon changing a variable, you must ensure that the concerned variable is a state-holder, i.e., initialized with one of the pre-built state initializers, like mutableStateOf(), or mutableStateMapOf, etc.
We usually have a mutableState*Of format for determining whether a pre-built state initializer is available. The most common ones are covered, obviously, but if not, you'll need to create a new type of initializer yourself, and if that is not something you know how to do, currently, you can just go about checking whether the type of data you wish to store is Immutable. If so, you can just wrap it in a mutableStateOf<DataType>() call, and it will trigger the recompositions. Know that this is not as efficient as pre-built initializers, but definitely gets the job done.
Also, I suggest you take the compose-pathway to get the basics down. It covers everything ranging from creating a simple UI using basic pre-built Layouts to creating a complex animation-driven application using custom Layouts.
So, definitely a lot to take it, but I hope you get there.
Happy composing,

How to add a searchbar below largeTitle using wix/react-native-navigation?

I have this using wix/react-native-navigation:
static navigatorStyle = {
navBarHideOnScroll: true,
largeTitle: true
}
How can I add a search input (without using navBarCustomView) under the large title? Something like this:
Currently it is not supported.
This feature requires setting a searchController to the navigationItem of the native view-controller. As simple as it might seem, it's actually a bit complicated due to the fact that RNN will need to expose the functionality and API of the native UISearchController. That said, it's possible that it will be added in the future.

IntelliJ type error when using Geb static content DSL with parameters

Whenever I use a static-content defined closure that takes parameters, IntelliJ will complain that the types do not match (even if I specify the type inside the closure).
For example, if I use this static content block:
static content = {
myModule { $('myModule').module(CustomModule) }
namedModule { String name -> $(".$name").module(CustomModule) }
}
Both of the above items can be used successfully in my tests, but if I was to use 'namedModule' in one of my tests as follows:
page.namedModule("moduleName").moduleMethod("blah blah blah")
IntelliJ will highlight the parameter "moduleName" in yellow with the error:
'namedModule' cannot be applied to '(java.lang.String)'
We are trying to refactor our tests in a way that means you can navigate through the code easier (e.g. avoiding any Geb/Groovy 'magic' that IntelliJ can't resolve), and this is one of the last remaining issues preventing this from being possible.
This is a known limitation to Geb support in IntelliJ. IntelliJ always treats content definitions as properties of pages and modules even though they can be parametrised. Given that Geb support in IntelliJ is open sourced we could probably add support for this.
In the mean time, as a workaround you can use methods for parametrised content instead of content definitions and IntelliJ will be able to understand these and be able to refactor them:
void namedModule(String name) {
$(".$name").module(CustomModule)
}
There are some caveats, though:
you will loose ability to use content definition options; if you need to use these for a content definition then I suggest creating a parameterised "private" content definition (for example with a _ at the beginning of the name) that you will only ever access from within the page or module
RequiredPageContentNotPresent will not be thrown even if the returned content is empty; to work around it you will either need to add manual verification to each such method or use a strategy outlined in the first bullet point with using "private" content definitions

Changing language in UWP doesn't change system features language - only on app restart

I have a UWP application.
And i have a need to change locale on the fly, so i have this for language changing:
Windows.Globalization.ApplicationLanguages.PrimaryLanguageOverride = language.FourDigitCode;
ResourceContext.GetForViewIndependentUse().Reset();
ResourceContext.GetForCurrentView();
But there is a problem that system features language doesn't switch ( only after application relaunch ) how can i fix it?
Here is an example:
Now i run this code:
Windows.Globalization.ApplicationLanguages.PrimaryLanguageOverride = "lv-LV";
ResourceContext.GetForViewIndependentUse().Reset();
ResourceContext.GetForCurrentView();
The UI gets localized, but system features still remain unlocalized:
But when i restart the app, all is OK:
Any ideas how can i fix it?
I'm afraid there is no fix for this and what you've seen is by design. Ref Remarks of PrimaryLanguageOverride property:
When you set the PrimaryLanguageOverride, this is immediately reflected in the Languages property. However, this change may not take effect immediately on resources loaded in the app UI. To make sure the app responds to such changes, you can listen to the QualifierValues property on a default resource context and take whatever actions may be needed to reload resources. Those requirements may vary depending on the UI framework used by the app, and it may be necessary to restart the app.
For your scenario, a restart is needed. I'd suggest that you can add a tip to tell users to restart the app and also a button to close the app like what used in News App.
And to close the app, we can call Application.Exit method like the following.
Application.Current.Exit();
Maybe page reloading can fix it? Try to re-navigate to the same page.
Found the example below here.
//like this
private bool Reload(object param = null)
{
var type = Frame.CurrentSourcePageType;
Frame.Navigate(type, param);
Frame.BackStack.Remove(Frame.BackStack.Last());
}
// or like this
private bool Reload(object param = null)
{
var frame = Window.Current.Content as Frame;
frame.Navigate(frame.CurrentSourcePageType, param);
frame.BackStack.Remove(frame .BackStack.Last());
}