Skip to content

Instantly share code, notes, and snippets.

@slluis
Last active March 10, 2017 17:30
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save slluis/444390820ca1d10d8af5 to your computer and use it in GitHub Desktop.
Save slluis/444390820ca1d10d8af5 to your computer and use it in GitHub Desktop.
//
// AndroidDesignerView.cs
//
// Author:
// Lluis Sanchez <lluis@xamarin.com>
//
// Copyright (c) 2011 Xamarin Inc
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Reflection;
using System.Xml.Linq;
using MonoDevelop.Components.Commands;
using MonoDevelop.Core;
using MonoDevelop.DesignerSupport;
using MonoDevelop.DesignerSupport.Toolbox;
using MonoDevelop.Ide;
using MonoDevelop.Ide.Commands;
using MonoDevelop.Ide.Gui;
using MonoDevelop.Projects;
using Xamarin.AndroidDesigner;
using Xamarin.AndroidDesigner.PropertyEditing;
using Xamarin.Designer;
using Xwt;
using Xwt.Backends;
using Xwt.Design;
using Xwt.GtkBackend;
using System.IO;
using MonoDevelop.SourceEditor;
using MonoDevelop.Ide.Gui.Content;
using Xwt.Drawing;
using Xamarin.AndroidTools;
using MonoDevelop.MonoDroid.Designer;
using Microsoft.CodeAnalysis;
using ICSharpCode.NRefactory6.CSharp;
using MonoDevelop.Ide.Editor;
namespace MonoDevelop.MonoDroid.Designer
{
public class AndroidDesignerView: AbstractViewContent, /*IPropertyPadProvider,*/
ICustomPropertyPadProvider, IToolboxDynamicProvider, IToolboxConsumer, IUndoHandler, IOutlinedDocument,
IClipboardHandler, IZoomable, IToolboxCustomizer, IPropertyPadCustomizer
{
AndroidDesignerSession session;
DesignerWidget ds;
LayoutSourceEditorView editorView;
LayoutSourceEditorView EditorView {
get{
if (editorView == null) {
editorView = WorkbenchWindow.SubViewContents.OfType<LayoutSourceEditorView> ().First ();
}
return editorView;
}
}
MonoDroidProjectFlavor project;
AndroidProjectWrapper projectWrapper;
static bool designerServiceInitialized;
static Gtk.TargetEntry[] dragTargets;
static AndroidDesignerView ()
{
// HACK: required by .NET for properly resolving the Xamarin.Designer types when deserializing
AppDomain.CurrentDomain.AssemblyResolve += (o, args) => {
if (args.Name.StartsWith ("Xamarin.Designer,"))
return typeof(Xamarin.Designer.DesignerItem).Assembly;
if (args.Name.StartsWith ("Xwt,"))
return typeof(Xwt.Widget).Assembly;
return null;
};
DesignerService.NativeToolkit = DesktopService.NativeToolkit;
}
public AndroidDesignerSession Session {
get { return session; }
}
public override bool CanReuseView (string fileName)
{
FilePath p1 = ContentName, p2 = fileName;
if (p1.FileName == p2.FileName && p1.ParentDirectory.ParentDirectory == p2.ParentDirectory.ParentDirectory && (p2.ParentDirectory.FileName.Equals ("layout", StringComparison.InvariantCultureIgnoreCase) || p2.ParentDirectory.FileName.StartsWith ("layout-", StringComparison.InvariantCultureIgnoreCase))) {
WorkbenchWindow.SwitchView (0);
ds.SetLayoutSelection (fileName);
return true;
} else
return false;
}
public override void Load (FileOpenInformation fileOpenInformation)
{
if (!designerServiceInitialized || !AndroidDesignerService.SdkIsValid) {
AndroidDesignerService.Initialize (AndroidSdk.AndroidSdkPath, AndroidSdk.JavaSdkPath,
UserProfile.Current.ConfigDir.Combine ("MonoForAndroid"));
if (!designerServiceInitialized) {
designerServiceInitialized = true;
IdeApp.Exited += delegate {
AndroidDesignerService.Shutdown ();
};
if (IdeApp.Workbench != null)
IdeApp.Workbench.ActiveDocumentChanged += OnActiveDocumentChanged;
}
}
project = Project.GetService<MonoDroidProjectFlavor> ();
if (project == null) {
var p = IdeApp.Workspace.GetProjectsContainingFile (fileOpenInformation.FileName).FirstOrDefault ();
if (p != null)
project = p.GetService<MonoDroidProjectFlavor> ();
}
DotNetProject dnp = (DotNetProject)Project;
projectWrapper = (AndroidProjectWrapper) project.Project.ExtendedProperties [typeof(AndroidProjectWrapper)];
DesignerProject dp;
if (projectWrapper == null) {
var resPrefixes = project.MonoDroidResourcePrefixes;
// TODO: processed aapt directory is computed by hardcoding path, when we have MSBuild support this should be instead retrieved through $(MonoAndroidResDirIntermediate)
dp = AndroidDesignerService.LoadProject (new IdeDesignerProject (dnp),
project.Project.BaseIntermediateOutputPath.Combine ((FilePath)project.Project.DefaultConfigurationId, (FilePath)"res"),
project.Project.BaseDirectory.Combine (resPrefixes.Any () ? resPrefixes.First () : (FilePath)"Resources"));
projectWrapper = new AndroidProjectWrapper (project, dp);
Project.ExtendedProperties [typeof(AndroidProjectWrapper)] = projectWrapper;
} else
dp = projectWrapper.DesignerProject;
session = dp.CreateSession ();
projectWrapper.UpdateApplicationInfo ();
// Initialize the target version. It initially matches the target version of the project
session.AndroidTarget = AndroidDesignerService.FindBestTarget (project.AndroidTargetFrameworkVersion);
session.LoadFile (fileOpenInformation.FileName);
#if MAC
var surface = Xamarin.Designer.DesignerSurface.ShouldUseNativeSurface ? new Xamarin.AndroidDesigner.Mac.NativeAndroidDesignerSurface () : null;
ds = new DesignerWidget (surface);
#else
ds = new DesignerWidget ();
#endif
ds.BoundsChanged += HandleDesignerWidgetBoundsSet;
session.ModifiedChanged += HandleSessionModifiedChanged;
project.Project.Saved += HandleProjectSaved;
ContentName = fileOpenInformation.FileName;
}
void HandleDocumentOutlinePadActivated (object sender, EventArgs e)
{
IdeApp.Workbench.Pads.DocumentOutlinePad.BringToFront (true);
}
void HandleDesignerWidgetBoundsSet (object sender, EventArgs e)
{
ds.BoundsChanged -= HandleDesignerWidgetBoundsSet;
ds.Session = session;
ds.PropertyPadActivated += HandlePropertyPadActivated;
ds.DocumentOutlinePadActivated += HandleDocumentOutlinePadActivated;
}
void HandlePropertyPadActivated (object sender, EventArgs e)
{
IdeApp.Workbench.Pads.PropertyPad.BringToFront (true);
}
protected override void OnWorkbenchWindowChanged (EventArgs e)
{
base.OnWorkbenchWindowChanged (e);
if (WorkbenchWindow != null) {
var toolbar = ds.ExtractToolbar ();
var docToolbar = WorkbenchWindow.GetToolbar (this);
<<<<<<< HEAD
docToolbar.Add ((Gtk.Widget)Xwt.Toolkit.CurrentEngine.GetNativeWidget (toolbar), true, 5);
=======
docToolbar.Add ((Gtk.Widget)Xwt.Toolkit.CurrentEngine.GetNativeWidget (toolbar), true, 12);
>>>>>>> origin/master
var al = typeof(DocumentToolbar)
.GetProperty ("Container", BindingFlags.Instance | BindingFlags.NonPublic)
.GetValue (docToolbar) as Gtk.Alignment;
if (al != null) {
al.LeftPadding = al.RightPadding = al.TopPadding = 0;
<<<<<<< HEAD
al.BottomPadding = 4;
=======
al.BottomPadding = 2;
>>>>>>> origin/master
}
}
}
void HandleProjectSaved (object sender, SolutionItemEventArgs e)
{
var target = AndroidDesignerService.FindBestTarget (project.AndroidTargetFrameworkVersion);
if (target != session.AndroidTarget) {
if (projectWrapper != null)
projectWrapper.UpdateApplicationInfo ();
session.AndroidTarget = target;
}
}
static string lastLayout;
static void OnActiveDocumentChanged (object s, EventArgs a)
{
AndroidDesignerView current;
if (IdeApp.Workbench.ActiveDocument == null || (current = IdeApp.Workbench.ActiveDocument.GetContent<AndroidDesignerView> ()) == null) {
if (lastLayout != null && IdeApp.Workbench.CurrentLayout == "Visual Design")
IdeApp.Workbench.CurrentLayout = lastLayout;
lastLayout = null;
} else {
if (IdeApp.Workbench.CurrentLayout != "Visual Design") {
if (lastLayout == null) {
lastLayout = IdeApp.Workbench.CurrentLayout;
IdeApp.Workbench.CurrentLayout = "Visual Design";
}
}
current.ds.SetSurfaceFocus ();
}
}
public override object GetContent (Type type)
{
var baseResult = base.GetContent (type);
if (baseResult == null && EditorView != null)
return EditorView.GetContent (type);
return baseResult;
}
void HandleSessionModifiedChanged (object sender, EventArgs e)
{
if (session != null)
IsDirty = session.IsModified;
}
public override bool IsFile {
get {
// This view can't be handled as a file since it handles several
// layout files at once
return false;
}
}
public override void Save ()
{
if (EditorView.IsSelected) {
EditorView.SaveContent ();
EditorView.SaveCheckpoint ();
}
session.SaveAll ();
IsDirty = false;
}
public override void Save (FileSaveInformation fileSaveInformation)
{
session.SaveAll ();
IsDirty = false;
}
public override void Dispose ()
{
Project.Saved -= HandleProjectSaved;
base.Dispose ();
session.Dispose ();
ds.Dispose ();
session = null;
}
Gtk.Widget ww;
public override Gtk.Widget Control {
get {
if (ww != null)
return ww;
Gtk.VBox b = new Gtk.VBox ();
b.ShowAll ();
Gtk.Widget w = (Gtk.Widget)Xwt.Toolkit.CurrentEngine.GetNativeWidget (ds);
b.PackStart (w, true, true, 0);
return ww = b;
}
}
public override string TabPageLabel {
get {
return "Designer";
}
}
#region IPropertyPadProvider implementation
public object GetActiveComponent ()
{
return ds.SelectedItem;
}
public object GetProvider ()
{
return ds.SelectedItem;
}
public void OnEndEditing (object obj)
{
}
public void OnChanged (object obj)
{
}
#endregion
#region IToolboxDynamicProvider implementation
public event EventHandler ItemsChanged;
public IEnumerable<ItemToolboxNode> GetDynamicItems (IToolboxConsumer consumer)
{
if (session.AndroidTarget != null) {
foreach (var cdv in session.AndroidTarget.GetComponents ().SelectMany (c => c.Views) ) {
var item = new AndroidWidgetToolboxNode { Descriptor = cdv };
item.Category = cdv.ClassDescriptor.Category;
item.Name = cdv.Name;
item.Description = cdv.ClassDescriptor.Description;
item.Icon = cdv.Icon;
yield return item;
}
}
}
#endregion
#region IToolboxConsumer implementation
public void ConsumeItem (ItemToolboxNode item)
{
}
DesignerDragData dragData;
public void DragItem (ItemToolboxNode item, Gtk.Widget source, Gdk.DragContext ctx)
{
AndroidWidgetToolboxNode ait = (AndroidWidgetToolboxNode)item;
dragData = ait.Descriptor.GetDragData ();
source.DragDataGet += HandleDragDataGet;
Image img = ait.Descriptor.GetDragImage (session);
Gtk.Drag.SetIconPixbuf (ctx, (Gdk.Pixbuf) Toolkit.CurrentEngine.GetNativeImage (img), 0, 0);
}
void HandleDragDataGet (object o, Gtk.DragDataGetArgs args)
{
TransferDataSource data = new TransferDataSource ();
data.AddValue (dragData);
Xwt.GtkBackend.Util.SetDragData (data, args);
}
public bool CustomFilterSupports (ItemToolboxNode item)
{
return item is AndroidWidgetToolboxNode;
}
public Gtk.TargetEntry[] DragTargets {
get {
if (dragTargets == null)
dragTargets = (Gtk.TargetEntry[]) Xwt.GtkBackend.Util.BuildTargetTable (new TransferDataType[] { Xwt.TransferDataType.FromType (typeof(DesignerDragData)) });
return dragTargets;
}
}
public ToolboxItemFilterAttribute[] ToolboxFilterAttributes {
get {
return new ToolboxItemFilterAttribute [0];
}
}
public string DefaultItemDomain {
get {
return "";
}
}
#endregion
#region ICustomPropertyPadProvider implementation
public Gtk.Widget GetCustomPropertyWidget ()
{
AndroidDesignerPropertyPad.Instance.SetSource (this, ds);
return AndroidDesignerPropertyPad.Instance;
}
public void DisposeCustomPropertyWidget ()
{
}
#endregion
#region IUndoHandler implementation
public void Undo ()
{
ds.Undo ();
}
public void Redo ()
{
ds.Redo ();
}
public IDisposable OpenUndoGroup ()
{
return null;
}
public bool EnableUndo {
get {
return ds.CanUndo ();
}
}
public bool EnableRedo {
get {
return ds.CanRedo ();
}
}
#endregion
#region IOutlinedDocument implementation
public Gtk.Widget GetOutlineWidget ()
{
AndroidDesignerOutlinePad.Instance.SetSource (this, ds);
return AndroidDesignerOutlinePad.Instance;
}
public IEnumerable<Gtk.Widget> GetToolbarWidgets ()
{
yield break;
}
public void ReleaseOutlineWidget ()
{
AndroidDesignerOutlinePad.Instance.SetSource (null, null);
}
#endregion
#region IClipboardHandler implementation
public void Cut ()
{
ds.CutSelection ();
}
public void Copy ()
{
ds.CopySelection ();
}
public void Paste ()
{
ds.PasteToSelection ();
}
public void Delete ()
{
ds.DeleteSelection ();
}
public void SelectAll ()
{
}
public bool EnableCut {
get {
return ds.CanCutSelection ();
}
}
public bool EnableCopy {
get {
return ds.CanCopySelection ();
}
}
public bool EnablePaste {
get {
return ds.CanPasteToSelection ();
}
}
public bool EnableDelete {
get {
return ds.CanDeleteSelection ();
}
}
public bool EnableSelectAll {
get {
return false;
}
}
#endregion
#region IZoomable implementation
public void ZoomIn ()
{
ds.ZoomIn ();
}
public void ZoomOut ()
{
ds.ZoomOut ();
}
public void ZoomReset ()
{
ds.ZoomFull ();
}
public bool EnableZoomIn {
get {
return ds.CanZoomIn ();
}
}
public bool EnableZoomOut {
get {
return ds.CanZoomOut ();
}
}
public bool EnableZoomReset {
get {
return ds.CanZoomFull ();
}
}
#endregion
#region IToolboxCustomizer implementation
void IToolboxCustomizer.Customize (IPadWindow padWindow, IToolboxConfiguration config)
{
config.AllowEditingComponents = false;
if (session.AndroidTarget != null) {
var cats = session.AndroidTarget.WidgetCategories;
for (int n=0; n < cats.Length; n++)
config.SetCategoryPriority (cats[n], cats.Length - n);
}
}
#endregion
#region IPropertyPadCustomizer implementation
void IPropertyPadCustomizer.Customize (IPadWindow padWindow, MonoDevelop.Components.PropertyGrid.PropertyGrid propertyGrid)
{
var toolbar = padWindow.GetToolbar (Gtk.PositionType.Top);
foreach (var b in AndroidDesignerPropertyPad.Instance.ToolbarButtons)
toolbar.Add (b);
}
#endregion
}
class AndroidWidgetToolboxNode: ItemToolboxNode
{
public ClassDescriptorView Descriptor;
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment