<commit_before>import { obj, string } from "lively.lang";
import { pt, Color, Point, Rectangle, rect } from "lively.graphics";
import { morph, Morph, MorphicEnv } from "./index.js";
import { Icon } from "./icons.js";
import { connect, disconnect } from "lively.bindings";
import { ShadowObject } from "./rendering/morphic-default.js";


export function show(target) {

  var world = MorphicEnv.default().world;

  if (target === null || target === undefined) target = String(target);
  if (target.isMorph) return showRect(target.world(), target.globalBounds());
  if (target.isPoint) return showRect(world, new Rectangle(target.x-5, target.y-5, 10,10));
  if (target.isRectangle) return showRect(world, target);
  if (typeof Element !== "undefined" && target instanceof Element) return showRect(world, Rectangle.fromElement(target));
  if (typeof target === "number" || typeof target === "symbol" || typeof target === "boolean"
    || (typeof Node !== "undefined" && target instanceof Node)
    || target instanceof RegExp) target = String(target);
  if (typeof target === "object") target = obj.inspect(target, {maxDepth: 1})
  if (typeof target === "string" && arguments.length === 1) return world.setStatusMessage(target);

  return world.setStatusMessage(string.formatFromArray(Array.from(arguments)));
}

function showRect(world, rect) {
  var marker = BoundsMarker.highlightBounds(rect);
  return showThenHide(world, marker);
}

function showThenHide (world, morphOrMorphs, duration = 3) {
  if (!world) return;
  var morphs = Array.isArray(morphOrMorphs) ? morphOrMorphs : [morphOrMorphs];
  morphs.forEach(ea => world.addMorph(ea));
  if (duration) { // FIXME use scheduler
    setTimeout(() => morphs.forEach(ea => ea.fadeOut(2000)), duration*1000);
  }
  return morphOrMorphs;
}

class BoundsMarker extends Morph {

  // creates a marker that looks like:
  // xxxx     xxxx
  // x           x
  // x           x
  //
  // x           x
  // x           x
  // xxxx     xxxx


  static highlightMorph(morph) {
    return new this().alignWithMorph(morph);
  }

  static highlightBounds(bounds) {
    return new this().alignWithBounds(bounds);
  }

  constructor() {
    super({borderWidth: 0, fill: Color.transparent, reactsToPointer: false});

    // this.ignoreEvents();
  }

  get isEpiMorph() { return true }

  markerLength(forBounds) {
    forBounds = forBounds.insetBy(-2);
    var length = Math.min(forBounds.width, forBounds.height);
    return Math.max(4, Math.floor((length/10) < 10 ? (length / 2) - 5 : length / 10));
  }

  createMarkerEdge() {
      var b = morph({fill: Color.red, reactsToPointer: false});
      // b.isEpiMorph = true;
      // b.ignoreEvents();
      return b;
  }

  ensureMarkerCorners() {
    var topLeftH     = this.topLeftH     || (this.topLeftH     = this.addMorph(this.createMarkerEdge())),
        topLeftV     = this.topLeftV     || (this.topLeftV     = this.addMorph(this.createMarkerEdge())),
        topRightH    = this.topRightH    || (this.topRightH    = this.addMorph(this.createMarkerEdge())),
        topRightV    = this.topRightV    || (this.topRightV    = this.addMorph(this.createMarkerEdge())),
        bottomRightH = this.bottomRightH || (this.bottomRightH = this.addMorph(this.createMarkerEdge())),
        bottomRightV = this.bottomRightV || (this.bottomRightV = this.addMorph(this.createMarkerEdge())),
        bottomLeftH  = this.bottomLeftH  || (this.bottomLeftH  = this.addMorph(this.createMarkerEdge())),
        bottomLeftV  = this.bottomLeftV  || (this.bottomLeftV  = this.addMorph(this.createMarkerEdge()));
    return [
      topLeftH, topLeftV,
      topRightH, topRightV,
      bottomRightH, bottomRightV,
      bottomLeftH, bottomLeftV];
  }

  alignWithMorph(otherMorph) {
    return this.alignWithBounds(otherMorph.globalBounds());
  }

  alignWithBounds(bounds) {
    this.alignWithRect(bounds.insetBy(-20));
    return this.alignWithRect(bounds, true)
  }

  alignWithRect(r, animated) {
    var markerWidth = 5,
        corners = this.ensureMarkerCorners(),
        markerLength = this.markerLength(r),
        boundsForMarkers = [
          r.topLeft().    addXY(0,0).                        extent(pt(markerLength, markerWidth)),
          r.topLeft().    addXY(0,0).                        extent(pt(markerWidth, markerLength)),
          r.topRight().   addXY(-markerLength, 0).           extent(pt(markerLength, markerWidth)),
          r.topRight().   addXY(-markerWidth,0).             extent(pt(markerWidth, markerLength)),
          r.bottomRight().addXY(-markerWidth, -markerLength).extent(pt(markerWidth, markerLength)),
          r.bottomRight().addXY(-markerLength, -markerWidth).extent(pt(markerLength, markerWidth)),
          r.bottomLeft(). addXY(0,-markerWidth).             extent(pt(markerLength, markerWidth)),
          r.bottomLeft(). addXY(0, -markerLength).           extent(pt(markerWidth, markerLength))];
    corners.forEach((corner, i) => corner.setBounds(boundsForMarkers[i]));
    if (animated) {
       this.adjustOrigin(r.center());
       this.opacity = 0, this.scale = 1.5;
       this.animate({opacity: 1, scale: 1, duration: 300});
    }
    return this;
  }

}


export class StatusMessage extends Morph {

  static get properties() {
    return {      
      stayOpen:     {defaultValue: false},
      slidable:     {defaultValue: true}, // auto slide up on new message
      isMaximized:  {defaultValue: false},
      expandable:   {defaultValue: true},
      maxLines:     {defaultValue: Infinity},
      name:         {defaultValue: "messageMorph"},
      extent:       {defaultValue: pt(240, 65)},
      clipMode:     {defaultValue: "hidden"},
      grabbing:     {defaultValue: false},
      dragging:     {defaultValue: false},
      borderRadius: {defaultValue: 20},
      borderWidth:  {defaultValue: 5},
      fill:         {defaultValue: Color.white},
      dropShadow:   {defaultValue: new ShadowObject(true)},

      message: {
        after: ["submorphs"],
        set(value) {
          this.setProperty("message", value);
          var text = this.getSubmorphNamed("messageText");
          if (!text) return;
          // FIXME not yet initialized
          text.value = value;<commit_msg>make status messages expandable via button<commit_after>import { obj, string } from "lively.lang";
import { pt, Color, Point, Rectangle, rect } from "lively.graphics";
import { morph, Morph, MorphicEnv } from "./index.js";
import { Icon } from "./icons.js";
import { connect, disconnect } from "lively.bindings";
import { ShadowObject } from "./rendering/morphic-default.js";


export function show(target) {

  var world = MorphicEnv.default().world;

  if (target === null || target === undefined) target = String(target);
  if (target.isMorph) return showRect(target.world(), target.globalBounds());
  if (target.isPoint) return showRect(world, new Rectangle(target.x-5, target.y-5, 10,10));
  if (target.isRectangle) return showRect(world, target);
  if (typeof Element !== "undefined" && target instanceof Element) return showRect(world, Rectangle.fromElement(target));
  if (typeof target === "number" || typeof target === "symbol" || typeof target === "boolean"
    || (typeof Node !== "undefined" && target instanceof Node)
    || target instanceof RegExp) target = String(target);
  if (typeof target === "object") target = obj.inspect(target, {maxDepth: 1})
  if (typeof target === "string" && arguments.length === 1) return world.setStatusMessage(target);

  return world.setStatusMessage(string.formatFromArray(Array.from(arguments)));
}

function showRect(world, rect) {
  var marker = BoundsMarker.highlightBounds(rect);
  return showThenHide(world, marker);
}

function showThenHide (world, morphOrMorphs, duration = 3) {
  if (!world) return;
  var morphs = Array.isArray(morphOrMorphs) ? morphOrMorphs : [morphOrMorphs];
  morphs.forEach(ea => world.addMorph(ea));
  if (duration) { // FIXME use scheduler
    setTimeout(() => morphs.forEach(ea => ea.fadeOut(2000)), duration*1000);
  }
  return morphOrMorphs;
}

class BoundsMarker extends Morph {

  // creates a marker that looks like:
  // xxxx     xxxx
  // x           x
  // x           x
  //
  // x           x
  // x           x
  // xxxx     xxxx


  static highlightMorph(morph) {
    return new this().alignWithMorph(morph);
  }

  static highlightBounds(bounds) {
    return new this().alignWithBounds(bounds);
  }

  constructor() {
    super({borderWidth: 0, fill: Color.transparent, reactsToPointer: false});

    // this.ignoreEvents();
  }

  get isEpiMorph() { return true }

  markerLength(forBounds) {
    forBounds = forBounds.insetBy(-2);
    var length = Math.min(forBounds.width, forBounds.height);
    return Math.max(4, Math.floor((length/10) < 10 ? (length / 2) - 5 : length / 10));
  }

  createMarkerEdge() {
      var b = morph({fill: Color.red, reactsToPointer: false});
      // b.isEpiMorph = true;
      // b.ignoreEvents();
      return b;
  }

  ensureMarkerCorners() {
    var topLeftH     = this.topLeftH     || (this.topLeftH     = this.addMorph(this.createMarkerEdge())),
        topLeftV     = this.topLeftV     || (this.topLeftV     = this.addMorph(this.createMarkerEdge())),
        topRightH    = this.topRightH    || (this.topRightH    = this.addMorph(this.createMarkerEdge())),
        topRightV    = this.topRightV    || (this.topRightV    = this.addMorph(this.createMarkerEdge())),
        bottomRightH = this.bottomRightH || (this.bottomRightH = this.addMorph(this.createMarkerEdge())),
        bottomRightV = this.bottomRightV || (this.bottomRightV = this.addMorph(this.createMarkerEdge())),
        bottomLeftH  = this.bottomLeftH  || (this.bottomLeftH  = this.addMorph(this.createMarkerEdge())),
        bottomLeftV  = this.bottomLeftV  || (this.bottomLeftV  = this.addMorph(this.createMarkerEdge()));
    return [
      topLeftH, topLeftV,
      topRightH, topRightV,
      bottomRightH, bottomRightV,
      bottomLeftH, bottomLeftV];
  }

  alignWithMorph(otherMorph) {
    return this.alignWithBounds(otherMorph.globalBounds());
  }

  alignWithBounds(bounds) {
    this.alignWithRect(bounds.insetBy(-20));
    return this.alignWithRect(bounds, true)
  }

  alignWithRect(r, animated) {
    var markerWidth = 5,
        corners = this.ensureMarkerCorners(),
        markerLength = this.markerLength(r),
        boundsForMarkers = [
          r.topLeft().    addXY(0,0).                        extent(pt(markerLength, markerWidth)),
          r.topLeft().    addXY(0,0).                        extent(pt(markerWidth, markerLength)),
          r.topRight().   addXY(-markerLength, 0).           extent(pt(markerLength, markerWidth)),
          r.topRight().   addXY(-markerWidth,0).             extent(pt(markerWidth, markerLength)),
          r.bottomRight().addXY(-markerWidth, -markerLength).extent(pt(markerWidth, markerLength)),
          r.bottomRight().addXY(-markerLength, -markerWidth).extent(pt(markerLength, markerWidth)),
          r.bottomLeft(). addXY(0,-markerWidth).             extent(pt(markerLength, markerWidth)),
          r.bottomLeft(). addXY(0, -markerLength).           extent(pt(markerWidth, markerLength))];
    corners.forEach((corner, i) => corner.setBounds(boundsForMarkers[i]));
    if (animated) {
       this.adjustOrigin(r.center());
       this.opacity = 0, this.scale = 1.5;
       this.animate({opacity: 1, scale: 1, duration: 300});
    }
    return this;
  }

}


export class StatusMessage extends Morph {

  static get properties() {
    return {      
      stayOpen:     {defaultValue: false},
      slidable:     {defaultValue: true}, // auto slide up on new message
      isMaximized:  {defaultValue: false},
      expandable:   {initialize() { this.expandable = true; }},
      maxLines:     {defaultValue: Infinity},
      name:         {defaultValue: "messageMorph"},
      extent:       {defaultValue: pt(240, 65)},
      clipMode:     {defaultValue: "hidden"},
      grabbing:     {defaultValue: false},
      dragging:     {defaultValue: false},
      borderRadius: {defaultValue: 20},
      borderWidth:  {defaultValue: 5},
      fill:         {defaultValue: Color.white},
      dropShadow:   {defaultValue: new ShadowObject(true)},

      message: {
        after: ["submorphs"],
        set(value) {
          this.setProperty("message", value);
          var text = this.getSubmorphNamed("messageText");
          if (!text) return;
          // FIXME not yet initialized
          text.value = value;
{'fraction_non_alphanumeric': 0.1397840863654538, 'fraction_numerical': 0.008796481407437025, 'mean_word_length': 3.0096184674575186, 'pattern_counts': {'":': 0, '<': 5, '<?xml version=': 0, '>': 11, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '851628', 'n_tokens_mistral': 3912, 'n_tokens_neox': 3782, 'n_words': 821}