Chandra

 view release on metacpan or  search on metacpan

examples/contextmenu_example.pl  view on Meta::CPAN

#!/usr/bin/env perl
#
# Example: Context Menus
#
# Right-click context menus with static items, dynamic items,
# submenus, icons, checkable items, and keyboard shortcut hints.
#

use strict;
use warnings;
use FindBin;
use lib "$FindBin::Bin/../blib/lib", "$FindBin::Bin/../blib/arch";
use Chandra::App;

my $app = Chandra::App->new(
    title  => 'Context Menu Example',
    width  => 600,
    height => 450,
    debug  => 1,
);

# ---- Static context menu on the editor area ----

$app->context_menu('#editor', [
    { label => 'Select All',  action => sub {
        $app->eval("var e=document.getElementById('editor');var r=document.createRange();r.selectNodeContents(e);var s=window.getSelection();s.removeAllRanges();s.addRange(r);");
        log_action('Select All');
      }, shortcut => 'Ctrl+A', icon => "\x{2610}" },
    { label => 'Clear Editor', action => sub {
        $app->eval("document.getElementById('editor').textContent='';");
        log_action('Clear Editor');
      }, icon => "\x{1f5d1}" },
    { separator => 1 },
    { label => 'Word Wrap',  checkable => 1, checked => 1,
      action => sub {
        my $on = $_[0] ? 'pre-wrap' : 'pre';
        $app->eval("document.getElementById('editor').style.whiteSpace='$on';");
        log_action("Word Wrap: " . ($_[0] ? 'ON' : 'OFF'));
      } },
    { separator => 1 },
    { label => 'Theme', submenu => [
        { label => 'Light',  action => sub {
            $app->eval("var e=document.getElementById('editor');e.style.background='#fff';e.style.color='#333';");
            log_action('Theme: Light');
          } },
        { label => 'Dark',   action => sub {
            $app->eval("var e=document.getElementById('editor');e.style.background='#1e1e1e';e.style.color='#d4d4d4';");
            log_action('Theme: Dark');
          } },
        { label => 'Sepia',  action => sub {
            $app->eval("var e=document.getElementById('editor');e.style.background='#f4ecd8';e.style.color='#5b4636';");
            log_action('Theme: Sepia');
          } },
    ]},
    { label => 'Insert Snippet', submenu => [
        { label => 'Hello World', action => sub {
            $app->eval("var e=document.getElementById('editor');e.textContent+='\\n# Hello World\\nprint \"Hello, World!\\\\n\";\\n';");
            log_action('Insert: Hello World');
          } },
        { label => 'Read File', action => sub {
            $app->eval('var e=document.getElementById("editor");e.textContent+="\nopen my $fh, \"<\", $file or die $!;\nmy $data = do { local $/; <$fh> };\nclose $fh;\n";');
            log_action('Insert: Read File');
          } },
    ]},
    { label => 'Increase Font', action => sub {
        $app->eval("var e=document.getElementById('editor');var s=parseFloat(getComputedStyle(e).fontSize);e.style.fontSize=(s+2)+'px';");
        log_action('Increase Font');
      }, shortcut => 'Ctrl+=' },
    { label => 'Decrease Font', action => sub {
        $app->eval("var e=document.getElementById('editor');var s=parseFloat(getComputedStyle(e).fontSize);if(s>8)e.style.fontSize=(s-2)+'px';");
        log_action('Decrease Font');
      }, shortcut => 'Ctrl+-' },
]);

# ---- Dynamic context menu on list items ----

examples/contextmenu_example.pl  view on Meta::CPAN

    my $name = $id;
    $name =~ s/^file-//;
    return [
        { label => "Open $name", icon => "\x{1f4c4}", action => sub {
            $app->eval("document.getElementById('editor').textContent='# Contents of $name\\n# (loaded from sidebar)\\nprint \"Editing $name\\\\n\";\\n';");
            $app->eval("document.querySelectorAll('.list-item').forEach(function(el){el.style.background='';});document.getElementById('$id').style.background='#b8d4f0';");
            log_action("Opened $name");
          } },
        { label => "Rename $name", icon => "\x{270f}", action => sub {
            $app->eval("var el=document.getElementById('$id');el.textContent='renamed-'+el.textContent;");
            log_action("Renamed $name");
          } },
        { label => "Duplicate $name", icon => "\x{2398}", action => sub {
            $app->eval("var el=document.getElementById('$id');var cl=el.cloneNode(true);cl.id='';cl.textContent=el.textContent+' (copy)';el.parentNode.insertBefore(cl,el.nextSibling);");
            log_action("Duplicated $name");
          } },
        { separator => 1 },
        { label => "Delete $name", icon => "\x{1f5d1}", action => sub {
            $app->eval("var el=document.getElementById('$id');if(el)el.remove();");
            log_action("Deleted $name");
          } },
    ];
});

sub log_action {
    my ($msg) = @_;
    $app->eval(
        "document.getElementById('log').innerHTML += "
        . "'<div class=\"log-entry\">" . $msg . "</div>';"
        . "document.getElementById('log').scrollTop = "
        . "document.getElementById('log').scrollHeight;"
    );
}

$app->set_content(<<'HTML');
<!DOCTYPE html>
<html>
<head>
<style>
    body { font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
           margin: 0; display: flex; flex-direction: column; height: 100vh;
           background: #f5f5f5; color: #333; }
    .header { padding: 12px 16px; background: #2c3e50; color: white;
              font-size: 14px; font-weight: 600; }
    .main { display: flex; flex: 1; overflow: hidden; }
    .sidebar { width: 200px; background: #ecf0f1; border-right: 1px solid #ddd;
               padding: 8px 0; overflow-y: auto; }
    .list-item { padding: 8px 16px; cursor: pointer; font-size: 13px; }
    .list-item:hover { background: #dfe6e9; }
    #editor { flex: 1; padding: 16px; font-family: monospace; font-size: 13px;
              white-space: pre-wrap; background: #fff; overflow-y: auto;
              line-height: 1.6; min-height: 200px; }
    #log { height: 120px; overflow-y: auto; background: #2d2d2d; color: #0f0;
           font-family: monospace; font-size: 12px; padding: 8px; border-top: 2px solid #444; }
    .log-entry { padding: 2px 0; }
    .hint { color: #888; font-size: 12px; padding: 8px 16px; background: #fafafa;
            border-top: 1px solid #eee; }
</style>
</head>
<body>
    <div class="header">Context Menu Example</div>
    <div class="main">
        <div class="sidebar">
            <div class="list-item" id="file-readme">README.md</div>
            <div class="list-item" id="file-main">main.pl</div>
            <div class="list-item" id="file-config">config.yml</div>
            <div class="list-item" id="file-test">test.t</div>
            <div class="list-item" id="file-lib">lib/App.pm</div>
        </div>
        <div id="editor">use strict;
use warnings;

sub hello {
    my ($name) = @_;
    print "Hello, $name!\n";
}

hello("World");
</div>
    </div>
    <div class="hint">Right-click on the editor or file list items to see context menus</div>
    <div id="log"></div>
</body>
</html>
HTML

$app->run;



( run in 1.867 second using v1.01-cache-2.11-cpan-39bf76dae61 )