Scala Swing GUI does not respond to pressed key - swing

I want to write a Scala interface where I click on an image to draw coloured columns. I would also like to detect if the Enter key is pressed. Here is my tentative code:
def main = {
def top = new MainFrame {
title = "Reactive Swing App"
val label = new Label {
var matRaw, mat = new Mat
mat = Imgcodecs.imread("data/batch/14-ZoneDetection/01-noExtraCol/VISSAGE_115.jp2", Imgcodecs.CV_LOAD_IMAGE_COLOR) // images are stored as 8UC3
Image.printCaracColor("mat", mat)
var buf = toBufferedImage(mat)
icon = new ImageIcon(buf)
listenTo(mouse.clicks) // mouse.clicks is a member of Label, this is why the click position is relative to the label
listenTo(keys)
reactions += {
case MousePressed(_, point, _, _, _) => {
mat.submat(0, mat.rows, point.getX.toInt, point.getX.toInt + 1).setTo(new Scalar(0.0, 255.0, 0.0))
buf = toBufferedImage(mat)
icon = new ImageIcon(buf)
println("Click position, x: " + point.getX + ", y: " + point.getY)
}
}
reactions += {
case KeyPressed(_, Key.Enter, _, _) => {
println("Enter pressed")
}
}
}
contents = label
}
val frame = top
frame.resizable = false
frame.visible = true
}
The case MousePressed works properly and the a green column is properly added to the displayed image. However, the case KeyPressed does not work. Could you help me fix this ?

It works if the label is encapsulated in a panel. The label listens to the mouse and the panel to the keyboard.

Related

How to make a image dynamically change to text

I am making an online game using Phaser and I need to make buttons with text on them for it that can change based on the text because the text can be different each time. I tried checking the API document but when I put in the get size function to try to get the bounds of the text my button disappears or the code will stop working with the error saying cannot read properties of undefined (reading getBounds) and it will swap between the two every time I reload the page.
count = Phaser.Math.Between(1,4)
for(let i = 50;i <= 750;i = i +200){
bingus = this.add.text(i, 400, quiz[category][difficulty][quest][count])
answs.push(bingus)
gorp.push(count)
count++
}
if(count > 4){
count = 1
}
}
this.butt1.setDisplaySize(Phaser.Geom.Rectangle.GetSize(answs[gorp[0]].getBounds()))
You could use the Phaser.GameObjects.Text and it's displayWidth and / or displayHeight properties, together with the global Phaser.Display.Align.In.Center function.
Maybe this works also for your UseCase.
Basically:
set the text of the text Object with setText
get the current displayWidth and displayHeight of the text Object
update/adjust the size of the button Object, also with displayWidth and displayHeight properties
Center the text Object inside of the button Object, with the Phaser.Display.Align.In.Center function
Here a small working Demo:
document.body.style = 'margin:0;';
var config = {
type: Phaser.AUTO,
width: 500,
height: 180,
scene: {
create
},
banner: false
};
let text;
let button;
let texts = ['first Text', 'Next', 'Second Text', 'Last', 'multiline1.\nmultiline2..\nmultiline3...' ];
let padding = 20;
let currentTextIdx = 0;
function create () {
this.add.text(10, 10, 'Text cycles about every second.')
button = this.add.rectangle(250, 90, 100, 40, 0xff0000 )
.setOrigin(.5);
text = this.add.text(250, 90, texts[currentTextIdx])
.setOrigin(.5);
this.time.addEvent({ delay: 1000, startAt:999, loop: true , callback: _ => {
currentTextIdx++;
if(currentTextIdx >= texts.length){
currentTextIdx = 0;
}
let newText = texts[currentTextIdx];
text.setText(newText);
button.displayWidth = padding * 2 + text.displayWidth;
button.displayHeight = padding * 2 + text.displayHeight;
Phaser.Display.Align.In.Center(text, button);
}});
}
new Phaser.Game(config);
<script src="//cdn.jsdelivr.net/npm/phaser#3.55.2/dist/phaser.js"></script>

How to check if the model out of the viewer container?

I made an info card and this card will disappear if the viewer is rotated until the model is not visible. I use isNodevisible but it always returns true.
updateInfoCard() {
if (this.infoCard && this.posModel) {
const pos = this.viewer.worldToClient(this.posModel);
console.log(pos);
this.infoCard.style.left = `${Math.floor(
50 + pos.x - this.infoCard.offsetWidth / 2
)}px`;
this.infoCard.style.top = `${Math.floor(
50 + pos.y - this.infoCard.offsetWidth / 2
)}px`;
const id = this.infoCard.dataset.id;
console.log(this.viewer.isNodeVisible(id));
this.infoCard.style.display = this.viewer.isNodeVisible(id)
? "block"
: "none";
}
}
If I understand your question correctly, you'll probably want to do an intersection test between the camera's frustum and the models's bounding box. That can be done like so:
viewer.addEventListener(Autodesk.Viewing.CAMERA_CHANGE_EVENT, function () {
if (!viewer.model) {
return;
}
const camera = viewer.getCamera();
const matrix = new THREE.Matrix4().multiplyMatrices(camera.projectionMatrix, camera.matrixWorldInverse);
const frustum = new THREE.Frustum().setFromMatrix(matrix);
const bbox = viewer.model.getBoundingBox();
console.log('Model in the view?', frustum.intersectsBox(bbox));
});
And if you only want to check the visibility of a specific element (based on its dbID) of your model, you can compute its bounding box like so:
function objectBounds(model, dbId) {
const tree = model.getInstanceTree();
const frags = model.getFragmentList();
const objectBounds = new THREE.Box3();
tree.enumNodeFragments(dbId, function (fragId) {
const fragBounds = new THREE.Box3();
frags.getWorldBounds(fragId, fragBounds);
objectBounds.union(fragBounds);
}, true);
return objectBounds;
}
The function isNodeVisible returns the visibility status of your node in the scene. If you do something like this.viewer.hide(id, model) your function will return false.
If I well understood what you want to achieve, you want to hide an info card when the associated object is occluded by others objects, so we can't see it from our point of view ?
So I think what you need is to check for occlusion. You can take a look at the checkOcclusion function of this point cloud markup extension made by Philippe Leefsma.
To check for node occlusion, you basically need to raycast from your point of view to the node that you want to check. If you hit something and it's your node, there is no occlusion. If it's not the same node, it's mean that something occlude your node.
checkOcclusion (markup) {
const clientPoint = this.viewer.worldToClient(
markup.point)
const offset = $(this.viewer.container).offset()
const rayCaster = this.pointToRaycaster(
this.viewer.impl.canvas,
this.viewer.impl.camera, {
x: clientPoint.x + offset.left,
y: clientPoint.y + offset.top
})
const hitTest = this.viewer.model.rayIntersect(
rayCaster, true, this.dbIds)
if (hitTest) {
if (hitTest.fragId === markup.fragId) {
const offset = {
x: hitTest.point.x - markup.point.x,
y: hitTest.point.y - markup.point.y,
z: hitTest.point.z - markup.point.z
}
const dist = Math.sqrt(
offset.x * offset.x +
offset.y * offset.y +
offset.z * offset.z)
if (this.options.logOcclusionDist) {
console.log(dist)
}
if (dist < this.options.occlusionDist) {
return false
}
}
return true
}
}

How can I show 2 different label sets as markup in the forge viewer

I'm trying to show some labels in the forge viewer, based on the example markup code. My code works fine for one dataset, but when I add another one, I get: "BuildingData.js:113 Uncaught TypeError: this.frags[("dbId" + dbId)] is not iterable". The 2 datasets are extensions that extend the BuildingData class & they just create a button & call super.init(). I don't get why it fails when I activate the second label set.
I'm using v7 of the viewer.
class BuildingData extends Autodesk.Viewing.Extension {
constructor(viewer, options) {
super(viewer, options);
}
init(){
//Callback to update labels
const updateLabelsCallback = () => {
if(this.button.getState() === 0) {
this.updateLabels();
}
};
//Events when to update the labels
OnEvent(this.viewer, Autodesk.Viewing.CAMERA_CHANGE_EVENT, updateLabelsCallback);
OnEvent(this.viewer, Autodesk.Viewing.ISOLATE_EVENT, updateLabelsCallback);
OnEvent(this.viewer, Autodesk.Viewing.HIDE_EVENT, updateLabelsCallback);
OnEvent(this.viewer, Autodesk.Viewing.SHOW_EVENT, updateLabelsCallback);
//Add the button to the data bar
this.button.onClick = (ev) => {
this.enabled = !this.enabled;
this.button.setState(this.enabled ? 0 : 1);
this.showLabels();
};
this.viewer.dataBar.getControl("dataGrp").addControl(this.button);
}
cleanup(){
//remove button from data bar
this.viewer.dataBar.getControl("dataGroup").removeControl(this.button);
//remove labels
let labels = FindAll(`#${this.viewer.clientContainer.id} div.adsk-viewing-viewer label.data.${this.dataClass}`);
for(let label of labels) label.remove();
}
showLabels() {
let viewerContainer = Find(`#${this.viewer.clientContainer.id} div.adsk-viewing-viewer`);
//remove old labels
let labels = FindAll(`#${this.viewer.clientContainer.id} div.adsk-viewing-viewer label.data.${this.dataClass}`);
for(let label of labels) label.remove();
//show new labels?
if(!this.enabled) return;
//check if the model tree is available
let tree = this.viewer.model.getInstanceTree();
if(tree === undefined){
console.log("Model tree is not loaded yet");
return;
}
//select sessor & fit to view
const onClick = (e) => {
this.viewer.select(e.currentTarget.dataset.dbId);
this.viewer.utilities.fitToView();
};
this.frags = [];
for(let i = 0; i < this.labels.length; i++){
const label = this.labels[i];
this.frags["dbId" + label.dbId] = [];
// create the label for the dbId
let lbl = document.createElement("label");
lbl.classList.add("data");
lbl.classList.add("update");
lbl.classList.add(this.dataClass);
lbl.dataset.dbId = label.dbId;
lbl.style.cssText = `display: ${this.viewer.isNodeVisible(label.dbId) ? "block" : "none"}`;
//add click event
OnEvent(lbl, "click", onClick);
let span = document.createElement("span");
lbl.appendChild(span);
span.innerText = label.name;
viewerContainer.appendChild(lbl);
//Collect fragment ids of dbId
tree.enumNodeFragments(label.dbId, (fragId) => {
this.frags["dbId" + label.dbId].push(fragId);
this.updateLabels();
});
}
}
updateLabels() {
for(const label of FindAll(`#${this.viewer.clientContainer.id} div.adsk-viewing-viewer .update`)){
const dbId = label.dataset.dbId;
//get center of the dbId based on the bounding box of the fragments
const pos = this.viewer.worldToClient(this.getBoundingBox(dbId).center());
//position label in the center of the box
label.style.cssText = `left: ${Math.floor(pos.x - label.offsetWidth / 2)}px`;
label.style.cssText +=`top: ${Math.floor(pos.y - label.offsetHeight / 2)}px`;
label.style.cssText +=`display: ${this.viewer.isNodeVisible(dbId) ? "block" : "none"})`;
}
}
getBoundingBox(dbId) {
var fragList = this.viewer.model.getFragmentList();
const nodebBox = new THREE.Box3()
//get bounding box for each fragment
for(const fragId of this.frags["dbId" + dbId]){ //<----- ERROR is here
const fragBBox = new THREE.Box3();
fragList.getWorldBounds(fragId, fragBBox);
nodebBox.union(fragBBox);
}
return nodebBox
}
}
I found the solution, I was trying to update all labels (of both datasets) in the viewer & not only the ones corresponding to the active dataset. Changing 1 line in updateLabels() fixed it:
const label of FindAll(`#${this.viewer.clientContainer.id} div.adsk-viewing-viewer .data.${this.dataClass}`

Display image as popup on same window

This is my first question. Please bear with me.
I have a table in which one of the column displays images, instead of displaying image in the column of table i want to change as popup image on the same window when user clicks View Image. I tried using windows.open(imgSrc), the new window popup(i don't new window to open) and image is downloaded. I attached piece of my code. Please help me.
Body
<td data-title='"Background Image"' header-class='text-left'>View Image</td>
JS
$scope.Page.Functions.GetBackgroundImageUrl = function (hero) {
if (hero && hero.BackgroundImageExist) {
var v = $scope.Page.Functions.GetDataVersion(hero.Id);
if (v) {
v = '?v=' + v;
}
window.open(ENV.apiUri + 'HomePageHero/BackgroundImage/' + hero.Id + v, "window Title", "width = 500, height = 450");
}
return null;
}
Code to display image in the table column
<td data-title='"Background Image"' header-class='text-left'><display-media model='Page.Functions.GetBackgroundImageUrl(item)'></display-media></td>
JS
$scope.Page.Functions.GetHtmlText = function (rawText) {
return $sce.trustAsHtml(rawText);
}
$scope.Page.Functions.GetBackgroundImageUrl = function (hero) {
if (hero && hero.BackgroundImageExist) {
var v = $scope.Page.Functions.GetDataVersion(hero.Id);
if (v) {
v = '?v=' + v;
}
return 'imageUrl:' + ENV.apiUri + 'HomePageHero/BackgroundImage/' + hero.Id + v;
}
return null;
}
You're passing the image to window.open which will open a new browser window, that's it's job, you're looking for a modal to open the image.
I suggest trying Modaal and adapting your code based on the Single Image Modal example on the page.

xamarin forms map's marker click event

I have a map with a single pin on it. as follows:
var map = new Map()
{
IsShowingUser = true,
HeightRequest = 100,
WidthRequest = 960,
VerticalOptions = LayoutOptions.FillAndExpand
};
and the pin location and label as follows:
var pin1 = new Pin();
pin1.Type = PinType.Place;
pin1.Position = position;
pin1.Label = "Ticket Number: " + Cache.Instance.Ticket.TicketNumber;
clicked event:
pin1.Clicked += delegate
{
uri = new Uri("http://maps.google.com/maps?daddr=" + position.Latitude + "," + position.Longitude);
Device.OpenUri(uri);
}
map loading:
var stack = new StackLayout { Spacing = 00 };
stack.Children.Add(map);
Content = stack;
when clicking on the pin marker, it opens an info window and clicking on the window and clicked event code triggers. It there any way to not show the info window and the event triggers as soon as I click on the marker?
Thanks
Use Map_PinClicked to handle the PinClick event, If you set e.Handled = true, then Pin selection doesn't work automatically. All pin selection operations are delegated to you.
In the Page:
map.PinClicked += Map_PinClicked;
// Selected Pin changed
map.SelectedPinChanged += SelectedPin_Changed;
map.InfoWindowClicked += InfoWindow_Clicked;
map.InfoWindowLongClicked += InfoWindow_LongClicked;
And then clickEvent:
void Map_PinClicked(object sender, PinClickedEventArgs e)
{
e.Handled = true;
uri = new Uri("http://maps.google.com/maps?daddr=" + position.Latitude + "," + position.Longitude);
Device.OpenUri(uri);
}
You can have a look at here for more information.
Currently with Xamarin.Forms 5, PinClicked event is designated as obsolete. Same goes for Device.OpenUri.
One can use pin1.MarkerClicked += Pin_Clicked; instead.
You can prevent the Info window from opening by setting the EventArgs's HideInfoWindow property to true.
docs.microsoft
private async void Pin_Clicked(object sender, PinClickedEventArgs e)
{
try
{
e.HideInfoWindow = true;
var pin = sender as Pin;
var uri = new Uri("http://maps.google.com/maps?daddr=" + pin.Position.Latitude + "," + pin.Position.Longitude);
Launcher.OpenAsync(uri);
}
catch (Exception ex)
{
//log error
}
}