Mostrando las entradas con la etiqueta Mac. Mostrar todas las entradas
Mostrando las entradas con la etiqueta Mac. Mostrar todas las entradas

2011-01-08

Nuevo chiche y una mejor forma de ver la Internet

Buenas! Este post tiene dos partes:

  1. Tengo chiche nuevo!
    Cambié la MacBook Pro de 13 pulgadas que tenía por la nueva MacBook Air de 11. Por ahora muy contento, así que después de darle un poco mas de uso escribiré alguna mini-reseña para compradores indecisos.
  2. Dejo acá una nueva actualización para el archivo de hosts de sus computadores, para que puedan evitar todo tipo de publicidad dentro de los sitios web que visiten, independientemente de la plataforma o browser que usen.
    Para el que no sepa mucho de que estoy hablando, peguen las lineas que dejo a continuación en el archivo /etc/hosts en OS X o cualquier variante de Unix, Linux, BSD o similar, o en C:\Windows\System32\Drivers\etc\hosts en cualquier versión de Windows.

Archivo de hosts:

127.0.0.1 ads.e-planning.net
127.0.0.1 codenew.impresionesweb.com
127.0.0.1 ads.doubleclick.net
127.0.0.1 pubads.g.doubleclick.net
127.0.0.1 googleads.g.doubleclick.net
127.0.0.1 pagead2.googlesyndication.com
127.0.0.1 api.ad.ly
127.0.0.1 api.140proof.com
127.0.0.1 api.ads.oneriot.com
127.0.0.1 ds.clickexperts.net
127.0.0.1 ad.metanetwork.com
127.0.0.1 d2.zedo.com

Hasta la próxima!
Zaiden


2009-07-14

Monos y Manzanas: Key Value Coding y Configuración

Hacia el final de la entrega anterior de Monos y Manzanas estuve contando sobre como utilizar los bindings de Cocoa con Monobjc. En el capítulo final de esta serie de posts veremos como es posible agilizar la implementación de clases que utilicen Key Value Coding (KVC de ahora en mas) desde .NET y como agregar un panel de preferencias para modificar la configuración de la aplicación y que la misma sea persistida de forma automática.

Ante todo, fe de erratas: una persona del equipo de MonobjC con la cual me contacté, me acalró que no es necesario crear una instancia que nadie usa de NSAutoReleasePool en el bootstrapping de la aplicación, ya que eso se hace automáticamente.

En el último ejemplo mostrado, cada vez que se quería implementar una propiedad que pudiese ser utilizada para los bindings con KVC era necesario crear los setters y getters, indicarles que iban a ser llamados como mensajes de ObjectiveC y llamar a las notificaciones de cambios de propiedad.
Para no tener que hacer esto por cada propiedad que queramos exponer al runtime de ObjectiveC, crearemos un nuevo atributo el cual indique que esa propiedad podrá ser utilizada con KVC:

using System;

namespace zPod.Objc
{
public class KeyValueCodingAttribute : Attribute
{
public KeyValueCodingAttribute (string propertyName)
{
PropertyName = propertyName;
}

public string PropertyName
{
get;
set;
}
}
}

Teniendo esto, las propiedades que querramos exponer se verán así:


[KeyValueCoding("modelo")]
public BrowserModel Modelo
{
get;
set;
}

Muy lindo hasta ahora pero... ¿Como le indicamos al runtime de ObjectiveC que deberá tomar la propiedad "modelo" al intentar llamar al mensaje "modelo" o "setModelo:"? Simple, sobreescribiendo el llamado a los mensajes "setValue:forUndefinedKey:" y "valueForUndefinedKey"


[ObjectiveCMessage("setValue:forUndefinedKey:")]
public override void SetValueForUndefinedKey (Id val, NSString key)
{
this.ResolveSetValueForUndefinedKey(val, key);
}

[ObjectiveCMessage("valueForUndefinedKey:")]
public override Id ValueForUndefinedKey (NSString key)
{
return this.ResolveValueForUndefinedKey(key);
}

Al no encontrar una clave definida, Cocoa avisa a la clase que se intento leer o dar valor a la clave utilizando estos mensajes.


En estas implementaciones se está llamando a los métodos ResolveSetValueForUndefinedKey y ResolveValueForUndefinedKey. Los mismos son extension methods creados para la clase NSObject que resuelven cual es la propiedad que deberá ser devuelta o modificada utilizando reflection para saber si tienen el atributo definido anteriormente. El código de estos métodos se incluye dentro de los fuentes de este ejemplo.




Hacia el final de la entrega anterior de Monos y Manzanas comenté que Cocoa proveía un controller especial llamado NSUserDefaultsController que permite guardar automáticamente las preferencias del usuario.
Ahora vamos a ver como crear un nuevo panel de preferencias donde configurar la URL Home de nuestro browser.


Volviendo al Interface Builder, agregamos un nuevo NSPanel y le agregamos un TextField para ingresar la URL Home


Picture 1


Para poder abrir el panel desde el menu de preferencias, conectamos la opción Preferences... del menú con el panel creado, asociándolo a la acción makeKeyAndOrderFront:.


Picture 3


Dado que se necesita únicamente una instancia de este panel, lo configuramos como se muestra en la siguiente imagen. Lo mas importante es desmascar las opciones Released When Closed y Visible at launch para que no se muestre al incio, pero que al cerrar la ventana solamente se oculte y no se destruya el objeto.


Picture 2


Creamos el binding para el value del TextField, conectándolo con la instancia de SharedUserDefaultsController, utilizando la clave values y el model key path homePage.
Al utilizar este controller, los valores conectados serán persistidos automáticamente.


Picture 4


Como último paso dentro del Interface Builder, agregamos un nuevo boton para ir a la home, agregamos una nueva acción llamada goHome: al BrowserController y los asociamos de la misma manera que el boton Go.


Picture 5


Volviendo a C#, Dentro del código del BrowserController, modificamos el método AwakeFromNib y agregamos el método que responda al mensaje goHome:, para que al iniciar la aplicación vaya a la URL definida como home y, en caso de no haber nada, definirle un valor default (recuenden que en _userDefaults tenemos guardada una instancia de SharedUserDefaultsController):


[ObjectiveCMessage("goHome:")]
public void GoHome(Id sender)
{
NSString home = _userDefaults.ValueForKeyPath("values.homePage");
this.Modelo.WillChangeValueForKey("url");
this.Modelo.Url = home;
this.Modelo.DidChangeValueForKey("url");
Browse(sender);
}

[ObjectiveCMessage("browse:")]
public void Browse(Id sender)
{
NSString url = this.Modelo.Url;
browser.MainFrameURL = url;
_userDefaults.SetValueForKeyPath("CocoaBrowser - " + url, "values.title");
}

[ObjectiveCMessage("awakeFromNib")]
public void AwakeFromNib ()
{
NSString home = _userDefaults.ValueForKeyPath("values.homePage");
if (home == null)
{
_userDefaults.SetValueForKeyPath(new NSString("http://zPod.com.ar"), "values.homePage");
}

GoHome(null);
}

Listo! Ahora a compilar y a disfrutar de su nuevo browser con botón de Home para Mac hecho en .NET!


Picture 6


Con esto queda finalizada la serie de posts: Monos y Manzanas. El código completo lo pueden bajar de aca.



Hasta la próxima!
Zaiden

2009-07-12

Monos y Manzanas: Binding entre la UI y el Modelo

En el último post expliqué como hacer una aplicación sencilla para Mac cuya UI estaba hecha con Interface Builder, y el resto de la plomería de fondo utilizaba C#, gracias a Mono y el bridge Monobjc.

Dada la extrema simplicidad de nuestro CocoaBrowser, hubieron algunos conceptos que escaparon al ejemplo inicial.

En esta nueva entrega de Monos y Manzanas veremos como hacer uso del binding provisto por Cocoa y el Interface Builder para conectar nuestra interfaz gráfica al Modelo de nuestra aplicación.

Por ahora, definiremos una nueva clase BrowserModel, la cual representará el “modelo” de nuestra apliación de juguete.

public class BrowserModel
{
private string _url;
public string GetUrl()
{
return _url;
}

public void SetUrl(string newUrl)
{
_url = newUrl;
}
}


Posiblemente se pregunten: Por qué la propiedad esta definida con metodos Get y Set, en vez se ser una propiedad “normal” de C#, al estilo “public string Url { get; set'; }”?

Es por un requerimiento del bridge con Objective-C y el binding con la UI, pero vamos a verlo bien mas adelante.


La idea es que lo que usuario tipee dentro del TextField utilizado para ingresar la URL de nuestro browser actualice automáticamente nuestro modelo y, en caso de actualizar el modelo desde otro lugar, se actualice automáticamente el TextField.


Para poder hacer esto, agregaremos un nuevo campo a la clase BrowserController que nos de acceso al modelo, y lo haremos visible para el bridge, con los siguientes getter y setter:


[ObjectiveCClass]
public class BrowserController : NSObject
{
//
// Mucho codigo antes...
//

private BrowserModel _modelo = new BrowserModel();

[ObjectiveCMessage("modelo")]
public BrowserModel getModelo()
{
return _modelo;
}

[ObjectiveCMessage("setModelo:")]
public void setModelo(BrowserModel nuevoModelo)
{
WillChangeValueForKey("modelo");
_modelo = nuevoModelo;
DidChangeValueForKey("modelo");
}

//
// Mucho codigo despues...
//

}

Esta forma de crear los campos permite a ObjectiveC utilizar la clase con algo conocido como Key Value Coding, accediendo a ciertas propiedades de nuestra clase como si fuesen pares Clave - Valor, tanto para leer como para escribir.

Los métodos WillChangeValueForKey y DidChangeValueForKey permiten avisarle a la UI que hubo un cambio en el valor la propiedad.


Para que las propiedades de la clase BrowserModel sean accesibles de la misma manera, las decoramos de forma similar, quedando como resultado:


[ObjectiveCClass]
public class BrowserModel : NSObject
{
// Constructores requeridos por NSObject
public BrowserModel()
{
}

public BrowserModel (IntPtr nativePointer) : base(nativePointer)
{
}

private string _url;

[ObjectiveCMessage("url")]
public string GetUrl()
{
return _url;
}

[ObjectiveCMessage("setUrl:")]
public void SetUrl(string newUrl)
{
WillChangeValueForKey("url");
_url = newUrl;
DidChangeValueForKey("url");
}
}

Como pueden ver, es requisito agregar el atributo [ObjectiveCClass] y hacer heredar la clase de NSObject (o algún hijo) para que la misma sea visible desde ObjectiveC. Al hacer esto, deberán agregar los dos constructores indicados en el código.


Dado que ahora el URL a navegar será indicado por las propiedades del modelo, podemos sacar la referencia al TextField del BrowserController y modificar el metodo Browse de la siguiente manera:


[ObjectiveCClass]
public class BrowserController : NSObject
{
//
// Mucho codigo antes...
//

[ObjectiveCMessage("browse:")]
public void Browse(Id sender)
{
string url = this.fModelo.getUrl();
browser.MainFrameURL = url;
}

//
// Mucho codigo despues...
//

}

Con esto listo, solo resta hacer los bindings propiamente dichos en el Interface Builder.


El primer paso será agregar un ObjectController desde la Library. Este tipo de controller nos permite hacer binding facilmente a un objeto concreto del modelo. En caso de querer hacer binding contra Arrays, Diccionarios y otros tipos de datos, existen controllers mas apropiados.


Picture 1


Dentro de los atributos del ObjectController, agregar una nueva clave llamada “url”, la cual usaremos como nexo entre el modelo y la UI.


Picture 2


Dentro de los bindings del ObjectController, indicamos que el Controller Content debe hacer binding con el BrowserController, utilizando como Model Key Path “modelo”. Esto hará referencia a la propiedad modelo que creamos – e hicimos visible con setter y getter para ObjectiveC – dentro de la clase creada en C#.


Picture 3


Finalmente, seleccionamos el TextField de la URL y dentro de la solapa de bindings indicamos que se deberá hacer el binding con el ObjectController, utilizando “selection” como Controller Key y “url” como Model Key Path. Para que el modelo se actualice cada vez que se modifica el TextField y no únicamente al terminar de editarlo cambiando el foco, deberemos marcar el check de “Continuosly Update Value”.


Picture 4


Como yapa, en esta versión de los fuentes, podrán ver como hacer binding contra una clase especial llamada NSUserDefaultsController, la cual nos permite mantener guardadas las preferencias del usuario haciendo binding contra las mismas. En este ejemplo se utiliza para guardar el titulo de la ventana de la aplicación, aunque su utilidad mas común es dentro de los paneles de preferencias.


El código lo pueden bajar de aca.


Hasta la próxima!
Zaiden

2008-06-10

zPod Reloaded: Vuelta al blog, desde el otro lado de La Fuerza!

Si... ya se. No posteo nada desde hace 2 meses.
La verdad es que intenté postear algo, pero no se me ocurrió nada piola...

Ahora estoy oficialmente del lado de la luz: tengo una MacBook :) Por lo que mis problemas configurando y tuneando drivers locos hechos por gente loca de internet se acabaron (por lo menos por un tiempo... mi viejo quiere probar ponerse OS X en su notebook!)

Actualmente estoy laburando en un proyecto que exprime a fondo los features nuevos de C# 3, asi que si hacemos algo realmente copado, veré de postearlo. 

Ya que no postié nada que valga la pena, les dejo dos videos que muestran cuan duro es trabajar en Lagash :)




Z