I'm trying to make a custom GTK widget in Vala, but I'm already failing at the very first basic attempt, so I'd like some help in knowing where I'm going wrong. I feel like I must be missing something painstakingly obvious, but I just can't see it.
I have three files with the following contents:
start.vala:
using Gtk;
namespace WTF
{
MainWindow main_window;
int main(string[] args)
{
Gtk.init(ref args);
main_window = new MainWindow();
Gtk.main();
return 0;
}
}
main_window.vala:
using Gtk;
namespace WTF
{
public class MainWindow : Window
{
public MainWindow()
{
/* */
Entry entry = new Entry();
entry.set_text("Yo!");
this.add(entry);
/* */
/*
CustomWidget cw = new CustomWidget();
this.add(cw);
/* */
this.window_position = WindowPosition.CENTER;
this.set_default_size(400, 200);
this.destroy.connect(Gtk.main_quit);
this.show_all();
}
}
}
custom_widget.vala:
using Gtk;
namespace WTF
{
public class CustomWidget : Bin
{
public CustomWidget()
{
Entry entry = new Entry();
entry.set_text("Yo");
this.add(entry);
this.show_all();
}
}
}
As you can see, in main_window.vala, I have two sets of code. One that adds the Entry widget directly, and one that adds my custom widget. If you run the one that adds the Entry widget directly, you get this result:
If you run the one with the custom widget, however, you get this result:
Just for the record, this is the complication command I use:
valac --pkg gtk+-2.0 start.vala main_window.vala custom_widget.vala -o wtf
EDIT:
Following user4815162342's suggestion, I implemented the size_allocate method on my custom Bin widget, like so:
public override void size_allocate(Gdk.Rectangle r)
{
stdout.printf("Size_allocate: %d,%d ; %d,%d\n", r.x, r.y, r.width, r.height);
Allocation a = Allocation() { x = r.x, y = r.y, width = r.width, height = r.height };
this.set_allocation(a);
stdout.printf("\tHas child: %s\n", this.child != null ? "true" : "false");
if (this.child != null)
{
int border_width = (int)this.border_width;
Gdk.Rectangle cr = Gdk.Rectangle()
{
x = r.x + border_width,
y = r.y + border_width,
width = r.width - 2 * border_width,
height = r.height - 2 * border_width
};
stdout.printf("\tChild size allocate: %d,%d ; %d, %d\n", cr.x, cr.y, cr.width, cr.height);
this.child.size_allocate(cr);
}
}
It writes the following in the console:
Size_allocate: 0,0 ; 400,200
Has child: true
Child size allocate: 0,0 ; 400, 200
And the window renders thusly:
GtkBin is an abstract single-child container, typically intended to decorate the child widget in some way, or change its visibility or size. Without some added value, a single-child container would be indistinguishable from the widget it contains and therefore not very useful.
Since GtkBin doesn't know what kind of decorations you will draw around the child, it expects you to implement your own size_allocate. A simple implementation is available in gtk_event_area_size_allocate, a more complex one in gtk_button_size_allocate.
This answer shows a minimal size_allocate implementation in PyGTK which should be straightforward to port to Vala. If you do anything more complex than that, you will need to also implement expose, and possibly other methods, but this will get you started.
Related
I want to override the functionality of an HTMLElement so that the scrollTop property, on get or set, will do some extra logic before touching the actual value. The only way, as far as I have found, is to delete the original property and then use Object.defineProperty():
delete element.scrollTop;
Object.defineProperty(element, "scrollTop", {
get: function() : number {
...
},
set: function(value: number) {
...
}
});
However, this removes all access to the original scrollTop, so the new logic can't do something like return base.scrollTop. The comments on this older, similar question claim that getting access to the original value is not possible when overriding with Object.defineProperty().
I'm wondering if a possible alternative is to create an adapter class that implements the HTMLElement interface and wraps the HTMLElement in question. All implemented properties delegate to the wrapped element's properties, but its scrollTop would do the extra work I need.
I'm quite new to Typescript, but is the alternative possible? If so, is there a lightweight way of defining all other properties on the adapter that we're not touching to automatically delegate to the wrapped element?
Here is my solution to the problem (which is a bit more tricky than might seem)
I implemented some utility for that and usage look like:
Let's say we want to replace some div 'scrollTop' method to return 50 if scrollTop is bigger than 50 and return original value if it is less than 50:
const div = document.createElement("div")
replaceOriginalPropertyDescriptor(div, "scrollTop", (originalDescriptor, div) => {
return {
// Keep rest of the descriptor (like setter) original
...originalDescriptor,
get() {
const originalScrollTop = originalDescriptor.get!.apply(div);
if (originalScrollTop > 50) return 50;
return originalScrollTop
},
}
})
Here is implementation with bunch of comments:
/**
* Will replace property descriptor of target while allowing us to access original one.
*
* Example:
*
* Will replace div.scrollTop to return 50, if original scrollTop is bigger than 50. Otherwise returns original scrollTop
*
* const div = document.createElement("div")
*
* replaceOriginalPropertyDescriptor(div, "scrollTop", (originalDescriptor, div) => {
* return {
* get() {
* const originalScrollTop = originalDescriptor.get.apply(div);
*
* if (originalScrollTop > 50) return 50;
*
* return originalScrollTop
* },
* // Keep rest of the descriptor (like setter) original
* ...originalDescriptor,
* }
* })
*/
function replaceOriginalPropertyDescriptor<T extends object>(
input: T,
property: keyof T,
newDescriptorCreator: (originalDescriptor: PropertyDescriptor, target: T) => PropertyDescriptor
) {
const originalDescriptor = findPropertyDescriptor(input, property);
if (!originalDescriptor) {
throw new Error(`Cannot replace original descriptor ${String(property)}. Target has no such property`);
}
const newDescriptor = newDescriptorCreator(originalDescriptor, input);
Reflect.defineProperty(input, property, newDescriptor);
}
What was my use case:
I am using drag and drop library that is reading scroll positions like 500 times a second. I wanted to cache this value for lifetime of a single frame. As I cannot control source code of the library itself, I am kinda injecting this cache to HTMLElements itself so they keep previous scroll position values for 1 frame.
I am attempting to use the geotools.org library (Version 24) to write a user-defined function to style my GIS map according to an external data source. The documentation for the GeoTools library includes this tantalizing paragraph in the section on Functions:
When a function is used as part of a Style users often want to calculate a value based on the attributes of the Feature being drawn. The Expression PropertyName is used in this fashion to extract values out of a Feature and pass them into the function for evaluation.
This is exactly what I want to do. However, the documentation includes no actual example of how to do this.
I have spent several days trying various permutations of the Function definition, and I get the same result every time: My user-defined function only receives the geometry attribute, not the extra attributes I have specified.
I have verified that everything else works:
The features are read correctly from the shapefile
The function is actually called
The Feature geometry is passed into the function
Upon completion, the map is drawn
But I cannot get the Geotools library to pass in the additional Feature properties from the shapefile. Has anyone gotten this working, or can you even point me to an example of where this is used?
My current function definition:
package org.geotools.tutorial.function;
import mycode.data.dao.MyTableDao;
import mycode.data.model.MyTable;
import org.geotools.filter.FunctionExpressionImpl;
import org.geotools.filter.capability.FunctionNameImpl;
import org.opengis.feature.Feature;
import org.opengis.filter.capability.FunctionName;
import org.opengis.filter.expression.PropertyName;
import org.opengis.filter.expression.VolatileFunction;
import org.springframework.beans.factory.annotation.Autowired;
import java.awt.*;
import java.beans.Expression;
import java.util.Random;
public class DataFunction extends FunctionExpressionImpl {
#Autowired
MyTableDao myTableDao;
public static FunctionName NAME =
new FunctionNameImpl(
"DataFunction",
Color.class,
FunctionNameImpl.parameter("featureData1", PropertyName.class),
FunctionNameImpl.parameter("featureData2", PropertyName.class));
public DataFunction() {
super("DataFunction");
}
public int getArgCount() {
return 2;
}
#Override
public Object evaluate(Object feature) {
Feature f = (Feature) feature;
// fallback definition
float pct = 0.5F;
if (f.getProperty("featureData1") != null) {
MyTable temp = myTableDao.read(
f.getProperty("featureData1").getValue().toString(),
f.getProperty("featureData2").getValue().toString());
pct = temp.getColumnValue();
}
Color color = new Color(pct, pct, pct);
return color;
}
}
EDIT: I am invoking the function programmatically through a Style that is created in code. This Style is then added to the Layer which is added to the Map during the drawing process.
private Style createMagicStyle(File file, FeatureSource featureSource) {
FeatureType schema = (FeatureType) featureSource.getSchema();
// create a partially opaque outline stroke
Stroke stroke =
styleFactory.createStroke(
filterFactory.literal(Color.BLUE),
filterFactory.literal(1),
filterFactory.literal(0.5));
// create a partial opaque fill
Fill fill =
styleFactory.createFill(
filterFactory.function("DataFunction",
new AttributeExpressionImpl("featureData1"),
new AttributeExpressionImpl("featureData2")));
/*
* Setting the geometryPropertyName arg to null signals that we want to
* draw the default geomettry of features
*/
PolygonSymbolizer sym = styleFactory.createPolygonSymbolizer(stroke, fill, null);
Rule rule = styleFactory.createRule();
rule.symbolizers().add(sym);
FeatureTypeStyle fts = styleFactory.createFeatureTypeStyle(new Rule[] {rule});
Style style = styleFactory.createStyle();
style.featureTypeStyles().add(fts);
return style;
}
I think you want to set up your function to take a pair of Strings or Doubles (or whatever those attributes are) so something like:
public static FunctionName NAME =
new FunctionNameImpl(
"DataFunction",
Color.class,
FunctionNameImpl.parameter("featureData1", Double.class),
FunctionNameImpl.parameter("featureData2", Double.class));
public DataFunction(List<Expression> params, Literal fallback) {
this.params = params;
this.fallback = fallback;
}
then your evaluate method becomes:
#Override
public Object evaluate(Object feature) {
Feature f = (Feature) feature;
// fallback definition
float pct = 0.5F;
Expression exp1 = params.get(0);
Expression exp2 = params.get(1);
if (f.getProperty("featureData1") != null) {
MyTable temp = myTableDao.read(
exp1.evaluate(f, Double.class),
exp2.evaluate(f, Double.class));
pct = temp.getColumnValue();
}
Color color = new Color(pct, pct, pct);
return color;
}
and you would use it in the style using something like:
Fill fill =
styleFactory.createFill(
filterFactory.function("DataFunction",
filterFactory.propertyName("featureData1"),
filterFactory.propertyName("featureData2")));
Just starting out hope this isn’t too noob a question
For the game I’m working on I want to have different tabs, the effect of which would be similar to an iOS bottom tab controller as pictured:
Where I’m at a loss is how to create this? Should this all be on one Scene with different layers being turned on and off? Should I be using multiple Scenes?
Seems like keeping this in one scene wouldn’t really scale. How is this normally done, does cocos2d-x have some sort of concept of containers or something?
I would suggest that you have 2 layers:
The first layers will deal with the images. I guess you can use
ImageView. and position them with the size that you want.
The second part is a tab menu that can be implemented with a second
layer in which you will add the buttons and the background. for the
buttons use the Button class.
I hope that helps.
bool ScreenController::init(){
if(Layer::init())
{
footer = Footer::create();
footer->setDelegate(this);
this->addChild(footer,3);
auto homeLayer= HomeView::create(psize,this);
homeLayer->setPosition(Point(0,footer->getBoundingBox().getMaxY()));
// homeLayer->setAllApiFireTo(this);
auto rakeLayer = RakeView::create(psize);
rakeLayer->setPosition(Point(0,footer->getBoundingBox().getMaxY()));
rakeLayer->setDelegate(this);
Tablayers = LayerMultiplex::create();
Tablayers->addLayer(homeLayer);
Tablayers->addLayer(rakeLayer);
this->addChild(Tablayers);
}
}
void ScreenController::TabControllerAction(ViewDisplayType type) {
Tablayers -> switchTo(index);
}
bool Footer::init(){
if(Layer::init())
{
this->setContentSize(Size(constantDeviceSize.width,108*gamescaleY));
// auto btlayer = LayerColor::create(Color4B::RED,this->getContentSize().width,this->getContentSize().height);
//
// this->addChild(btlayer);
SubButton*sb1 = SubButton::create(Size(this->getContentSize().width/5,this->getContentSize().height),"Home.png","Home.png",CC_CALLBACK_2(Footer::callMenuItem, this) );
sb1->setAnchorPoint(Point(0,0));
sb1->setPosition(Point(0,0));
sb1->addTitleText("Home", SFUI_Regular.c_str());
sb1->colorEfact(true);
sb1->setColorbtn(Color3B::WHITE,colorSkyBlue3B);
this->addChild(sb1);
SubButton*sb2 = SubButton::create(Size(this->getContentSize().width/5,this->getContentSize().height),"Balance.png","Balance.png",CC_CALLBACK_2(Footer::callMenuItem, this));
sb2->setAnchorPoint(Point(0,0));
sb2->setPosition(Point(sb2->getBoundingBox().size.width,0));
sb2->addTitleText("Rake", "arial.ttf");
sb2->colorEfact(true);
sb2->setColorbtn(Color3B::WHITE,colorSkyBlue3B);
this->addChild(sb2);
sb1->setDisplayType(myHomeView);
sb2->setDisplayType(myRakeView);
}
void Footer::selectionByController(int tab)
{
auto tmpbar = dynamic_cast<SubButton *>(this -> getChildren().at(tab-1));
callMenuItem(tmpbar, TouchBegan);
}
void Footer::callMenuItem(Ref *sender, TouchEvent event)
{
if(event==TouchBegan)
{
auto sbitem = (SubButton*)sender;
if(event==TouchBegan)
{
sbitem->selected();
if(this->getDelegate())
{
this->getDelegate()->TabControllerAction(sbitem->getDisplayType());/*call to parant*/
}
CCLOG("CLICKED!->%d",sbitem->getTag());
}
for (int i = 0; i < this->getChildren().size(); i++)
{
auto tmpbar = dynamic_cast<SubButton *>(this -> getChildren().at(i));
if (tmpbar != NULL && tmpbar -> getTag() != sbitem -> getTag())
{
tmpbar ->unSelected();
}
}
}
}
When adding a derived column to a data flow with ezAPI, I get the following warnings
"Add stuff here.Inputs[Derived Column Input].Columns[ad_zip]" on "Add
stuff here" has usage type READONLY, but is not referenced by an
expression. Remove the column from the list of available input
columns, or reference it in an expression.
I've tried to delete the input columns, but either the method is not working or I'm doing it wrong:
foreach (Microsoft.SqlServer.Dts.Pipeline.Wrapper.IDTSInputColumn100 col in derFull.Meta.InputCollection[0].InputColumnCollection)
{
Console.WriteLine(col.Name);
derFull.DeleteInputColumn(col.Name);
}
I have the following piece of code that fixes the problem.
I got it from a guy called Daniel Otykier. So he is propably the one that should be credited for it... Unlesss he got it from someone else :-)
static public void RemoveUnusedInputColumns(this EzDerivedColumn component)
{
var usedLineageIds = new HashSet<int>();
// Parse all expressions used in new output columns, to determine which input lineage ID's are being used:
foreach (IDTSOutputColumn100 column in component.GetOutputColumns())
{
AddLineageIdsFromExpression(column.CustomPropertyCollection, usedLineageIds);
}
// Parse all expressions in replaced input columns, to determine which input lineage ID's are being used:
foreach (IDTSInputColumn100 column in component.GetInputColumns())
{
AddLineageIdsFromExpression(column.CustomPropertyCollection, usedLineageIds);
}
var inputColumns = component.GetInputColumns();
// Remove all input columns not used in any expressions:
for (var i = inputColumns.Count - 1; i >= 0; i--)
{
if (!usedLineageIds.Contains(inputColumns[i].LineageID))
{
inputColumns.RemoveObjectByIndex(i);
}
}
}
static private void AddLineageIdsFromExpression(IDTSCustomPropertyCollection100 columnProperties, ICollection<int> lineageIds)
{
int lineageId = 1;
var expressionProperty = columnProperties.Cast<IDTSCustomProperty100>().FirstOrDefault(p => p.Name == "Expression");
if (expressionProperty != null)
{
// Input columns used in expressions are always referenced as "#xxx" where xxx is the integer lineage ID.
var expression = expressionProperty.Value.ToString();
var expressionTokens = expression.Split(new[] { ' ', ',', '(', ')' });
foreach (var c in expressionTokens.Where(t => t.Length > 1 && t.StartsWith("#") && int.TryParse(t.Substring(1), out lineageId)))
{
if (!lineageIds.Contains(lineageId)) lineageIds.Add(lineageId);
}
}
}
Simple but not 100% Guaranteed Method
Call ReinitializeMetaData on the base component that EzApi is extending:
dc.Comp.ReinitializeMetaData();
This doesn't always respect some of the customizations and logic checks that EzAPI has, so test it carefully. For most vanilla components, though, this should work fine.
100% Guaranteed Method But Requires A Strategy For Identifying Columns To Ignore
You can set the UsageType property of those VirtualInputColumns to the enumerated value DTSUsageType.UT_IGNORED using EzApi's SetUsageType wrapper method.
But! You have to do this after you're done modifying any of the other metadata of your component (attaching other components, adding new input or output columns, etc.) since each of these triggers the ReinitializeMetaData method on the component, which automatically sets (or resets) all UT_IGNORED VirtualInputColumn's UsageType to UT_READONLY.
So some sample code:
// define EzSourceComponent with SourceColumnToIgnore output column, SomeConnection for destination
EzDerivedColumn dc = new EzDerivedColumn(this);
dc.AttachTo(EzSourceComponent);
dc.Name = "Errors, Go Away";
dc.InsertOutputColumn("NewDerivedColumn");
dc.Expression["NewDerivedColumn"] = "I was inserted!";
// Right here, UsageType is UT_READONLY
Console.WriteLine(dc.VirtualInputCol("SourceColumnToIgnore").UsageType.ToString());
EzOleDbDestination d = new EzOleDbDestination(f);
d.Name = "Destination";
d.Connection = SomeConnection;
d.Table = "dbo.DestinationTable";
d.AccessMode = AccessMode.AM_OPENROWSET_FASTLOAD;
d.AttachTo(dc);
// Now we can set usage type on columns to remove them from the available inputs.
// Note the false boolean at the end.
// That's required to not trigger ReinitializeMetadata for usage type changes.
dc.SetUsageType(0, "SourceColumnToIgnore", DTSUsageType.UT_IGNORED, false);
// Now UsageType is UT_IGNORED and if you saved the package and viewed it,
// you'll see this column has been removed from the available input columns
// ... and the warning for it has gone away!
Console.WriteLine(dc.VirtualInputCol("SourceColumnToIgnore").UsageType.ToString());
I was having exactly your problem and found a way to solve it. The problem is that the EzDerivedColumn has not the PassThrough defined in it's class.
You just need to add this to the class:
private PassThroughIndexer m_passThrough;
public PassThroughIndexer PassThrough
{
get
{
if (m_passThrough == null)
m_passThrough = new PassThroughIndexer(this);
return m_passThrough;
}
}
And alter the ReinitializeMetadataNoCast() to this:
public override void ReinitializeMetaDataNoCast()
{
try
{
if (Meta.InputCollection[0].InputColumnCollection.Count == 0)
{
base.ReinitializeMetaDataNoCast();
LinkAllInputsToOutputs();
return;
}
Dictionary<string, bool> cols = new Dictionary<string, bool>();
foreach (IDTSInputColumn100 c in Meta.InputCollection[0].InputColumnCollection)
cols.Add(c.Name, PassThrough[c.Name]);
base.ReinitializeMetaDataNoCast();
foreach (IDTSInputColumn100 c in Meta.InputCollection[0].InputColumnCollection)
{
if (cols.ContainsKey(c.Name))
SetUsageType(0, c.Name, cols[c.Name] ? DTSUsageType.UT_READONLY : DTSUsageType.UT_IGNORED, false);
else
SetUsageType(0, c.Name, DTSUsageType.UT_IGNORED, false);
}
}
catch { }
}
That is the strategy used by other components. If you want to see all the code you can check my EzApi2016#GitHub. I'm updating the original code from Microsoft to SQL Server 2016.
I'm trying to get an intro screen in my game. After a few seconds it should change to the game itself.
In the Init of my introscreen class, I try to make a sequence, but I'm getting the following error:
cannot cast from type void to member pointer type
'cocos2d::SEL_CallFuncN' (aka
'void(cocos2d::CCObject::*)(cocos2d::CCNode *)'
here's my code:
bool IntroScreen::init() {
if (!CCLayer::init()) {
return false;
}
cocos2d::CCSize screenSize = cocos2d::CCDirector::sharedDirector()->getWinSize();
cocos2d::CCSprite* logomarca = cocos2d::CCSprite::create("rayman_intro.png");
logomarca->setPosition(ccp(screenSize.width / 2, screenSize.height / 2));
this->addChild(logomarca, 1);
cocos2d::CCFiniteTimeAction *seq1 = cocos2d::CCSequence::create(cocos2d::CCDelayTime::create(3.0),
cocos2d::CCCallFuncN::create(this,
cocos2d::SEL_CallFuncN(cocos2d::CCDirector::sharedDirector()->replaceScene(Controller::scene())), NULL);
this->runAction(seq1);
return true;
}
What am I doing wrong?
You can use scheduler here instead of CCFiniteTimeAction like this..
this->scheduleOnce(schedule_selector(IntroScreen::replace), 3);
void IntroScreen::replace(CCObject* sender){
cocos2d::CCDirector::sharedDirector()->replaceScene(Controller::scene();
}
Also insert a statement "using namespace cocos2d;" at the top to avoid writing cocos2d:: everywhere.
in SEL_CallFuncN you should put pointer to function not the function
For example
cocos2d::SEL_CallFuncN(IntroScreen::replace)
In replace put the code you want to invoke, if you use CallFuncN your method should have in first parametr place for object, which run the function
void IntroScreen::replace(CCObject* sender){
cocos2d::CCDirector::sharedDirector()->replaceScene(Controller::scene();
}