Author Topic: Would like ability to change layout according to filetype  (Read 2174 times)

bogedo

  • Community Member
  • Posts: 31
  • Hero Points: 0
Would like ability to change layout according to filetype
« on: October 14, 2021, 09:34:24 PM »
there was a thread on this forum(here: https://community.slickedit.com/index.php/topic,18107.msg71278.html#msg71278) where the user Graeme tried to help me with a request i had: i wanted to be able to change the layout of SlickEdit when a specific file was opened. he was able to give me some very useful macro's to load and helpful tips to try out but ultimately using macros to accomplish this did not work 100%.

i would like to submit a request for this feature and would like to present a use case for it:

let's say a user works with html, javascript and plaintext. for javascript they would want to have only the tool windows on the right in their workspace so they create a layout for their javascript work that has tool windows only on the right with the tool windows in the left and bottom  removed.

for html they would save a layout with the tool windows in the left and bottom present in the workspace and the tool window on the right removed.

the user usually opens plaintext files as readmes and they would want minimum distraction while reading those files so they remove all tool windows and some toolbars from the workspace in order to have a distraction free reading space.

the user now has three layouts: javascript_layout, html_layout and txt_layout. it would be great if the user clicks on .txt files in a file manager and SlickEdit is able to open the file with the layout txt_layout applied. in the languages section of the options there would be a section where a user would be able to apply a default layout for a chosen language in this case the user specified txt_layout to be used when .txt files are opened in SlickEdit. when a .html file is opened, the html_layout would be applied and when a javascript file is opened, the javascript_layout would be applied.

now let's say the user has three files open: a javascript file, a html file and a plaintext file. the user is currently reading a plaintext file and the txt_layout is loaded and now they want to switch to the javascript file by clicking its tab. in this feature, SlickEdit would load the javascript_layout the moment the user switches from the plaintext file tab to the javascript file tab and from the javascript_layout to the html_layout when they switch tabs from editing their javascript to editing a html file.

i hope that you seriously consider this feature since it would add more flexibility to how we open, edit and present our files in SlickEdit.

Thank you.

jporkkahtc

  • Senior Community Member
  • Posts: 2620
  • Hero Points: 210
  • Text
Re: Would like ability to change layout according to filetype
« Reply #1 on: October 15, 2021, 08:29:59 PM »
Interesting idea... Personally, I think this would be pretty disruptive for UI to relay out so much - but I can see how you might want this.

OTOH, maybe it could be attached to the project (or workspace)?
Change which project is the active project and the layout changes.

In any case, I too would like to see better window layout support.
Lately, I've been wanting different layouts depending on the size of the slickedit window.
When it is maximized, I like my docked tools pinned.
When slickedit window is smaller, then I'd like the toolbars to be unpinned.

A problem though is the management of this.
How does the user pick what layout to use, and when?
It should also be possible to delete or reset layouts, and maybe list them.

Graeme

  • Senior Community Member
  • Posts: 2793
  • Hero Points: 347
Re: Would like ability to change layout according to filetype
« Reply #2 on: October 16, 2021, 06:06:12 AM »
Hi bogedo

Slick support sent me a fix to the timer callback code that crashed as mentioned in that other thread but I didn't get round to looking at it - just as well because I might have got the timer handle handling wrong.  All it needed was a check for the timer callback function already being active.  The code below seems to be reliable - it switches to the "minimal" layout for html file extension and otherwise to default layout.  It has a global variable for the timer handle.  I've used a prefix of "user_bogedo" as this is the recommended way to avoid name collisions.  All global variables appear in the global names table regardless of whose code it is.
If you need any more help, let me know.

Code: [Select]
#include "slick.sh"
#import "sc/lang/ScopedValueGuard.e"

#pragma option(strictsemicolons,on)
#pragma option(strict,on)
#pragma option(autodecl,off)
#pragma option(strictparens,on)


// user_bogedo_my_layout_timer_handle is global because it handles a resource that must
// be freed when the module is loaded.   Because it is global, the handle value is preserved
// by the reload.
int user_bogedo_my_layout_timer_handle = -1;

static bool layout_is_default;
static int my_layout_startup_counter;
static _str my_layout_current_buf_name;
static bool in_my_layout_timer_callback1 = false;

static void my_layout_timer_callback1() {

   // we need to exit immediately if this function is already active
   // It's possible that the call to load_named_layout can result in the slick c interpreter
   // checking and running timer callbacks - this code crashes without this.
   // A possible alternative is to kill the timer on entry, not tested though.

   if ( in_my_layout_timer_callback1 ) {
      return;
   }
   sc.lang.ScopedValueGuard guard(in_my_layout_timer_callback1);
   // Note that guard will set in_my_layout_timer_callback1=false when
   // this function falls out of scope (i.e on return or end-of-scope).
   in_my_layout_timer_callback1 = true;

   if (!_use_timers || user_bogedo_my_layout_timer_handle < 0)
      return;

   if (++my_layout_startup_counter < 6)
      return;

   my_layout_startup_counter = 6;

   if (_no_child_windows()) {
      return;
   }

   if ( get_extension(_mdi.p_child.p_buf_name) == "html" ) {
      //say("html " :+ layout_is_default :+ " x " :+ oldbuffname );

      if (layout_is_default)
         load_named_layout("minimal");
      layout_is_default = false;

   }  else {
      //say("nothtml " :+ layout_is_default :+ " x " :+ oldbuffname );
      if (!layout_is_default)
         load_named_layout("default");
      layout_is_default = true;
   }
   _kill_timer(user_bogedo_my_layout_timer_handle);
   user_bogedo_my_layout_timer_handle = _set_timer(1000, my_layout_timer_callback1);
}


void _switchbuf_my_layout_timer() {
   if (my_layout_startup_counter < 6)
      return;

   _kill_timer(user_bogedo_my_layout_timer_handle);
   user_bogedo_my_layout_timer_handle = _set_timer(50, my_layout_timer_callback1);
}


definit() {
   in_my_layout_timer_callback1 = false;
   if (arg(1) == "L" && user_bogedo_my_layout_timer_handle != -1) {
      _kill_timer(user_bogedo_my_layout_timer_handle);
   }
   else {
      // this code does not run on every startup, only on some of them.  If slickedit decides the module
      // needs to be recompiled when it starts up, this code does not run.
      // The decision on which state file to use has an effect on this.
      user_bogedo_my_layout_timer_handle = -1;
   }

   user_bogedo_my_layout_timer_handle = _set_timer(1000, my_layout_timer_callback1);
   my_layout_startup_counter = 0;
   my_layout_current_buf_name = "";
}

Joe

Slick V23 introduced six functions
save-named-layout, load-named-layout
save-named-files, load-named-files
save-named-state, load-named-state

You are prompted for a name and you can delete existing names from the list.
One of the files shown in the code segment below is used to store the information, with the name you supply being the name of a "section" in the file.  The data is in the same format as in vrestore.slk I think.

Code: [Select]
_command void save_named_state(_str sectionName="",bool save_files=true, bool save_layout=true) name_info(',')
{
   // This really should have had a .ini extension. Too late now.
   _str filename='windowstate.slk';
   if (save_files) {
      if (save_layout) {
         filename='state.ini';
      } else {
         filename='windowstate.slk';
      }
   } else/* if (save_layout)*/ {
      filename='layoutstate.ini';
   }
   gload_named_state_name=filename;
   filename = _ConfigPath():+filename;

load-named-layout gets toolbars and tool-windows
load-named-files gets just a set of open files
load-named-state gets both layout and files, plus bookmarks and I don't know what else - probably floating edit windows and not the workspace.

I haven't tested the code below but if you create layouts for "midi" and "maxi" it might work.

_command void  midi() name_info(',')
{
   restore_mdi();
   load_named_layout("midi");
}

_command void  maxi() name_info(',')
{
   maximise_mdi();
   load_named_layout("maxi");
}

Clark told me recently that slick C code is single threaded but it seems that the call to load-named-state from within a timer callback can result in the slick interpreter re-running the timer callback so it has to check for re-entrancy.  Maybe the call to the file-system to open a file does timer maintenance while waiting for the file operation.

bogedo

  • Community Member
  • Posts: 31
  • Hero Points: 0
Re: Would like ability to change layout according to filetype
« Reply #3 on: October 17, 2021, 08:37:20 PM »
Interesting idea... Personally, I think this would be pretty disruptive for UI to relay out so much - but I can see how you might want this.

check out this feature of Sublime Text: https://www.sublimetext.com/docs/distraction_free.html it is the inspiration for this feature request.


Hi bogedo

Slick support sent me a fix to the timer callback code that crashed as mentioned in that other thread but I didn't get round to looking at it - just as well because I might have got the timer handle handling wrong.  All it needed was a check for the timer callback function already being active.  The code below seems to be reliable - it switches to the "minimal" layout for html file extension and otherwise to default layout.  It has a global variable for the timer handle.  I've used a prefix of "user_bogedo" as this is the recommended way to avoid name collisions.  All global variables appear in the global names table regardless of whose code it is.
If you need any more help, let me know.

Code: [Select]
#include "slick.sh"
#import "sc/lang/ScopedValueGuard.e"

...

Hello Graeme,

great to see that you are still on the case, please accept my sincere thanks for your continued efforts on this feature. the timer callback code is working great. SlickEdit does not crash at startup when restoring the previous session and i can now switch tabs and the layout changes without seeing any error messages. it looks good but there are some issues:

- let's say when you close SlickEdit with a file that has loaded the default layout. opening a html file from explorer that should load the minimal layout does not do so, it will open in SlickEdit with the default layout and will only load the minimal layout when you switch to another tab and switch back to it.

- when we close SlickEdit with a file that has loaded the minimal layout, when we open a file from explorer that should open the default layout it first opens in the minimal layout then after a delay it switches to the default layout. is there a place in the code where the delay can be adjusted ?

 all in all, i really appreciate the new code. i'm very grateful that this feature is now within reach due to your efforts.

Graeme

  • Senior Community Member
  • Posts: 2793
  • Hero Points: 347
Re: Would like ability to change layout according to filetype
« Reply #4 on: October 17, 2021, 10:11:44 PM »
ok, try this version

Code: [Select]
#include "slick.sh"
#import "sc/lang/ScopedValueGuard.e"

#pragma option(strictsemicolons,on)
#pragma option(strict,on)
#pragma option(autodecl,off)
#pragma option(strictparens,on)


// user_bogedo_my_layout_timer_handle is global because it handles a resource that must
// be freed when the module is loaded.   Because it is global, the handle value is preserved
// by the reload.
int               user_bogedo_my_layout_timer_handle = -1;

static bool       layout_is_default;
static int        my_layout_startup_counter;
static bool       in_my_layout_timer_callback1 = false;
static const      STARTUP_DELAY = 2;
static bool       switched_last_time;
static bool       force_first_time;


static void my_layout_timer_callback1() {

   // we need to exit immediately if this function is already active
   // It's possible that the call to load_named_layout can result in the slick c interpreter
   // checking and running timer callbacks - this code crashes without this.
   // A possible alternative is to kill the timer on entry, not tested though.

   if ( in_my_layout_timer_callback1 ) {
      return;
   }
   sc.lang.ScopedValueGuard guard(in_my_layout_timer_callback1);
   // Note that guard will set in_my_layout_timer_callback1=false when
   // this function falls out of scope (i.e on return or end-of-scope).
   in_my_layout_timer_callback1 = true;

   if (!_use_timers || user_bogedo_my_layout_timer_handle < 0)
      return;

   _kill_timer(user_bogedo_my_layout_timer_handle);
   user_bogedo_my_layout_timer_handle = _set_timer(1000, my_layout_timer_callback1);

   if (++my_layout_startup_counter < STARTUP_DELAY)
      return;

   my_layout_startup_counter = STARTUP_DELAY;

   if (_no_child_windows()) {
      return;
   }

   if ( switched_last_time ) {
      switched_last_time = false;
      return;
   }

   if ( get_extension(_mdi.p_child.p_buf_name) == "html" ) {
      //say("html " :+ layout_is_default :+ " x " :+ oldbuffname );

      if (layout_is_default || force_first_time)
      {
         load_named_layout("minimal");
         switched_last_time = true;
      }
      layout_is_default = false;

   }  else {
      //say("nothtml " :+ layout_is_default :+ " x " :+ oldbuffname );
      if (!layout_is_default || force_first_time)
      {
         load_named_layout("default");
         switched_last_time = true;
      }
      layout_is_default = true;
   }
   force_first_time = false;
}


void _switchbuf_my_layout_timer() {
   if (my_layout_startup_counter < STARTUP_DELAY || switched_last_time)
      return;

   _kill_timer(user_bogedo_my_layout_timer_handle);
   user_bogedo_my_layout_timer_handle = _set_timer(50, my_layout_timer_callback1);
}


definit() {
   in_my_layout_timer_callback1 = false;
   switched_last_time = false;
   if (arg(1) == "L" && user_bogedo_my_layout_timer_handle != -1) {
      _kill_timer(user_bogedo_my_layout_timer_handle);
   }
   else {
      // this code does not run on every startup, only on some of them.  If slickedit decides the module
      // needs to be recompiled when it starts up, this code does not run.
      // The decision on which state file to use has an effect on this.
      user_bogedo_my_layout_timer_handle = -1;
   }

   my_layout_startup_counter = 0;
   force_first_time = true;
   user_bogedo_my_layout_timer_handle = _set_timer(1000, my_layout_timer_callback1);
}

bogedo

  • Community Member
  • Posts: 31
  • Hero Points: 0
Re: Would like ability to change layout according to filetype
« Reply #5 on: October 18, 2021, 10:09:03 PM »
Thank you Graeme, the script now works as expected. i can open files from explorer and the appropriate layout is loaded regardless of the layout loaded at SlickEdit exit. will continue using the macro and will update this thread with any issues that may crop up while using it. hopefully something like this is baked into future versions of SlickEdit.