Titanium Window removes child views - javascript

I create a window A in Titanium (latest ver) and add some views which works well.
var winA = Titanium.UI.createWindow({
backgroundColor: bgColor,
statusBarHidden:true
});
var circle = createDragCircle('Dent'); //This is a Ti.Draggable view
winA.add(circle);
I then open a modal window B i.e:
winB.open({modal: true});
The winB opens fine but as it's, opening all the winA child views I've added are removed. I can tell it isn't reloading the winA.
Is this default behaviour?
Edit:
OK. On further investigation, it's not removing the added views. It resets them to the position they were at before the drag event. Essentially, I'm doing the following:
var drag = require('ti.draggable');
var win = Titanium.UI.createWindow({
backgroundColor: bgColor,
statusBarHidden:true
});
var circle = drag.createView({
height: 30,
width: 30,
zIndex: 100,
top: top,
left: 25,
minLeft: 65,
maxLeft: 285,
minTop: 105,
maxTop: 370,
isDragged: false,
type: type
});
//Add an event listener to catch drag end
circle.addEventListener('end', function(e) {
var editwin; //Call to create winB
editwin.open({modal: true});
});
winB opens fine but the circle object moves back to the place it was before it was dragged.

if you are opening winB within winA with modal property then it will create a problem.

Thanks for help all.
I sorted it out by doing the following:
circle.addEventListener('end', function(e) {
this.top = e.source.top;
this.left = e.source.left;
});
This forces the object to take on the drop top & left properties.

Related

How to create a Konva-React context menu

To the best of my knowledge there isn't an easy/built in way with Konva to create a context menu for right clicking on objects. I am busy working on a project which requires the use of context menus, so I thought I'd just create my own.
Needless to say I am fairly new to Konva, so I was hoping someone on SO might have more experience to help me get over the last hurdles.
I have create a sandbox, located HERE
The requirements are:
An object should be draggable. (I copied a working example off the Konva sandbox.)
An object should show a context menu when right clicked upon.
The context menu should be dynamic, thus allow for multiple items, each executing its own callback when clicked upon.
Once a selection has been made, the context menu should be closed.
Thus far I have gotten most of it right, but the things I am struggling with are:
I cannot figure out how to hover over one context menu item, have it highlighted, then move to the next which should be highlighted and the old one restored to original settings.
Moving out of the context menu repaints the whole object. I don't understand why.
Clicking on one items fires both item's callbacks. Why? I a targeting the specific menu item which was clicked on, but getting both?
This point is less of a bug and more that I am unsure as how to proceed: How would I prevent multiple context menus to be create if a user right clicks multiple times on the object? Conceptually I understand that I could search for any items in a layer(?) with the name of the context menu and close it, however I have no idea how to do this.
I would appreciate any help. Thanks in advance.
Not sure if I'm late but I would use React Portals, theres a example about it on the react-konva page: https://konvajs.github.io/docs/react/DOM_Portal.html
I forked your sandbox with how this would be done: https://codesandbox.io/s/km0n1x8367
Not in react but plain JS I am afraid, but it shines a light on some of what you will have to do.
Click the pink circle, then take option 2 and click sub-option 2.
Areas requiring more work:
deliver the menu config data via JSON
make adding callbacks a method within the class
add a timeout on the hide to allow shaky mouse hands
how to handle hiding sub-menus when user mouse-outs or clicks another option
add reveal & hide animations
// Set up the canvas / stage
var stage = new Konva.Stage({container: 'container1', width: 600, height: 300});
// Add a layer some sample shapes
var layer = new Konva.Layer({draggable: false});
stage.add(layer);
// draw some shapes.
var circle = new Konva.Circle({ x: 80, y: 80, radius: 30, fill: 'Magenta'});
layer.add(circle);
var rect = new Konva.Rect({ x: 80, y: 80, width: 60, height: 40, fill: 'Cyan'});
layer.add(rect);
stage.draw();
// that is the boring bit over - now menu fun
// I decided to set up a plain JS object to define my menu structure - could easily receive from async in JSON format. [Homework #1]
var menuData = { options: [
{key: 'opt1', text: 'Option 1', callBack: null},
{key: 'opt2', text: 'Option 2', callBack: null,
options: [
{key: 'opt2-1', text: 'Sub 1', callBack: null},
{key: 'opt2-2', text: 'Sub 2', callBack: null}
]
},
{key: 'opt3', text: 'Option 3', callBack: null},
{key: 'opt4', text: 'Option 4', callBack: null}
]};
// Define a menu 'class' object.
var menu = function(menuData) {
var optHeight = 20; // couple of dimension constants.
var optWidth = 100;
var colors = ['white','gold'];
this.options = {}; // prepare an associative list accessible by key - will put key into the shape as the name so we can can get from click event to this entry
this.menuGroup = new Konva.Group({}); // prepare a canvas group to hold the option rects for this level. Make it accessible externally by this-prefix
var _this = this; // put a ref for this-this to overcome this-confusion later.
// recursive func to add a menu level and assign its option components.
var addHost = function(menuData, hostGroup, level, pos){ // params are the data for the level, the parent group, the level counter, and an offset position counter
var menuHost = new Konva.Group({ visible: false}); // make a canvas group to contain new options
hostGroup.add(menuHost); // add to the parent group
// for every option at this level
for (var i = 0; i < menuData.options.length; i = i + 1 ){
var option = menuData.options[i]; // get the option into a var for readability
// Add a rect as the background for the visible option in the menu.
option.optionRect = new Konva.Rect({x: (level * optWidth), y: (pos + i) * optHeight, width: optWidth, height: optHeight, fill: colors[0], stroke: 'silver', name: option.key });
option.optionText = new Konva.Text({x: (level * optWidth), y: (pos + i) * optHeight, width: optWidth, height: optHeight, text: ' ' + option.text, listening: false, verticalAlign: 'middle'})
console.log(option.optionText.height())
option.optionRect
.on('mouseover', function(){
this.fill(colors[1])
layer.draw();
})
.on('mouseleave', function(){
this.fill(colors[0])
layer.draw();
})
// click event listener for the menu option
option.optionRect.on('click', function(e){
var key = this.name(); // get back the key we stashed in the rect so we can get the options object from the lookup list
if (_this.options[key] && (typeof _this.options[key].callback == 'function')){ // is we found an option and it has a real function as a callback then call it.
_this.options[key].callback();
}
else {
console.log('No callback for ' + key)
}
})
menuHost.add(option.optionRect); // better add the rect and text to the canvas or we will not see it
menuHost.add(option.optionText);
_this.options[option.key] = option; // stash the option in the lookup list for later retrieval in click handlers.
// pay attention Bond - if this menu level has a sub-level then we call into this function again.
if (option.options){
var optionGroup = addHost(option, menuHost, level + 1, i) // params 3 & 4 are menu depth and popout depth for positioning the rects.
// make an onclick listener to show the sub-options
option.callback = function(e){
optionGroup.visible(true);
layer.draw();
}
}
}
return menuHost; // return the konva group
}
// so - now we can call out addHost function for the top level of the menu and it will recurse as needed down the sub-options.
var mainGroup = addHost(menuData, this.menuGroup, 0, 0);
// lets be nice and make a show() method that takes a position x,y too.
this.show = function(location){
location.x = location.x - 10; // little offset to get the group under the mouse
location.y = location.y - 10;
mainGroup.position(location);
mainGroup.show(); // notice we do not draw the layer here - leave that to the caller to avoid too much redraw.
}
// and if we have a show we better have a hide.
this.hide = function(){
mainGroup.hide();
}
// and a top-level group listener for mouse-out to hide the menu. You might want to put a timer on this [Homework #3]
mainGroup.on('mouseleave', function(){
this.hide();
layer.draw();
})
// end of the menu class object.
return this;
}
// ok - now we can get our menu data turned into a menu
var theMenu = new menu(menuData);
layer.add(theMenu.menuGroup); // add the returned canvas group to the layer
layer.draw(); // and never forget to draw the layer when it is time!
//
// now we can add some arbitrary callbacks to some of the options.
//
// make a trivial function to pop a message when we click option 1
var helloFunc = function(){
alert('hello')
}
// make this the callback for opt1 - you can move this inside the menu class object as a setCallback(name, function()) method if you prefer [homework #2]
theMenu.options['opt1'].callback = helloFunc;
// put a function on sub2 just to show it works.
theMenu.options['opt2-2'].callback = function(){ alert('click on sub-2') };
// and the original reason for this - make it a context menu on a shape.
circle.on('click', function(e){
theMenu.show({x: e.evt.offsetX, y: e.evt.offsetY});
layer.draw();
})
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/konva/2.5.1/konva.min.js"></script>
<div id='container1' style="width: 300px, height: 200px; background-color: silver;"></div>

FabricJS adding Image to Group causes odd control behavior

I'm attempting to add a loaded image into a fabric Group object. Everything looks ok, but the selection controls aren't selectable and I can't drag the object around. The top left control works though and after clicking it everything is fine.
Here is a jsfiddle that demonstrates the behavior.
var canvas = new fabric.Canvas('canvas', {
width: 200,
height: 200
});
var group = new fabric.Group();
canvas.add(group);
fabric.Image.fromURL('https://placehold.it/100x100', function(img) {
group.addWithUpdate(img);
canvas.setActiveObject(group);
canvas.renderAll();
});
Is this a bug or am I doing something wrong?
For some performance reason, fabricjs does not call setCoords automatically after adding objects to a group ( in case of many object added you can call setCoords just once ).
So after doing addWithUpdate, just call group.setCoords();
var canvas = new fabric.Canvas('canvas', {
width: 200,
height: 200
});
var group = new fabric.Group();
canvas.add(group);
fabric.Image.fromURL('https://placehold.it/100x100', function(img) {
group.addWithUpdate(img);
group.setCoords();
canvas.setActiveObject(group);
canvas.renderAll();
});
I came across this post because I was having trouble getting the positioning of images to work properly. I ended up finding that it's easier to just create create an image element using document.createElement and set the src, then feed that into fabric.Image with all the options you need (instead of using fabric.Image.fromURL which was too much of a headache to use), before adding it to the group.
var oImg = document.createElement("img");
oImg.setAttribute('src', 'https://upload.wikimedia.org/wikipedia/commons/thumb/9/92/Cog_font_awesome.svg/512px-Cog_font_awesome.svg.png');
var i = new fabric.Image(oImg, {
originX: 'center',
originY: 'center',
left: left+35,
top: top-30,
scaleX:.05,
scaleY:.05,
});
g = new fabric.Group([r, t, i]); // where r and t are other fabric objects

Surface order is messed when switching back with famo.us RenderController

A beginner question. I'm trying to put up a famo.us app. So far, I had nice results on a simple experiment.
Now that my app is growing, I want to allow moving between "screens" : landing screen -> info screen -> back to landing screen -> etc.
My "screens" are View() with surfaces, modifiers, etc. added to the View()
I use RenderController() to switch between screens, with its hide() and show() functions.
The problem is that when going back to a previously used screen, surfaces no longer show up !
Code :
var app_render_controller = new RenderController();
app_render_controller.show(landing_screen); // ok
app_render_controller.hide(landing_screen); // ok
app_render_controller.show(info_screen); // ok
app_render_controller.hide(info_screen); // ok
app_render_controller.show(landing_screen); // NOK -> I only see my background surface !
When examining the DOM, I'm able to see the outline of my surfaces at the place they should be, but they are hidden. It seems the background surface is now in front of the content surfaces (a ScrollView). If I hide the background, I see the Scrollview again.
I read that the overlap of elements was corresponding to the order they were added to the rendering tree. So since the tree under my View()-based "screen" hasn't changed, the Scrollview should still be drawn on top of the background like the 1st time ?
Is this expected ? Am I doing something wrong ? Am I missinq something ?
------- edit ------
my code (roughly) :
var main_context = Engine.createContext();
var root_render_controller = new RenderController();
main_context.add(root_render_controller);
var landing_screen = new View();
landing_screen.add(new Surface(...background texture...));
var scrollview = new Scrollview();
landing_screen.add(scrollview);
// (add stuff to the scrollview)
var info_screen = new View();
info_screen.add(new Surface(...background texture...));
info_screen.add(new Surface(...some message...));
root_render_controller.show(landing_screen);
// display is fine
root_render_controller.hide(landing_screen);
root_render_controller.show(info_screen);
// display is fine
root_render_controller.hide(info_screen);
root_render_controller.show(landing_screen); // again
// display is wrecked : the background surface is now hiding the scrollview !
When looking at the DOM, we can see the flip :
1st display of the landing screen : background then the scrollview and its 6 elements
2nd display of the landing screen : scrollview and its 6 elements then the background !
I read that the overlap of elements was corresponding to the order they were added to the rendering tree
From what I've seen, overlap of elements is set by the inverse of the order of creation (e.g., last one on top), not addition to the render tree.
When a Surface is created, it is registered with Entity:
https://github.com/Famous/core/blob/master/Surface.js#L60
function Surface(options) {
...
this.id = Entity.register(this);
...
}
When changes on a RenderNode are committed, they are applied based on the creation order (as Object.keys returns indices from an array back in numerical order):
https://github.com/Famous/core/blob/master/RenderNode.js#L106
function _applyCommit(spec, context, cacheStorage) {
var result = SpecParser.parse(spec, context);
var keys = Object.keys(result);
for (var i = 0; i < keys.length; i++) {
var id = keys[i];
var childNode = Entity.get(id);
....
}
You can test this out by creating two Surfaces and adding the one you created second to a Context before the first. It will still be on top:
define(function(require, exports, module) {
var Engine = require("famous/core/Engine");
var Surface = require("famous/core/Surface");
var mainContext = Engine.createContext();
var surface1 = new Surface({
size: [200, 200],
content: "Red",
properties: {
lineHeight: "200px",
textAlign: "center",
backgroundColor: "#f00"
}
});
var surface2 = new Surface({
size: [200, 200],
content: "Green",
properties: {
lineHeight: "200px",
textAlign: "center",
backgroundColor: "#0f0"
}
});
// Doesn't matter what order you add these - surface2 will be on top
mainContext.add(surface2);
mainContext.add(surface1);
});
Hopefully that gives you a good place to start looking. It would help if you shared where your background is added to the Context to get a sense of why it is now in front.
It looks like you've found a bug in RenderController. The simple example below is exhibiting the same behavior you described. I've filed it against their repo at https://github.com/Famous/views/issues/42.
Note: If you don't hide the current view before showing the next, you get the same behavior. Interestingly, if you wait between the hide/show operations, you see different incorrect behavior.
define(function(require, exports, module) {
var Engine = require("famous/core/Engine");
var Surface = require("famous/core/Surface");
var View = require("famous/core/View");
var RenderController = require("famous/views/RenderController");
var Timer = require("famous/utilities/Timer");
var mainContext = Engine.createContext();
var renderController = new RenderController();
mainContext.add(renderController);
var view1 = new View();
var view2 = new View();
var surface1 = new Surface({
size: [200, 200],
content: "Red Background",
properties: {
lineHeight: "200px",
textAlign: "center",
backgroundColor: "#f00"
}
});
var surface2 = new Surface({
size: [200, 200],
content: "Green Foreground",
properties: {
lineHeight: "200px",
textAlign: "center",
backgroundColor: "#0f0"
}
});
var surface3 = new Surface({
size: [200, 200],
content: "Blue Background",
properties: {
lineHeight: "200px",
textAlign: "center",
backgroundColor: "#00f"
}
});
var surface4 = new Surface({
size: [200, 200],
content: "Black Foreground",
properties: {
lineHeight: "200px",
textAlign: "center",
backgroundColor: "#000",
color: "white"
}
});
view1.add(surface1);
view1.add(surface2);
view2.add(surface3);
view2.add(surface4);
// If performing hide/show on same tick (or not hiding before show),
// you get green-fg, black-fg, red-bg (wrong)
renderController.show(view1);
Timer.setTimeout(function() {
renderController.hide(view1); // <- commenting this out results in same behavior
renderController.show(view2);
Timer.setTimeout(function() {
renderController.hide(view2); // <- commenting this out results in same behavior
renderController.show(view1);
}, 1000);
}, 1000);
// If waiting between each hide/show
// you get green-fg, blue-bg (wrong), green-fg
// renderController.show(view1);
// Timer.setTimeout(function() {
// renderController.hide(view1);
// Timer.setTimeout(function() {
// renderController.show(view2);
// Timer.setTimeout(function() {
// renderController.hide(view2);
// Timer.setTimeout(function() {
// renderController.show(view1);
// }, 1000)
// }, 1000)
// }, 1000)
// }, 1000)
});
A View has no idea the order in which Surfaces should be layered in. When something is hidden with a RenderController it frees up the document fragment that the Surface is managing. When that View is brought back into the scene graph, the Surfaces get a document fragment to fill with its content from the pool of unused elements if any exist, if the pool is empty, it creates new ones.
Due to the way that browsers implement layering, newly created DOM nodes exist on top of old DOM nodes if they exist at the same transform in Z and have the same CSS zIndex. You should not be relying on the creation order if you need explicit layering. Try getting around this by either adding Modifiers to transform them in z-space or add explicit CSS zIndexing to denote what your layers should be.

How to create a back button to previous page on Android with Titanium Studio?

I have created a back button to take me to the previous page. See code bellow:
var backbutton = Titanium.UI.createButton({
title:'back',
bottom: 10,
left: 10,
zIndex:2
});
win3.add(backbutton);
I add a addEventListener to backbutton. See code bellow:
backbutton.addEventListener('click',function() {
var win = Titanium.UI.createWindow({
url:'alarmgroups.js',
title:'Sensor/Larm Objekt'
});
win.open({modal:true});
win3.close();
win3.hide();
});
Know I wonder what the problem could be.
When Im using the code above It makes the Application crash.
Im using zIndex on every .js page that I have in my project, but I dont know if Its right to do so.
I use win.open({modal:true}); and after that code I run win3.close(); and win3.hide();. win3 Its my current window.
Does anyone having a solution on how to create a back button for Android?
You have two native solutions to create a back button on android, the first one is adding a back button to the action bar:
To achieve this, you have to edit the android's action bar in the window's open event.
(Note: do not use modal:true while opening the window)
var window = Ti.UI.createWindow({
title: "test",
backgroundColor: "white",
});
window.addEventListener('open', function({
window.activity.actionBar.onHomeIconItemSelected = function() { window.close(); };
window.activity.actionBar.displayHomeAsUp = true;
});
window.open();
The second way, is overriding the android's back button of the current window.
var window = Ti.UI.createWindow({
title: "test",
backgroundColor: "white",
});
window.addEventListener('androidback', function({
window.close();
});
window.open();
Try this :
var win = Ti.UI.createWindow({
title:'Hello world',
backgroundColor:'#fff',
fullscreen:false
});
win.addEventListener('androidback',function() {
// do something
});
Also,here is the link : Android Back Button in Titanium.
Thanks.

kineticjs add element to stage

I'm new to JS and KineticJS, and was wondering if someone could help me understand how the Kinetic.Stage node works. I've used this tutorial as a starting place -- but would like to be able to append objects (such as new Kinetic.Text() or new Kinetic.Group()) to the existing stage on click of a button.
An example of what I'm trying to do is have a form next to the canvas that allows a user to enter text, click "go", and have the "text that they entered" appear inside the canvas.
I've tried to declare "var stage" inside the document.ready and in global scope, thinking that I could access the stage from within a function, for instance:
function writeTextToStage(stage) {
var text = new Kinetic.Text({
x: 190,
y: 15,
text: 'Simple Text',
fontSize: 30,
fontFamily: 'Calibri',
textFill: 'green'
});
var layer = new Kinetic.Layer();
layer.add(text);
stage.add(layer);
stage.draw();
}
window.onload = function(){
var stage = new Kinetic.Stage({
container: "designer",
width: 578,
height: 520
});
var go = document.getElementById('go');
go.addEventListener('mousedown', writeTextToCanvas(stage);
};
Anyways, if the "stage" object is declared within another function -- "initStage()", is it possible to get the stage node after it has been initialized in order to append things to it? I've tried accessing it via the DOM element, $('kineticjs-content').children()[0].
Please assist. The documentation on kineticjs.com is not very verbose.
Thanks!
You need to create the layer outside your function, then in your function just add the text to the layer...
function AddText() {
var text = new Kinetic.Text({
...
});
layer.add(text);
}

Categories

Resources