how to implement autocomplete combobox in tornadofx - tornadofx

I have gone through the doc but no luck. Where as there is autocomplete in the github repo. Please provide a working example of tornadofx auto complete combobox?

In it's simplest form, you just call makeAutocompletable() on a ComboBox. Here is a complete view with a Form.
class MyView : View() {
val selectedFruit = SimpleStringProperty()
val fruits = listOf("Apple", "Banana", "Pear")
override val root = form {
fieldset {
field("Fruit") {
combobox(selectedFruit, fruits) {
makeAutocompletable()
}
}
}
}
}

Related

Prevent a macOS bar from having a sidebar

By default the first list added to a view seems to be set to sidebar. Even if you don't specifically call .listStyle(SidebarListStyle()).
Is there any way that the list you set on the view (even if it's the first view) is not set to a sidebar? There is no indication on the Apple's documentation, and anything I have tried to style the list is not working.
So instead of the list looking like this:
It should look like:
Edit:
Yes, code. Any simple code will do.
struct ContentView: View {
var body: some View {
List {
NavigationLink(
destination: TextEView(),
) {
Text("One")
}
}
}
}
struct TextEView: View {
#State private var fullText: String = "This is some editable text..."
var body: some View {
TextEditor(text: $fullText)
}
}
For your case try
List {
NavigationLink(
destination: TextEView(),
) {
Text("One")
}
}
.listStyle(.plain) // << here !!

How to change particular property value of a class in a LiveData<List<T>> (in my case LiveData<List<Item>>) using MediatorLiveData

The Item.kt class is
#Entity(tableName = "item")
class Item(
val id: Long,
val title: String,
) {
#Ignore
var selection: Boolean = false
}
Then i make a query to get all the items in the table ,it return
LiveData<List<Item>>
Then in the viewModel i want to apply selection(true) accordig to the Mutablelivedata selectionId, the selection id contain MutableLiveData<Long> (it contain an id in the LiveData<List<Item>>)
The MyViewModel.kt code is look like this
class MyViewModel(val repository: Repository) : ViewModel() {
..........
......
val selectionId: MutableLiveData<Long> by lazy {
MutableLiveData<Long>()
}
fun setSelectionId(id: Long) {
selectionId.postValue(id)
}
..........
......
val itemLiveList: LiveData<List<Item>> = liveData(Dispatchers.IO) {
emitSource(repository.getItems())
}
}
If it is an List<Item> i can do somethig like this
val ItemWithSelection: List<Item> = repository.getItems().apply {
this.forEach {
if (it.id == selectionId) {
it.selection = true
}
}
}
but i don't know how to achieve this using Mediator LiveData . Please help me
I don't understand everything in your code, for example I have never seen a function called liveData(CoroutineDispatcher). But do you mean you want something like this?
val listWithoutSelection = liveData(Dispatchers.IO) {
emitSource(repository.getItems())
}
val listWithSelection = MediatorLiveData<List<Item>>().apply {
addSource(listWithoutSelection) { updateListSelection() }
addSource(selectionId) { updateListSelection() }
}
fun updateListSelection() {
listWithSelection.value = listWithoutSelection.value?.map {
if (it.id == selectionId.value)
it.copyWithSelection(true)
else
it
}
}
The copyWithSelection could be easily done with Kotlin data classes. It is not needed dependent on whether you want to modify the object you get from the database. If you only use that object here, you could just always reset the selection of the others to false and then you can keep the object and you don't need a copy.

How to pass data from the navigationdrawer to Activity using safe-args?

Let's say we have a project like this one:
class MainActivity : AppCompatActivity() {
private lateinit var drawerLayout: DrawerLayout
private lateinit var appBarConfiguration : AppBarConfiguration
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
val binding = DataBindingUtil.setContentView<ActivityMainBinding>(this, R.layout.activity_main)
drawerLayout = binding.drawerLayout
val navController = this.findNavController(R.id.myNavHostFragment)
NavigationUI.setupActionBarWithNavController(this, navController, drawerLayout)
appBarConfiguration = AppBarConfiguration(navController.graph, drawerLayout)
// prevent nav gesture if not on start destination
navController.addOnDestinationChangedListener { nc: NavController, nd: NavDestination, bundle: Bundle? ->
if (nd.id == nc.graph.startDestination) {
drawerLayout.setDrawerLockMode(DrawerLayout.LOCK_MODE_UNLOCKED)
} else {
drawerLayout.setDrawerLockMode(DrawerLayout.LOCK_MODE_LOCKED_CLOSED)
}
}
NavigationUI.setupWithNavController(binding.navView, navController)
}
Link to a simple project: https://github.com/udacity/andfun-kotlin-android-trivia/blob/Step.11-Solution-Adding-Animation/app/src/main/java/com/example/android/navigation/MainActivity.kt
My file for navGraph contains fragments and also one Activity where I want to go if user select its title from the navigation drawer. I want to send some data to this Activity. How can I do it using safe-args?
I'm using:
apply plugin: "androidx.navigation.safeargs"
and
implementation 'androidx.navigation:navigation-fragment:2.0.0'
implementation 'androidx.navigation:navigation-ui:2.0.0'
Firstly, you'll have to go to the navigation graph and specify an argument for the activity destination. This can be done via the design UI or in XML like this;
<navigation>
<activity android:id="#+id/someActivity">
<argument
android:name="isEditMode"
app:argType="boolean"
android:defaultValue="false" />
</activity>
</navigation>
This snippet assumes you are passing a boolean to the activity.
At this point, you can build the project so that all required files are generated.
Then in the onClick of whatever Navigation menu item responsible for starting the activity, you pass in the data;
override boolean onNavigationItemSelected(menuItem: menuItem) {
val id = menuItem.itemId
when (id) {
R.id.openActivity -> {
val bundle = bundleOf("isEditMode" to false)
findNavController().navigate(R.id.someActivity, bundle)
}
}
return true
}
Then in your activity, get the pass data safely as;
val safeArguments: MyActivityArgs by navArgs()
val isEditMode = safeArgs.isEditMode)
Inside your onCreate method in MainActivity use this:
val navController = this.findNavController(R.id.myNavHostFragment)
navController.addOnDestinationChangedListener { controller, destination, arguments ->
when(destination.id) {
R.id.caixaFragment -> {
var myVar = someMethodThatBringsMyVarValue()
val argument = NavArgument.Builder().setDefaultValue(mayVar).build()
destination.addArgument("idMes", argument)
}
}
}
I got it from Ayxan Haqverdili in this question

How can I clear a 'textfield'

I'm trying to write text to a textfield, clear the text and the write a new text. I can't get rid of the old text. The new is written on the old so I see them both. I'm using choosefile and trying to show the selected file in a textfield so I can confirm the selection.
class TestView : View("My View") {
var tf: TextField by singleAssign()
override val root = BorderPane()
init {
with(root) {
center = form {
fieldset("Main") {
field("File") {
vbox {
tf = textfield()
tf.text("678")
tf.clear()
tf.text("999")
}
}
}
}
}
}
}
I expected to to see '999' in the textfield but I see both 678 and 999 at the same place.
You are actually calling the text() builder on the textfield, so you're basically creating two text elements inside the text field. I think you should take a step back and read the guide before you go further :) Here is a cleaned up version of your code:
class TestView : View("My View") {
val filename = SimpleStringProperty()
override val root = borderpane {
center {
form {
fieldset("Main") {
field("File") {
textfield(filename)
button("Browse...").action {
val filters = arrayOf(FileChooser.ExtensionFilter("All Files", "*.*"))
chooseFile("Choose file...", filters).firstOrNull()?.let {
filename.value = it.path
}
}
}
}
}
}
}
}

fragment for flowpane in TornadoFX

The TornadoFX docs describe using the ListCellFragment to bind each cell in a list control to each item model in a list. I am wondering how to do something similar in a flowpane. I'd like to use that kind of class to render a bunch of controls and an SVG drawing in each cell. (So it would replace the button component in the example code below and somehow bind the shapeItem model to it).
class LibraryView : View("Current Library") {
val shapeLibraryViewModel : LibraryViewModel by inject()
override val root = anchorpane{
flowpane {
bindChildren(shapeLibraryViewModel.libraryItemsProperty){
shapeItem -> button(shapeItem.nameProperty)
}
}
}
}
Since I don't see a pre-made class like the one for list view, perhaps I would need to create something similar to it...or maybe there's a more lightweight approach?
Using an ItemFragment is a bit overkill, since the itemProperty of the fragment will never change (a new fragment would be created for every item whenever the libraryItemsProperty change. However, if your view logic for each item is substantial, this approach will provide a clean way to separate and contain that logic, so it might be worth it. Here is a complete example you can use as a starting point.
class ShapeItemFragment : ItemFragment<ShapeItem>() {
val shapeModel = ShapeItemModel().bindTo(this)
override val root = stackpane {
label(shapeModel.name)
}
}
class ShapeItem(name: String) {
val nameProperty = SimpleStringProperty(name)
}
class ShapeItemModel : ItemViewModel<ShapeItem>() {
val name = bind(ShapeItem::nameProperty)
}
class LibraryViewModel : ViewModel() {
val libraryItemsProperty = SimpleListProperty<ShapeItem>(
listOf(
ShapeItem("Shape 1"),
ShapeItem("Shape 2")
).observable()
)
}
class LibraryView : View("Current Library") {
val shapeLibraryViewModel: LibraryViewModel by inject()
override val root = anchorpane {
flowpane {
bindChildren(shapeLibraryViewModel.libraryItemsProperty) { shapeItem ->
val itemFragment = find<ShapeItemFragment>()
itemFragment.itemProperty.value = shapeItem
itemFragment.root
}
}
}
}
A slightly lighter version would be to pass the parameter into your fragment manually and just extend Fragment:
class ShapeItemFragment : Fragment() {
val item: ShapeItem by param()
override val root = stackpane {
label(item.nameProperty)
}
}
You can still bind to changes to properties inside the ShapeItem, since the underlying item won't change (as seen from the ItemFragment) anyway.
Your bindChildren statement would then look like this:
bindChildren(shapeLibraryViewModel.libraryItemsProperty) { shapeItem ->
find<ShapeItemFragment>(ShapeItemFragment::item to shapeItem).root
}

Resources