Archive

Posts Tagged ‘UI’

Fix: MacOS-X mousewheel scroll like on Windows

If you’re a long-time Windows user you may get puzzled when you connect a mouse to your MacBook Air and realize that the mouse wheel is scrolling documents and webpages in the reverse direction than it does on Windows.

Luckily, this is controlled by an option in Settings. Pick Mouse on the lefthand bar and then you should see a “Natural scroll” option claiming that the “natural” is to follow the fingers movement (as if you were panning with two fingers on a touchpad that is).

Note: screenshots below are in Greek, but should be easy to follow the icons

Well, not that natural since the mouse wheel keeps on rolling without your fingers going much down as they do on a touchpad, making you feel very strangely, esp if you’re coming from the Windows world, or you’re switching often between operating systems.

Just turn off that toggle switch and you’re ready to scroll again like a king!

HowTo: show a confirmation dialog with Delphi FireMonkey UI library

Here’s my answer at https://stackoverflow.com/a/71728787/903783 on how to show a confirmation dialog with Delphi’s cross-platform FireMonkey UI library.

Based on other answers, I’ve added a “Confirm” class function (adapted to return a Boolean) to TApplicationHelper (which also has an untested function to get the executable filename), currently maintained under the READCOM_App repository.

Update: had to modify the answer to use a callback so that it also works on platforms where FMX supports only asynchronous dialogs, like Android

unit Zoomicon.Helpers.FMX.Forms.ApplicationHelper;

interface
uses
  FMX.Forms; //for TApplication

type
  TApplicationHelper = class helper for TApplication
  public
    function ExeName: String;
    class function Confirm(Prompt: String): Boolean;
  end;

implementation
  uses
    {$IFDEF ANDROID}
    System.SysUtils, //TODO: is this needed?
    Androidapi.Helpers, //for TAndroidHelper, JStringToString
    Androidapi.Jni.JavaTypes, //to avoid "H2443 Inline function 'JStringToString' has not been expanded"
    {$ENDIF}
    System.UITypes, //for TMsgDlgType
    FMX.DialogService, //for TDialogService
    FMX.Dialogs; //for mbYesNo

{$region 'TApplicationHelper'}

//from https://gist.github.com/freeonterminate/2f7b2e29e40fa30ed3c4
function TApplicationHelper.ExeName: String;
begin
  Result := ParamStr(0);

  {$IFDEF ANDROID}
  if (Result.IsEmpty) then
    Result := JStringToString(TAndroidHelper.Context.getPackageCodePath);
  {$ENDIF}
end;

//based on https://stackoverflow.com/questions/42852945/delphi-correctly-displaying-a-message-dialog-in-firemonkey-and-returning-the-m
class procedure TApplicationHelper.Confirm(const Prompt: String; const SetConfirmationResult: TProc<Boolean>);
begin
  with TDialogService do
  begin
    PreferredMode := TPreferredMode.Platform;
    MessageDialog(Prompt, TMsgDlgType.mtConfirmation, mbYesNo, TMsgDlgBtn.mbNo, 0,
      procedure(const AResult: TModalResult)
      begin
        //Note: assuming this is executed on the main/UI thread later on, so we just call the "SetResult" callback procedure passing it the dialog result value
        case AResult of
          mrYes: SetConfirmationResult(true);
          mrNo: SetConfirmationResult(false);
        end;
      end
    );
  end;
end;

{$endregion}

end.

usage example is in the MainForm of READCOM_App

procedure TMainForm.HUDactionNewExecute(Sender: TObject);
begin
  TApplication.Confirm(MSG_CONFIRM_CLEAR_STORY, //confirmation done only at the action level //Note: could also use Application.Confirm since Confirm is defined as a class function in ApplicationHelper (and those can be called on object instances of the respective class too)
    procedure(Confirmed: Boolean)
    begin
      if Confirmed and (not LoadDefaultDocument) then
        NewRootStoryItem;
    end
  );
end;

where MSG_CONFIRM_CLEAR_STORY is declared as

resourcestring
  MSG_CONFIRM_CLEAR_STORY = 'Clearing story: are you sure?';

Fix: make ownCloud installer display in English language

OwnCloud is an interesting solution for setting up a file sharing cloud for a group of people.

However,one issue I’ve found with its Windows desktop client’s current version (which looks clean of any viruses since I always check first) is that if your Windows 10 is configured with a preferred language that the desktop client’s installer doesn’t have localization support for, then it doesn’t show up in English as you’d expect, but in Czech or someother language that most of us don’t know how to read.

Screenshot (493)

So I tried running it’s MSI installer (ownCloud-2.6.1.13407.13049.msi) with –? parameter from the command-line and the /g languageCode parameter mentioned there looked promising, but trying /g en for English didn’t work. I guessed it needed some specific language code number (and not double-letter language code like en for English), since the help text was mentioning to see Windows Installer SDK for more help.

After a quick search I found an article that suggested passing the parameter Productlanguage=1033 to an msi installer on the command-line for it to ALWAYS show in English. And indeed it worked.

Screenshot (494)

To open a command window one can click the Search icon on the windows taskbar and type CMD then press ENTER.

Then they can drag-drop the .MSI file of ownCloud installer onto the black command-line window that opens up and type an extra space char and then Productlanguage=1033 before pressing ENTER to launch the ownCloud installer in English. After that they can close the command-line window at anytime.

Since many users may be uncomfortable with such instructions, one could provide an msiEnglish.bat file that just contains

%1 Productlanguage=1033

User could drag-drop the .msi they want onto that msiEnglish.bat file and it would run the msi installer being displayed in English language, irrespective of any preferred language settings at the Windows operating system.

Of course the best thing would be if ownCloud fixed their desktop client installer to fallback to the Engish language (set it as default) if it can’t find localization strings for the currently prefered language of the user. Have filed an issue at https://github.com/owncloud/client/issues/7825

Fix: WordPress administration UI content area showing up blank

Some time ago, at a WordPress 3.3 blog, a friend was getting a blank main area at the administration UI (just the menu was showing up there) when they visited the classic URL of the form http://somesite/wp-admin/. Here is a writeup of some notes I had kept while troubleshooting that issue and then upgrading to newer WordPress, with updated theme and plugins.

Visiting the problematic admin UI page online, I right-clicked and selected View Source in the browser and browsed to the bottom of it where it was showing: 

image

That div tag wasn’t not closed with a matching closing div. Obviously some PHP code that was outputing that tag failed at that point.

For starters, I copied all files via FTP (with the FireFtp Firefox plugin) from the remote server locally (into my Dropbox). Useful for backup too in case I messed up something.

Could use grepWin tool (http://stefanstools.sourceforge.net/grepWin.html) to search all locally copied php files for “contextual-help-sidebar” and see which one was outputing this (if that string was assigned to some variable could then search again where that variable was being used).

However, WordPress also has debugging mode, so I edited wp-config.php and uploaded the edited version to the server replacing the old file. I changed WP_DEBUG setting to true below: 

  image

At the problematic admin UI online page again, I right-clicked and selected View Source in the browser and browsed to the bottom of it, this time reading:

image

This led to indentifying the exact place in the PHP code that was causing this issue:

image

So, I commented out the buggy PHP code, converting it to an HTML comment block instead of a PHP block:

image

and the admin UI was working again after I pressed ENTER at the address bar again to refresh the admin UI (http://somesite/wp-admin/) and it was not showing up fine (F5 function key didn’t seem to really refresh the site when using Firefox btw, probably some caching issue).

Then installed (the free version) of the WP Database Backup plugin for WordPress:

http://www.wpseeds.com/documentation/docs/wp-database-backup/

by searching for “Backup” at

http://somesite/wp-admin/plugins.php

and evaluating the different backup plugins listed there (judging from both their votes and by checking out if WordPress wasn’t saying at the details page of a plugin that it hasn’t been tested with that [old] 3.3 WordPress version that was on that site) and backed up from Tools/WP-DB Backup menu:

http://somesite/wp-admin/tools.php?page=wp-database-backup

Then Downloaded the MySQL backup (.sql) file, so now I could update Themes, then Plugins, then update WordPress to new Core from

http://somesite/wp-admin/update-core.php

Did keep copies of the wp-content/themes and wp-content/plugins folders before and after updating them of course.

After WP updated, it asked to update the DB, all went ok

Then did backup up again the database via the WP-DB Backup tool and went again to

http://somesite/wp-admin/update-core.php

and installed an update for one of the plugins (can also do update per-plugin from http://somesite/wp-admin/plugins.php)

Then backed up again every file (db not included there) via FTP and done.

HowTo: Hide HTML markup from non-signedin users at MonoX Social CMS

At MonoX Social CMS, which I use at both ClipFlair Social and Trafilm websites, I was in the need of hiding some HTML markup when the user is not signed-in.

The solution for this is to add runat="server" to the HMTL element one wants to hide and then set the Visible property that the object acquires due to the runat clause. The Visible property is set using some special syntax to access the MonoX API like below:

<ul runat="server"
      Visible="<% $Code: Page.User.Identity.IsAuthenticated %>"  >

</ul>

Categories: Posts Tags: , , , , , , ,

HowTo: Drop files onto Silverlight controls

I was recently adding drop-files support to ClipFlair Studio, so I had to do some research on the related API that is available to Silverlight apps.

Silverlight supports a limited set of Drag-and-Drop interaction with the operating system (supposedly for security reasons, but most probably because of the classic cross-platform implementation pains).

For example it allows you to drop a collections of file objects from the operating system’s file explorer (or other similar application) onto a Silverlight drop target, but not the other way around.

Also, it doesn’t allow dropping other flavors of content, like text, images etc., only collections of file objects.

To allow dropping files onto a Silverlight control you set AllowDrop property to true and handle the Drop and optionally the DragEnter/DragOver/DragLeave events to provide visual feedback during the drop operation (mostly when over the drop target, unless you do Mouse Capturing).

<RichTextBox
  x:Name="rtb"
  …
  AllowDrop="True" 
  Drop="rtb_Drop"
  DragEnter="rtb_DragEnter"
  DragOver="rtb_DragOver"
  DragLeave="rtb_DragLeave"

  />

 

  #region DragAndDrop

    private void rtb_Drop(object sender, System.Windows.DragEventArgs e)
    {
      VisualStateManager.GoToState(this, "Normal", true);

      //the Drop event passes in an array of FileInfo objects for the list of files that were selected and drag-dropped onto the RichTextBox.
      if (e.Data == null)
        return;

     IDataObject f = e.Data as IDataObject;
      if (f != null) //checks if the dropped objects are files

      {
        object data = f.GetData(DataFormats.FileDrop); //Silverlight only supports FileDrop
        FileInfo[] files = data as FileInfo[]; //…GetData returns null if format is not supported

        e.Handled = true;

        if (files != null)

          //Walk through the list of FileInfo objects of the selected and drag-dropped files and parse the .txt and .docx files
          //and insert their content in the RichTextBox.
          foreach (FileInfo file in files)
            Load(file, false);
      }

    }

    private void rtb_DragEnter(object sender, System.Windows.DragEventArgs e)
    {
      VisualStateManager.GoToState(this, "DragOver", true);
      e.Handled = true;
    }
   
    private void rtb_DragOver(object sender, System.Windows.DragEventArgs e)
    {
      e.Handled = true;
      //NOP
    }

    private void rtb_DragLeave(object sender, System.Windows.DragEventArgs e)
    {
      VisualStateManager.GoToState(this, "Normal", true);
      e.Handled = true;
    }

    #endregion

 

For the visual feedback you can use VisualStateManager and respective VisualStates at say a Grid that wraps the drop target in the XAML layout.

<Grid x:Name="LayoutRoot">
   <VisualStateManager.VisualStateGroups>
     <VisualStateGroup x:Name="DragStates">
       <VisualStateGroup.Transitions>
         <VisualTransition GeneratedDuration="0:0:0.3">
           <VisualTransition.GeneratedEasingFunction>
             <CircleEase EasingMode="EaseIn"/>
           </VisualTransition.GeneratedEasingFunction>
         </VisualTransition>
       </VisualStateGroup.Transitions>
       <VisualState x:Name="Normal"/>
       <VisualState x:Name="DragOver">
         <Storyboard>
           <ColorAnimationUsingKeyFrames Storyboard.TargetProperty="(Control.Background).(SolidColorBrush.Color)" Storyboard.TargetName="rtb">
             <EasingColorKeyFrame KeyTime="0" Value="#FFFFF7D1"/>
           </ColorAnimationUsingKeyFrames>
         </Storyboard>
       </VisualState>
     </VisualStateGroup>
   </VisualStateManager.VisualStateGroups>

 

Note that on MacOS-X Safari doesn’t pass drop events automatically to plugins and you have to catch them via Javascript and pass them to Silverlight, plus I’ve read that in MacOS-X Firefox doesn’t support this at all.

Even worse, Microsoft’s workaround article has a typo in the Javascript (should call dragDrop instead of drop) and there is a chance it doesn’t work in latest version of Safari. For more see:

http://msdn.microsoft.com/en-us/library/ee670998%28v=vs.95%29.aspx

http://social.msdn.microsoft.com/Forums/silverlight/en-US/42a6b672-7d26-4690-be80-2149da755020/silverlight-4-detect-file-drop-event-on-mac?forum=silverlightarchieve&prof=required

http://community.rightpoint.com/blogs/viewpoint/archive/2011/03/27/silverlight-4-file-drag-and-drop-on-firefox-on-mac.aspx

http://www.telerik.com/forums/dropping-files-using-safari-on-mac (this says one can use HTML5 events and the Silverlight HTML/Javascript Bridge to notify Silverlight)

http://www.thebuzzmedia.com/html5-drag-and-drop-and-file-api-tutorial/

Gotcha: OnLostMouseCapture always called by CaptureMouse at WPF

It seems that CaptureMouse is behaving differently in WPF and Silverlight, in that in the former one it immediately calls OnLostMouseCapture at a Visual, whereas in Silverlight it doesn’t get called if the element didn’t have the mouse capture already (btw, in Silverlight that method is at a UIElement – there is no Visual ancestor as in WPF).

    #region --- Events ---

    private void OnMouseLeftButtonDown(object source, MouseButtonEventArgs e)
    {
      if (IsMoveToPointEnabled)
      {
        MoveThumbToPoint(e.GetPosition(this));
        CaptureMouse(); //must do before setting dragging=true, since WPF seems
//to be always calling OnLostMouseCapture on all
//controls, even if they didn't have the mouse capture
dragging = true; //always set, we might not make it to capture the mouse } } private void OnMouseLeftButtonUp(object source, MouseButtonEventArgs e) { ReleaseMouseCapture(); dragging = false; //always clear, in case we never had the mouse capture } protected override void OnMouseMove(MouseEventArgs e) { base.OnMouseMove(e); if (dragging && IsMoveToPointEnabled) MoveThumbToPoint(e.GetPosition(this)); } protected override void OnLostMouseCapture(MouseEventArgs e) { base.OnLostMouseCapture(e); dragging = false; //set dragging to false whatever the value of
//IsMoveToPointEnabled (may have changed while dragging)
} #endregion

 

The rest of the code of the SliderExt class that I’ve just added to ClipFlair codebase is below, where … is where the code region that was quoted above goes. This class is a descendent of Slider and implements IsMoveToPointEnabled property for Silverlight (in WPF that property exists), plus in WPF it fixes the behavior of the control so that when that property is set, it not only supports moving the slider thumb to the point on its track where mouse button was held down (instead of stepping up/down as is usually done in scrollbars), but also supports dragging from any point in the slider track which WPF’s implementation of IsMouseToPointEnabled didn’t do (it only allowed to drag from the thumb).

//Project: ClipFlair (http://ClipFlair.codeplex.com)
//Filename: SliderExt.cs
//Version: 20140313

using System.Windows;
using System.Windows.Controls;
using System.Windows.Controls.Primitives;
using System.Windows.Input;

namespace SliderExtLib
{
  public class SliderExt : Slider
  {

    public SliderExt()
    {

      //peek into mouse button events (even handled ones)
AddHandler(MouseLeftButtonDownEvent,
new MouseButtonEventHandler(OnMouseLeftButtonDown), true); AddHandler(MouseLeftButtonUpEvent,
new MouseButtonEventHandler(OnMouseLeftButtonUp), true); } #region --- Fields --- protected bool dragging; #endregion #region --- Properties --- public bool IsMoveToPointEnabled //WPF has this prop (hiding it), doesn't
//implement move to point if you click and drag on the track instead of thumb
{ get { return (bool)GetValue(IsMoveToPointEnabledProperty); } set { SetValue(IsMoveToPointEnabledProperty, value); } } public static readonly DependencyProperty IsMoveToPointEnabledProperty = DependencyProperty.RegisterAttached("IsMoveToPointEnabled", typeof(bool), typeof(SliderExt),
new PropertyMetadata(false)); #endregion #region --- Methods --- private void MoveThumbToPoint(Point p) { if (Orientation == Orientation.Horizontal) Value = p.X / ActualWidth * Maximum; else //if (Orientation == Orientation.Vertical) Value = p.Y / ActualHeight * Maximum; } #endregion




...



}
}

The incentive of making such a slider was for using it at the ColorPicker control I’ve just added to the ClipFlair codebase.

HowTo: find max ZIndex from a collection of UIElements with LINQ

Based on a relevant answer at

http://stackoverflow.com/questions/1101841/linq-how-to-perform-max-on-a-property-of-all-objects-in-a-collection-and-ret

and some digging into LINQ’s Aggregate method documentation is what I’ve just added to my enhanced version of SilverFlow library’s FloatingWindowHost (copying from FloatingWindowHost.cs at http://clipflair.codeplex.com source code)

        /// <summary>
        /// Sets the specified UIElement topmost.
        /// </summary>
        /// <param name="element">UIElement to set topmost.</param>
        /// <exception cref="ArgumentNullException">UIElement is null</exception>
        private void SetTopmost(UIElement element)
        {
            if (element == null)
                throw new ArgumentNullException("element");

            Canvas.SetZIndex(element, MaxZIndex + 1);
        }

        public int MaxZIndex
        {
          get {
            return FloatingWindows.Aggregate(-1, (maxZIndex, window) => {
              int w = Canvas.GetZIndex(window);
              return (w > maxZIndex) ? w : maxZIndex; 
            });  //Math.Max would be cleaner, but slower to call
          }
        }

Worth noting regarding the code above is that Canvas.ZIndex is an attached property available for UIElements in various containers, not just used when being hosted in a Canvas (see http://stackoverflow.com/questions/569751/controlling-rendering-order-zorder-in-silverlight-without-using-the-canvas-con)

Guess one could even make a SetTopmost and SetBottomMost static extension method for UIElement easily by adapting this code.

Gotcha: .NET Point and PointConverter inconsistency in string format used

I have submitted the following issue to Microsoft Connect (product feedback center):

http://connect.microsoft.com/VisualStudio/feedback/details/809084/point-class-issue-with-two-way-databinding

Point class issue with two-way databinding

 

In Silverlight, when using databinding code like the following:
<StackPanel Orientation="Vertical" Name="propPosition">
    <sdk:Label Style="{StaticResource PropertyLabelStyle}" Content="Position:" Target="{Binding ElementName=edPosition}" />
     <TextBox Name="edPosition" Text="{Binding Position, Mode=TwoWay, ValidatesOnExceptions=True, NotifyOnValidationError=true}" />
</StackPanel>

and binding (via DataContext) to a View that has the following property:

Point Position { get; set; }

then what you see on the UI is values like 500; 100

but when you try to edit the position and enter say 400; 100 it shows a red message that it is invalid format (caught an exception that is and showing on the UI automatically cause of ValidatesOnExceptions and NotifyOnValidationError being true)
if you enter 400, 100 it works ok (it moves a ClipFlair Studio [http://ClipFlair.net] window arround in my case), so it outputs the two numbers (X, Y) with a ";" but expects to get them separated with a "," (also wonder if it copes OK with numbers in say format used in Greece where decimal point separator is , instead of .)

 

I managed to reproduce the same behaviour in a .NET console program:

//Console program in C# to demonstrate Point.ToString and PointConverter (from string) inconsistency in string format
//(the former outputs X;Y, the later expects X,Y)

using System;
using System.Windows; //for "Point" class (needs WindowsBase.dll reference)

namespace ConsoleApplication1
{
  class Program
  {

    static PointConverter conv = new PointConverter();

    static void Main(string[] args)
    {
      Point p = new Point(10, 20);
      Console.WriteLine(p); //outputs "10;20"
           
      //——————

      Point p1 = (Point)conv.ConvertFrom("10, 20");
      Console.WriteLine(p1); //outputs "10;20"

      //——————

      Point p2 = (Point)conv.ConvertFrom("10; 20"); //unhandled exception "System.FormatException" in mscorlib.dll
      Console.WriteLine(p2); //never reached cause of exception above
    }

  }
}