<commit_before><!-- This Source Code Form is licensed MPL-2.0: http://mozilla.org/MPL/2.0 -->

<docs>
  # B-PROJECTSHELL
  Shell for editing and display of a Bse.Project.
  ## Props:
  *project*
  : Implicit *Bse.Project*, using App.bse_project().
</docs>

<style lang="scss">
  @import 'mixins.scss';

  .b-projectshell {
    --b-resize-handle-thickness: #{$b-resize-handle-thickness};
    --b-transition-fast-slide: #{$b-transition-fast-slide};
    width: 100%;
    height: 100%;
    justify-content: space-between;
    align-items: stretch;
  }
  .b-projectshell-panel1 {
    @include b-panel-box;
    flex-grow: 1;
  }
  .b-projectshell-panel2 {
    @include b-panel-box;
    height: 19em;
    flex-shrink: 0;
  }
  .b-projectshell-sidebar {
    padding: 3px;
    overflow: hidden scroll;
    &, * { text-overflow: ellipsis; white-space: nowrap; }
  }
  .b-projectshell-resizer {
    width: var(--b-resize-handle-thickness);
    background: $b-resize-handle-bgcolor;
    border-left: $b-resize-handle-border;
    border-right: $b-resize-handle-border;
    cursor: col-resize;
  }
  html.b-projectshell-during-drag .b-root {
    .b-projectshell-resizer { background: $b-resize-handle-hvcolor; }
    * { cursor: col-resize !important; user-select: none !important; }
  }
</style>

<template>
  <b-vflex class="b-projectshell" >

    <!-- play controls, time display -->
    <b-hflex center style="margin: 5px">
      <b-playcontrols :project="project"> </b-playcontrols>
      <span style="width: 3em"><!-- spacer --></span>
      <b-positionview :song="song"> </b-positionview>
    </b-hflex>

    <b-hflex style="overflow: hidden; flex: 1 1 auto">
      <b-vflex grow1 shrink1>
	<!-- upper main area -->
	<b-hflex class="b-projectshell-panel1" >
	  <b-track-list class="grow1" :song="song"></b-track-list>
	</b-hflex>
	<!-- lower main area -->
	<b-hflex class="b-projectshell-panel2" >
	  <b-piano-roll class="grow1" :msrc="App.piano_roll_source" v-show="App.panel2 == 'p'" ></b-piano-roll>
	  <b-devicepanel v-show="App.panel2 == 'd'" :track="current_track" />
	</b-hflex>
      </b-vflex>

      <!-- browser -->
      <b-hflex ref="sidebarcontainer" style="width: 0; flex: 0 0 15%" >
	<div     style="flex-grow: 0; flex-shrink: 0" class="b-projectshell-resizer" @mousedown="sidebar_mouse" ></div>
	<b-vflex class="b-projectshell-sidebar" start shrink1 grow1 >
	  <b-treeselector :tree="o.filetree" v-show="App.panel3 == 'b'" ></b-treeselector>
	  <span v-show="App.panel3 == 'i'" >Info Panel</span>
	</b-vflex>
      </b-hflex>
    </b-hflex>

    <!-- status bar -->
    <b-statusbar />

    <!-- popup dialogs -->
    <b-aboutdialog v-model="show_about_dialog" />
    <b-preferencesdialog v-model="show_preferences_dialog" />

  </b-vflex>
</template>

<script>
async function list_sample_files() {
  const crawler = await Bse.server.resource_crawler();
  const entries = await crawler.list_files ('wave', 'user-downloads');
  return Object.freeze ({ entries: entries });
}

function observable_project_data () { // yields reactive Proxy object
  const data = {
    filetree:	     { default: {}, getter: c => list_sample_files(), },
    usermessagehook: { notify: n => Bse.server.on ("usermessage", this.usermessage), },
    // TODO: tracks: { getter: c => list_tracks.call (this), notify: n => this.song.on ("treechange", n), },
    // update current_track if tracks change
    __update__: Util.observable_force_update,
  };
  return this.observable_from_getters (data, () => this.project);
}

export default {
  name: 'b-projectshell',
  data() { return {
    project: undefined,
    o: observable_project_data.call (this),
    current_track: undefined,
    show_about_dialog: false,
    show_preferences_dialog: false,
    song: undefined,
    notifynameclear: () => 0,
  }; },
  watch: {
    show_about_dialog:       function (newval) { if (newval && this.show_preferences_dialog) this.show_preferences_dialog = false; },
    show_preferences_dialog: function (newval) { if (newval && this.show_about_dialog) this.show_about_dialog = false; },
  },
  created() {
    // delete `Shell` dummy on toplebel if present
    let p = this;
    while (p.$parent)
      p = p.$parent;
    if (p.Shell && p.Shell.Shell_placeholder)
      delete p.Shell;
    // inject Shell into all Vue components
    Vue.prototype.Shell = this;
    console.assert (this === p.Shell);
    // provide default project
    this.load_project (Bse.server.last_project());
    // load_project() also forces an update with new Shell properties in place
  },
  mounted() {
    this.switch_panel2 = App.switch_panel2.bind (App);
    Util.add_hotkey ('Backquote', this.switch_panel2);
    this.switch_panel3 = App.switch_panel3.bind (App);
    Util.add_hotkey ('KeyI', this.switch_panel3);
  },
  destroyed() {
    Util.remove_hotkey ('Backquote', this.switch_panel2);
    Util.remove_hotkey ('KeyI', this.switch_panel3);
    this.notifynameclear();
  },
  provide () { return { 'b-projectshell': this }; },
  methods: {
    usermessage (e) {
      let msg = '';
      switch (e.umtype)
      {
	case Bse.UserMessageType.ERROR:		msg += '### '; break;
	case Bse.UserMessageType.WARNING:	msg += '### '; break;
	case Bse.UserMessageType.INFO:		msg += '# '; break;
	case Bse.UserMessageType.DEBUG:		msg += '##### '; break;
      }
      msg += e.title + '\n';
      msg += e.text1 + '\n';
      msg += e.text2 + '\n';
      if (e.text3)
	msg += '  \n  \n**' + e.text3 + '**\n';
      Util.show_note (msg);
    },
    sidebar_mouse (e) {
      const sidebar = this.$refs.sidebarcontainer;
      console.assert (sidebar);
      const html_classes = document.documentElement.classList;
      if (e.type == 'mousedown' && !this.listening)
	{<commit_msg>EBEAST: b/projectshell.vue: grow panel2 for piano roll<commit_after><!-- This Source Code Form is licensed MPL-2.0: http://mozilla.org/MPL/2.0 -->

<docs>
  # B-PROJECTSHELL
  Shell for editing and display of a Bse.Project.
  ## Props:
  *project*
  : Implicit *Bse.Project*, using App.bse_project().
</docs>

<style lang="scss">
  @import 'mixins.scss';

  .b-projectshell {
    --b-resize-handle-thickness: #{$b-resize-handle-thickness};
    --b-transition-fast-slide: #{$b-transition-fast-slide};
    width: 100%;
    height: 100%;
    justify-content: space-between;
    align-items: stretch;
  }
  .b-projectshell-panel1 {
    @include b-panel-box;
    flex-grow: 1;
  }
  .b-projectshell-panel2 {
    @include b-panel-box;
    height: 19em;
    flex-shrink: 0;
  }
  .b-projectshell-sidebar {
    padding: 3px;
    overflow: hidden scroll;
    &, * { text-overflow: ellipsis; white-space: nowrap; }
  }
  .b-projectshell-resizer {
    width: var(--b-resize-handle-thickness);
    background: $b-resize-handle-bgcolor;
    border-left: $b-resize-handle-border;
    border-right: $b-resize-handle-border;
    cursor: col-resize;
  }
  html.b-projectshell-during-drag .b-root {
    .b-projectshell-resizer { background: $b-resize-handle-hvcolor; }
    * { cursor: col-resize !important; user-select: none !important; }
  }
</style>

<template>
  <b-vflex class="b-projectshell" >

    <!-- play controls, time display -->
    <b-hflex center style="margin: 5px">
      <b-playcontrols :project="project"> </b-playcontrols>
      <span style="width: 3em"><!-- spacer --></span>
      <b-positionview :song="song"> </b-positionview>
    </b-hflex>

    <b-hflex style="overflow: hidden; flex: 1 1 auto">
      <b-vflex grow1 shrink1>
	<!-- upper main area -->
	<b-hflex class="b-projectshell-panel1" >
	  <b-track-list class="grow1" :song="song"></b-track-list>
	</b-hflex>
	<!-- lower main area -->
	<b-hflex class="b-projectshell-panel2" :style="panel2_style()" >
	  <b-piano-roll class="grow1" :msrc="App.piano_roll_source" v-show="App.panel2 == 'p'" ></b-piano-roll>
	  <b-devicepanel v-show="App.panel2 == 'd'" :track="current_track" />
	</b-hflex>
      </b-vflex>

      <!-- browser -->
      <b-hflex ref="sidebarcontainer" style="width: 0; flex: 0 0 15%" >
	<div     style="flex-grow: 0; flex-shrink: 0" class="b-projectshell-resizer" @mousedown="sidebar_mouse" ></div>
	<b-vflex class="b-projectshell-sidebar" start shrink1 grow1 >
	  <b-treeselector :tree="o.filetree" v-show="App.panel3 == 'b'" ></b-treeselector>
	  <span v-show="App.panel3 == 'i'" >Info Panel</span>
	</b-vflex>
      </b-hflex>
    </b-hflex>

    <!-- status bar -->
    <b-statusbar />

    <!-- popup dialogs -->
    <b-aboutdialog v-model="show_about_dialog" />
    <b-preferencesdialog v-model="show_preferences_dialog" />

  </b-vflex>
</template>

<script>
async function list_sample_files() {
  const crawler = await Bse.server.resource_crawler();
  const entries = await crawler.list_files ('wave', 'user-downloads');
  return Object.freeze ({ entries: entries });
}

function observable_project_data () { // yields reactive Proxy object
  const data = {
    filetree:	     { default: {}, getter: c => list_sample_files(), },
    usermessagehook: { notify: n => Bse.server.on ("usermessage", this.usermessage), },
    // TODO: tracks: { getter: c => list_tracks.call (this), notify: n => this.song.on ("treechange", n), },
    // update current_track if tracks change
    __update__: Util.observable_force_update,
  };
  return this.observable_from_getters (data, () => this.project);
}

export default {
  name: 'b-projectshell',
  data() { return {
    project: undefined,
    o: observable_project_data.call (this),
    current_track: undefined,
    show_about_dialog: false,
    show_preferences_dialog: false,
    song: undefined,
    notifynameclear: () => 0,
  }; },
  watch: {
    show_about_dialog:       function (newval) { if (newval && this.show_preferences_dialog) this.show_preferences_dialog = false; },
    show_preferences_dialog: function (newval) { if (newval && this.show_about_dialog) this.show_about_dialog = false; },
  },
  created() {
    // delete `Shell` dummy on toplebel if present
    let p = this;
    while (p.$parent)
      p = p.$parent;
    if (p.Shell && p.Shell.Shell_placeholder)
      delete p.Shell;
    // inject Shell into all Vue components
    Vue.prototype.Shell = this;
    console.assert (this === p.Shell);
    // provide default project
    this.load_project (Bse.server.last_project());
    // load_project() also forces an update with new Shell properties in place
  },
  mounted() {
    this.switch_panel2 = App.switch_panel2.bind (App);
    Util.add_hotkey ('Backquote', this.switch_panel2);
    this.switch_panel3 = App.switch_panel3.bind (App);
    Util.add_hotkey ('KeyI', this.switch_panel3);
  },
  destroyed() {
    Util.remove_hotkey ('Backquote', this.switch_panel2);
    Util.remove_hotkey ('KeyI', this.switch_panel3);
    this.notifynameclear();
  },
  provide () { return { 'b-projectshell': this }; },
  methods: {
    panel2_style() {
      return App.panel2 == 'p' ? 'flex-grow: 5' : '';
    },
    usermessage (e) {
      let msg = '';
      switch (e.umtype)
      {
	case Bse.UserMessageType.ERROR:		msg += '### '; break;
	case Bse.UserMessageType.WARNING:	msg += '### '; break;
	case Bse.UserMessageType.INFO:		msg += '# '; break;
	case Bse.UserMessageType.DEBUG:		msg += '##### '; break;
      }
      msg += e.title + '\n';
      msg += e.text1 + '\n';
      msg += e.text2 + '\n';
      if (e.text3)
	msg += '  \n  \n**' + e.text3 + '**\n';
      Util.show_note (msg);
    },
    sidebar_mouse (e) {
      const sidebar = this.$refs.sidebarcontainer;
      console.assert (sidebar);
      const html_classes = document.documentElement.classList;
      if (e.type == 'mousedown' && !this.listening)
	{
{'fraction_non_alphanumeric': 0.17093795840883597, 'fraction_numerical': 0.009578048149106911, 'mean_word_length': 3.0925141242937855, 'pattern_counts': {'":': 0, '<': 105, '<?xml version=': 0, '>': 117, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 2, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '7525170', 'n_tokens_mistral': 4195, 'n_tokens_neox': 4086, 'n_words': 847}