comboBoxSelectionDidChange with multiple NScomboboxes - swift

I have an app with two combo boxes in the same view that is both loaded from an SQLite database. If the user makes a selection in combo box 1, I want to fire a method to restrict the values in combo box 2.
I think I need comboBoxSelectionDidChange function, but I don't know how to tell whether the function has been fired by a selection in combo box 1 or 2?
Have looked at the function parameters but can't see how I can tell which combo box fired the function?

The notification contains an object which represents the NSComboBox instance.
If you have created an NSComboBox outlet
#IBOutlet weak var firstComboBox : NSComboBox!
you can compare the instance with the notification object
func comboBoxSelectionDidChange(_ notification: Notification) {
let comboBox = notification.object as! NSComboBox
if comboBox == firstComboBox {
// do something with firstComboBox
} else {
// it's another comboBox
}
}

The solution for ascertaining which combobox fired the selectionDidChange method worked fine. One thing I did learn though was that the method is fired before .stringValue for the new selection is updated. I wanted to use the new .stringValue in the selectionDidChange method but discovered the previous .stringValue.
The way I got around this was to use the indexOfSelectedItem which is updated before the selectionDidChange method is fired.
So my code became
//In order to be able to restrict the contacts available when a company is selected use the comboboSelectionDidChange function
func comboBoxSelectionDidChange(_ notification: Notification) {
//Define a constant combobox that takes the notification object and casts it as an NSCombobox
let combobox = notification.object as! NSComboBox
//Can now test on the combobox object because we only want to do something if cmbCompany selection changes
if combobox == cmbCompany {
//The issue with comboboSelectionDidChange function is that the method is fired before the .stringValue is updated. So using .stringValue wont work as it will use the .stringValue from previous selected item. Therefore use index of selected item as this is updated before the comboboSelectionDidChange function is fired
//Define index which is the index of the newly selected item
let index = cmbCompany.indexOfSelectedItem
//Because the compIndex index will be the same as the index of the selected item we can get the newly selected company from the compIndex which is used to populate the cmbCombobox
let company = compIndex[index]
//Get the idCompany from combobox selection passing in the company from the new selection
idcom = (delegate as! ViewController).getPrimaryKeyComp(company: company)
//Call the function to reload the contIndex with only contacts for the company selected
contIndex = (delegate as!ViewController).getContactForCompany(ic: idcom)
cmbContact.reloadData()
}
}

Related

Trying to get user's latest mouse click in scalafx

I'm trying to get user's latest mouse click in order to display the right table. However, I can't find any way to implement this idea. How do i get user's latest mouse click by using mouseEvent function?
I tried using if else statements but it doesn't work when there is still value in the monstersTable1
def handleEditMonster(action : ActionEvent) = {
val selectedMonster1 = monstersTable1.selectionModel().selectedItem.value
val selectedMonster2 = monstersTable2.selectionModel().selectedItem.value
if (selectedMonster1 != null){
val okClicked = MainApp.showMonsterEditDialog(selectedMonster1)
if (okClicked) showMonstersDetails(Some(selectedMonster1))
} else if (selectedMonster2 != null) {
val okClicked = MainApp.showMonsterEditDialog(selectedMonster2)
if (okClicked) showMonstersDetails(Some(selectedMonster2))
} else {
// Nothing selected.
val alert = new Alert(Alert.AlertType.Warning){
initOwner(MainApp.stage)
title = "No Selection"
headerText = "No monsters Selected"
contentText = "Please select a monsters in the table."
}.showAndWait()
}
}
I want it to be able to access the second table even though selectedMonster1 is still != null
It's not entirely clear from your question what it is you're trying to do, so please bear with me... (For future reference, it's best if you can create a ''minimal, complete and verifiable example'' that illustrates your problem.)
I'm assuming that you have two scalafx.scene.control.TableView instances, referenced via monstersTable1 and monstersTable2. You want to allow the user to select either one of the monsters in the first table, or one of the monsters in the second table, but not to be able to select one monster from each table simultaneously.
I'm unclear when your handleEditMonster function is called, so I'm guessing that it's invoked when the user clicks, say, an Edit Monster button, as that button's clicked event handler.
Do I have that right?
Assuming the above is accurate, you should listen for changes in table selection, and clear the selection in the other table when a new selection is made. The currently selected item in each table is a property that we can add a listener to, so we can achieve this with the following code (in your scene's initialization):
// In the onChange handlers, the first argument references the observable property
// that has been changed (in this case, the property identifying the currently
// selected item in the table), the second is the property's new value and the third
// is its previous value. We can ignore the first and the third arguments in this
// case. If the newValue is non-null (that is, if the user has made a
// selection from this table), then clear the current selection in the other
// table.
monstersTable1.selectionModel.selectedItem.onChange {(_, newValue, _) =>
if(newValue ne null) monstersTable2.selectionModel.clearSelection()
}
monstersTable2.selectionModel.selectedItem.onChange {(_, newValue, _) =>
if(newValue ne null) monstersTable1.selectionModel.clearSelection()
}
This should do the trick for you, and your handleEditMonster function should now work. You might want to add an assertion to guard against both tables having a current selection, which would indicate a bug in the selection handler logic.

Looping through entities which describe what action should be taken on screen after keypresses

Please forgive me if I don't describe this question too well, I am new to programming MacOS apps using Swift. I know the way I'm going about this is probably wrong and I just need someone to tell me the right way.
My main app screen
I have a Core Data application that stores an ordered list of entities called Items. These Items are intended to describe a single step in an activity that describes what should happen on screen. If you know the Mac application QLab each Item is like a single cue in QLab.
I have created an Activity class that is designed to read through each Item to determine the Item type and it's related information. Once the Item type has been determined the Activity class needs to present a View with information related to that particular Item and then wait until the user presses the right arrow key to then proceed to the next Item in the Core Data store where the process repeats until all Items have been read. Each time a new Item is read in the loop, the information on the screen should change after the user presses the right arrow each time.
The problem is that I don't know exactly how the best way of going about this should be programatically speaking. I have the code that retrieves the array of Items as an NSFetchRequest:
let moc = (NSApplication.shared.mainWindow?.contentViewController?.representedObject as! NSPersistentDocument).managedObjectContext!
let fetchRequest : NSFetchRequest = Item.fetchRequest()
do {
let items = try moc.fetch(fetchRequest)
print("Found " + String(items.count) + " items to use in the activity.")
for item in items {
print(item.itemType)
// How do I pause this loop for a user keypress after using data from this Item to display?
}
} catch {
print("Error retrieving Items")
}
I can retrieve the keydown event using NSEvent.addLocalMonitorForEvents(matching: .keyDown) and I'm also able to create View Controllers to display the information on a second screen. I just don't know how I should create the 'main loop', so to speak, so that information is displayed and then the app waits until the user presses a key to proceed...
I can share my project code if more information is needed and many thanks to anyone who can enlighten me... :)
You could try using a NSPageController. In your NSPageController you add a ContainerView which will display the ViewControllers that display information for each item. Each ViewController will need a storyboard identifier, e.g. ViewControllerItem1.
Your NSPageController class must conform to the NSPageControllerDelegate protocol and contains an array of ViewControllers to display.
override func viewDidLoad() {
super.viewDidLoad()
delegate = self
arrangedObjects = ["ViewControllerItem1", "ViewControllerItem2", "...","ViewControllerItemN" ]
}
Note about arrangedObjects from the NSPageController documentation: An array containing the objects displayed in the page controller’s view.
Then you implement NSPageControllers viewControllerForIdentifier to return the ViewController that you currently want to display in the ContainerView.
func pageController(_ pageController: NSPageController, viewControllerForIdentifier identifier: String) -> NSViewController {
switch identifier {
case "ViewControllerItem1":
return mainStoryboard().instantiateController(withIdentifier:"ViewControllerItem1") as? ViewControllerItem1
case "...":
default:
}
}
In your action handler for the key down event you implement.
self.navigateForward(sender) or self.navigateBack(sender)
I also implemented this method but I don't remember whether it was required.
func pageControllerDidEndLiveTransition(_ pageController: NSPageController) {
self.completeTransition()
}

Swift best practice: How to keep track of different objects?

I'm wondering what is a good solution to keep track of different objects of same type.
I have this function:
private extension MenuButtonsViewController {
// TODO: Find a way to find correct button based on MenuItem
func buttonFor(for menuItem: MenuItem) -> EmojiButton? {
guard let subViews = stackView.subviews as? [EmojiButton] else {
return nil
}
let button = buttonFactory.makeEmojiButton(title: menuItem.icon)
for subView in subViews where subView == button {
return subView
}
return nil
}
}
I have an array (UIStackView) with a varying number of buttons (EmojiButton). The buttons are created with content from MenuItem.
I'm looking for a good and clean solution how to find and remove a particular button from the stackView array, based on a MenuItem.
So far I had three ideas:
To create a new object, initalized with same values as the one to remove, and then match using ==. (Solution above). This didn't work.
To add an id to all buttons, and then a corresponding id to the MenuItem object. But this doesn't seem like an elegant solution to have to add that everywhere, and expose this variable from the button object.
Maybe store the button in a wrapper class (like MenuItemButton) with an id to match to, or by storing the MenuItem object so I can match against that.
Any ideas? How is this usually done?
If MenuItem and EmojiButton inherit from UIView, you can make use of the tag property that is available on all UIView's.
You first need to assign a unique tag value to each of your MenuItem's.
You then need to assign this same value to the corresponding Emoji button's tag property. (This would be a good thing to do in your factory.)
Having done that, you can modify your function as follows:
//assumes MenuItem and EmojiButton inherit from UIView
func buttonFor(for menuItem: MenuItem) -> EmojiButton? {
return stackView.viewWithTag(menuItem.tag) as? EmojiButton
}

How to have a subject in RxSwift push values to itself without creating an infinite loop

I have a UITableView, which I want to put into an editing state if certain conditions are met. The primary way to toggling edit is through an edit button.
So the view elements I have are
let tableView = UITableView()
let editButton = UIButton()
And whether the tableView should be in editing mode is fed from:
let editing = BehaviorSubject(value: false)
Which will be hooked up to the tableView using something like:
editing.subscribeNext { isEditing in
tableView.setEditing(isEditing, animated: true)
}
When the edit button is tapped, I want that to push a new value to editing, that is the negation of the most recent value sent to editing. The most recently value may have been set by a tap on editButton, or it may have come from somewhere else.
I don't understand how to combine the stream for the button press with the stream for editing in such a way that allows this without an infinite loop e.g.
Obervable.combineLatest(editButton.rx_tap.asObservable(), editing) { _, isEditing in
editing.onNext(!isEditing)
}
I'm aware that the tableView has an editing property, but I don't want to rely on that as I am looking for a more general solution that I can re-use elsewhere. I'm also not looking to track the value of isEditing in an instance var, or even as a Variable(), as I am looking for a stateless, stream based solution (if this is at all possible).
Thank you!
With some help from the RxSwift GitHub issues forum I've now worked it out :). The key was withLatestFrom. I've included an example of this below in case it will help anyone else. editButton is the primary way to trigger editing mode on or off, and I've included an event sent via tableView.rx_itemSelected as an additional input example (in this case, I want editing to end any time an item is selected).
let isEditing = BehaviorSubject(value: false)
let tableView = UITableView()
let editButton = UIButton()
tableView.rx_itemSelected
.map { _ in false }
.bindTo(isEditing)
editButton.rx_tap.withLatestFrom(isEditing)
.map { !$0 }
.bindTo(isEditing)
isEditing.subscribeNext { editing in
tableView.setEditing(editing, animated: true)
}
Note: This solution sends .Next(false) to isEditing every time an item is selected, even if the table isn't currently in editing mode. If you feel this is a bad thing, and want to filter rx_itemSelected to only send .Next(false) if the table is actually in editing mode, you could probably do this using a combination of withLatestFrom and filter.
What if you define editing as a Variable instead of a BehaviourSubject. A Variable cannot error out which makes sense in this case. The declaration would look like this:
let editing = Variable(value: false)
You could subscribe to a button tap and change the value of editing to the negated current one:
editButton.rx_tap.asObservable().subscribeNext { editing.value = !editing.value }
With changing the value property of editing this method is called
editing.subscribeNext { isEditing in
tableView.setEditing(isEditing, animated: true)
}
All of this is not tested, but might lead you in the right direction for the right solution.

call action methods in #Html.RadioButtonFor in MVC3

I have 3 radio buttons and 2 text boxes in a view. Initially I need to save the values in DB through the view and once its saved I need to display the saved values in the particular view according to which radio button I clicked.
One way is to post the values to a controller method, save it, then render back a new view with the values.
This got solved. We can use JQuery to achive tgis
View
--------
var url = '#Url.Action("YourActionName", "YourControllerName")';
$.post(url, 'ID=' + radio button Id , function (data) {
$('#txtBox1').val(data.Key1);
$('#txtBox2').val(data.Key2);
$('#txtBox3').val(data.Key3);
}
Controller
----------
Inside your action method , construct a JSON string as showed below and send back to JQuery Function.
var dataTest = new { "Key1"= "value1","Key2"= "value2", "Key3"= "value3" };
return Json(dataTest, JsonRequestBehavior.AllowGet);