Does anyone know of a low level (no frameworks) example of a drag & drop, re-order-able list?

heh I hate frameworks so this is easy as pie for me…

this is what I coded for my students during my lectures few years back:

This is the main engine code (complete project + exe is in that zip above):

//---------------------------------------------------------------------------
//--- Constants: ------------------------------------------------------------
//---------------------------------------------------------------------------
const int _select_max_l=16;
const int _select_max_ll=_select_max_l*_select_max_l;
const int _half_size=10;
const int _atoms_max=32;
//---------------------------------------------------------------------------
enum _atom_type_enum        //++++
        {
        _atom_type_non=0,
        _atom_type_kruh,
        _atom_type_stvorec,
        _atom_type_enum_end
        };
//---------------------------------------------------------------------------
enum _editor_edit_mode_enum //****
        {
        _editor_edit_mode_non=0,
        _editor_edit_mode_move,
        _editor_edit_mode_mov,
        _editor_edit_mode_add_kruh,
        _editor_edit_mode_add_stvorec,
        _editor_edit_mode_del,
        _editor_edit_mode_enum_end
        };
//---------------------------------------------------------------------------
//--- viewer: ---------------------------------------------------------------
//---------------------------------------------------------------------------
class viewer
        {
public: int x0,y0;
        viewer() { x0=0; y0=0; }
        void world2screen(int &sx,int &sy,int wx,int wy) { sx=wx-x0; sy=wy-y0; }
        void screen2world(int &wx,int &wy,int sx,int sy) { wx=sx+x0; wy=sy+y0; }
        void world2screen(int &sl,int wl) { sl=wl; }
        void screen2world(int &wl,int sl) { wl=sl; }
        };
//---------------------------------------------------------------------------
//--- atom kruh: ------------------------------------------------------------
//---------------------------------------------------------------------------
class atom_kruh
        {
public: int x,y,r;      // world coordinates
        TColor col0,col1,col2;
        AnsiString str;
        atom_kruh() { x=0; y=0; r=_half_size; str=""; col0=clBlue; col1=clAqua; col2=clWhite; }
        void draw(TCanvas *scr,const viewer &view)
            {
            int xx,yy,rr;
            view.world2screen(xx,yy,x,y);
            view.world2screen(rr,r);
            scr->Brush->Color=col0;
            scr->Pen  ->Color=col1;
            scr->Font ->Color=col2;
            scr->Ellipse(xx-rr,yy-rr,xx+rr,yy+rr);
            scr->Brush->Style=bsClear;
            xx-=scr->TextWidth(str)>>1;
            yy-=scr->TextHeight(str)>>1;
            scr->TextOutA(xx,yy,str);
            scr->Brush->Style=bsSolid;
            }
        bool select(int &ll,int wx,int wy)
            {
            int qq,xx,yy;
            xx=wx-x; xx*=xx;
            yy=wy-y; yy*=yy;
            qq=xx+yy;
            if ((qq<=_select_max_ll)&&((qq<=ll)||(ll<0))) { ll=qq; return true; }
            return false;
            }
        };
//---------------------------------------------------------------------------
//--- atom kruh: ------------------------------------------------------------
//---------------------------------------------------------------------------
class atom_stvorec
        {
public: int x,y,r;      // world coordinates
        TColor col0,col1,col2;
        AnsiString str;
        atom_stvorec() { x=0; y=0; r=_half_size; str=""; col0=clBlue; col1=clAqua; col2=clWhite; }
        void draw(TCanvas *scr,const viewer &view)
            {
            int xx,yy,rr;
            view.world2screen(xx,yy,x,y);
            view.world2screen(rr,r);
            scr->Brush->Color=col0;
            scr->Pen  ->Color=col1;
            scr->Font ->Color=col2;
            scr->Rectangle(xx-rr,yy-rr,xx+rr,yy+rr);
            scr->Brush->Style=bsClear;
            xx-=scr->TextWidth(str)>>1;
            yy-=scr->TextHeight(str)>>1;
            scr->TextOutA(xx,yy,str);
            scr->Brush->Style=bsSolid;
            }
        bool select(int &ll,int wx,int wy)
            {
            int qq,xx,yy;
            xx=wx-x; xx*=xx;
            yy=wy-y; yy*=yy;
            qq=xx+yy;
            if ((qq<=_select_max_ll)&&((qq<=ll)||(ll<0))) { ll=qq; return true; }
            return false;
            }
        };
//---------------------------------------------------------------------------
//---------------------------------------------------------------------------
//---------------------------------------------------------------------------
class editor
        {
public: Graphics::TBitmap *bmp;         // back buffer
        int xs,ys;

        int sel_ix,sel_tp;              // actual mouse selected item
        int edit_mode;                  // selected edit tool
        viewer view;                    // view
        bool redraw;                    // redraw needed?
        bool locked;                    // edit in progress?

        WORD key,key0;
        int mx,my,mx0,my0;
        TShiftState sh,sh0;

        atom_kruh    kruh[_atoms_max];  // all object lists
        atom_stvorec stvorec[_atoms_max];
        int kruhov;
        int stvorcov;

        editor();
        ~editor();

        void resize(int _xs,int _ys);   // interface with window
        void draw();
        void mouse(int x,int y,TShiftState s) { mx0=mx; my0=my; sh0=sh; mx=x; my=y; sh=s; edit(); }
        void keys(WORD k,TShiftState s) { key0=key; sh0=sh; key=k; sh=s; edit(); }

        void select();                  // helper functions
        void edit();
        void move       (bool q0,bool q1,int x,int y,int dx,int dy);
        void mov        (bool q0,bool q1,int x,int y,int dx,int dy);
        void add_kruh   (bool q0,bool q1,int x,int y,int dx,int dy);
        void add_stvorec(bool q0,bool q1,int x,int y,int dx,int dy);
        void del        (bool q0,bool q1,int x,int y,int dx,int dy);
        };
//---------------------------------------------------------------------------
editor::editor()
        {
        bmp=new Graphics::TBitmap;
        resize(1,1);

        sel_ix=-1;
        sel_tp=_atom_type_non;
        edit_mode=_editor_edit_mode_non;
        key=0;  key0=0;
        mx=0;   mx0=0;
        my=0;   my0=0;
        locked=false;

        kruhov=0;
        stvorcov=0;
        }
//---------------------------------------------------------------------------
editor::~editor()
        {
        delete bmp;
        }
//---------------------------------------------------------------------------
void editor::resize(int _xs,int _ys)
        {
        bmp->Width=_xs;
        bmp->Height=_ys;
        xs=bmp->Width;
        ys=bmp->Height;
        redraw=true;
        }
//---------------------------------------------------------------------------
void editor::draw()
        {
        int i;
        if (!redraw) return;
        redraw=false;
        bmp->Canvas->Brush->Color=clBlack;
        bmp->Canvas->FillRect(Rect(0,0,xs,ys));
        //++++
        for (i=0;i<kruhov  ;i++) kruh[i]   .draw(bmp->Canvas,view);
        for (i=0;i<stvorcov;i++) stvorec[i].draw(bmp->Canvas,view);
        }
//---------------------------------------------------------------------------
void editor::select()
        {
        int i,wx,wy,ll;
        int sel_tp0=sel_tp; sel_tp=_atom_type_non;
        int sel_ix0=sel_ix; sel_ix=-1;
        view.screen2world(wx,wy,mx,my);
        //++++
        ll=-1;
        for (i=0;i<kruhov  ;i++) if (kruh[i]   .select(ll,wx,wy)) { sel_tp=_atom_type_kruh;    sel_ix=i; };
        for (i=0;i<stvorcov;i++) if (stvorec[i].select(ll,wx,wy)) { sel_tp=_atom_type_stvorec; sel_ix=i; };
        if (sel_tp!=sel_tp0) redraw=true;
        if (sel_ix!=sel_ix0) redraw=true;
        }
//---------------------------------------------------------------------------
void editor::edit()
        {
        bool q0,q1;
        int x,y,dx,dy;
        x=mx; dx=mx-mx0;
        y=my; dy=my-my0;
        view.screen2world( x, y, x, y);
        view.screen2world(dx,dx);
        view.screen2world(dy,dy);
        q0=sh0.Contains(ssLeft);
        q1=sh .Contains(ssLeft);
        if (!locked) select();
        //****
        if(edit_mode==_editor_edit_mode_mov)        mov         (q0,q1,x,y,dx,dy);
        if(edit_mode==_editor_edit_mode_add_kruh)   add_kruh    (q0,q1,x,y,dx,dy);
        if(edit_mode==_editor_edit_mode_add_stvorec)add_stvorec (q0,q1,x,y,dx,dy);
        if(edit_mode==_editor_edit_mode_del)        del         (q0,q1,x,y,dx,dy);

        q0=sh0.Contains(ssRight);
        q1=sh .Contains(ssRight);
        if (!locked) move(q0,q1,x,y,dx,dy);
        }
//---------------------------------------------------------------------------
void editor::move   (bool q0,bool q1,int x,int y,int dx,int dy)
        {
        if ((sel_ix>=0)&&(sel_tp!=_atom_type_non)) return;
        if (q1)
            {
            view.x0-=dx;
            view.y0-=dy;
            redraw=true;
            }
        }
//---------------------------------------------------------------------------
void editor::mov        (bool q0,bool q1,int x,int y,int dx,int dy)
        {
        if ((!locked)&&((sel_ix<0)||(sel_tp==_atom_type_non))) return;
        locked=false;
        if ((q1)||((q0)&&(!q1)))
            {
            //++++
            if (sel_tp==_atom_type_kruh)
                {
                kruh[sel_ix].x=x;
                kruh[sel_ix].y=y;
                }
            if (sel_tp==_atom_type_stvorec)
                {
                stvorec[sel_ix].x=x;
                stvorec[sel_ix].y=y;
                }
            locked=true;
            }
        if (!q1) locked=false;
        redraw=true;
        }
//---------------------------------------------------------------------------
void editor::add_kruh   (bool q0,bool q1,int x,int y,int dx,int dy)
        {
        if ((!locked)&&(sel_ix>=0)&&(sel_tp!=_atom_type_non)) return;
        locked=false;
        if (kruhov>=_atoms_max) return;
        if ((!q0)&&( q1))
            {
            sel_tp=_atom_type_kruh;
            sel_ix=kruhov;
            kruhov++;
            kruh[sel_ix].x=x;
            kruh[sel_ix].y=y;
            kruh[sel_ix].str=kruhov;
            locked=true;
            }
        if (( q0)&&( q1))
            {
            kruh[sel_ix].x=x;
            kruh[sel_ix].y=y;
            locked=true;
            }
        if (( q0)&&(!q1))
            {
            kruh[sel_ix].x=x;
            kruh[sel_ix].y=y;
            }
        if ((!q0)&&(!q1))
            {
            }
        redraw=true;
        }
//---------------------------------------------------------------------------
void editor::add_stvorec(bool q0,bool q1,int x,int y,int dx,int dy)
        {
        if ((!locked)&&(sel_ix>=0)&&(sel_tp!=_atom_type_non)) return;
        locked=false;
        if (stvorcov>=_atoms_max) return;
        if ((!q0)&&( q1))
            {
            sel_tp=_atom_type_stvorec;
            sel_ix=stvorcov;
            stvorcov++;
            stvorec[sel_ix].x=x;
            stvorec[sel_ix].y=y;
            stvorec[sel_ix].str=stvorcov;
            locked=true;
            }
        if (( q0)&&( q1))
            {
            stvorec[sel_ix].x=x;
            stvorec[sel_ix].y=y;
            locked=true;
            }
        if (( q0)&&(!q1))
            {
            stvorec[sel_ix].x=x;
            stvorec[sel_ix].y=y;
            }
        if ((!q0)&&(!q1))
            {
            }
        redraw=true;
        }
//---------------------------------------------------------------------------
void editor::del        (bool q0,bool q1,int x,int y,int dx,int dy)
        {
        locked=false;
        if ((sel_ix<0)||(sel_tp==_atom_type_non)) return;
        if ((!q0)&&( q1))
            {
            //++++
            if (sel_tp==_atom_type_kruh)
             if (kruhov>0)
                {
                kruhov--;
                kruh[sel_ix]=kruh[kruhov];
                }
            if (sel_tp==_atom_type_stvorec)
             if (stvorcov>0)
                {
                stvorcov--;
                stvorec[sel_ix]=stvorec[stvorcov];
                }
            sel_ix=-1;
            sel_tp=_atom_type_non;
            }
        redraw=true;
        }
//---------------------------------------------------------------------------

sorry for that its not entirely in English

  • kruh means circle
  • stvorec means square

This is code for window (BDS2006 VCL style)

//$$---- Form CPP ----
//---------------------------------------------------------------------------
#include <vcl.h>
#pragma hdrstop
#include "Unit1.h"
#include "editor.h"
//---------------------------------------------------------------------------
#pragma package(smart_init)
#pragma resource "*.dfm"
TForm1 *Form1;
editor edit;
int x0,y0;
//---------------------------------------------------------------------------
//---------------------------------------------------------------------------
//---------------------------------------------------------------------------
void draw() // redraw app screen
    {
    edit.draw();
    Form1->Canvas->Draw(x0,y0,edit.bmp);
    // here just some info print outs
    int dy=16,x=x0,y=y0-dy;
    Form1->Canvas->Font->Color=clAqua;
    Form1->Canvas->Brush->Style=bsClear;
    Form1->Canvas->TextOutA(x,y+=dy,AnsiString().sprintf("locked: %i",edit.locked));
    Form1->Canvas->TextOutA(x,y+=dy,AnsiString().sprintf("Key: %d",edit.key));
    Form1->Canvas->TextOutA(x,y+=dy,AnsiString().sprintf("sel_tp: %i",edit.sel_tp));
    Form1->Canvas->TextOutA(x,y+=dy,AnsiString().sprintf("sel_ix: %i",edit.sel_ix));
    Form1->Canvas->TextOutA(x,y+=dy,AnsiString().sprintf("kruhov: %i",edit.kruhov));
    Form1->Canvas->TextOutA(x,y+=dy,AnsiString().sprintf("stvorcov: %i",edit.stvorcov));
    Form1->Canvas->Brush->Style=bsSolid;
    }
//---------------------------------------------------------------------------
__fastcall TForm1::TForm1(TComponent* Owner):TForm(Owner) // init app
    {
    // select tool on app start
    bt_tool_kruhClick(this);
    }
//--- window events: ---------------------------------------------------------
void __fastcall TForm1::FormPaint(TObject *Sender) { draw(); }
void __fastcall TForm1::FormResize(TObject *Sender) { x0=pan_top->Left; y0=pan_top->Height; edit.resize(ClientWidth-x0,ClientHeight-y0); draw(); }
void __fastcall TForm1::FormActivate(TObject *Sender) { draw(); }
//---------------------------------------------------------------------------
void __fastcall TForm1::FormKeyDown(TObject *Sender, WORD &Key, TShiftState Shift)                          { edit.keys(Key,Shift); draw(); }
void __fastcall TForm1::FormMouseMove(TObject *Sender, TShiftState Shift, int X, int Y)                     { edit.mouse(X-x0,Y-y0,Shift); draw(); }
void __fastcall TForm1::FormMouseDown(TObject *Sender, TMouseButton Button, TShiftState Shift, int X, int Y){ edit.mouse(X-x0,Y-y0,Shift); draw(); }
void __fastcall TForm1::FormMouseUp(TObject *Sender, TMouseButton Button, TShiftState Shift, int X, int Y)  { edit.mouse(X-x0,Y-y0,Shift); draw(); }
//---------------------------------------------------------------------------
void __fastcall TForm1::bt_tool_kruhClick(TObject *Sender) // event on any tool button click
    {
    // select editor tool mode ...
    edit.edit_mode=_editor_edit_mode_non;
    if (bt_tool_kruh   ->Down) edit.edit_mode=_editor_edit_mode_add_kruh;
    if (bt_tool_stvorec->Down) edit.edit_mode=_editor_edit_mode_add_stvorec;
    if (bt_tool_move   ->Down) edit.edit_mode=_editor_edit_mode_mov;
    if (bt_tool_delete ->Down) edit.edit_mode=_editor_edit_mode_del;
    }
//---------------------------------------------------------------------------

Window has only 4 tool buttons (locked together by same guid so only one can be down at a time)

  • add circle tool
  • add square tool
  • move tool
  • delete tool

Everything is statically allocated for simplicity

[edit1] more info

  1. create gfx object data type/class (atom_xxxx)

    it should hold the size,position,shape of visual gfx representation of object. Add connection variables (object type and object index to what it should be connected). Add the real object/data inside

  2. object ID

    I am using int tp,ix;

    • tp means type of object
    • ix means index in list of object of type tp
  3. editor engine

    this should be also class or set of variables and functions. It should hold the whole edited world (lists of objects):

    • visualization variables like screen/backbufer bitmap or rendering context, mouse position, selection list
    • add functions/events like onmouse, onkey, draw,
    • add edit function it should be able to select,add,del,move (drag&drop) objects. Ideally controlled by set of command to ease up the undo/redo operation
    • add undo/redo
    • add save,load
    • add your desired simulation functionality

    and that is all if I did not forgot something.

  4. create Application GUI interface

    so create window, add panel with buttons for each tool,menu and whatever you need. Add events for mouse,keyboard,repaint,resize,drag&drop,… My example looks like this:

    editor app

    Add editor edit; to it globally or as a member of it. Member option is better if you want to have MDI later. Add events interface between edit and window (the second source code).

[Notes]

  • ++++ marks part of code where you need add changes if any atom type is added to the system
  • **** marks part of code where you need add changes if any edit mode is added to the system

Sorry for not add more commented code if you need clarify something comment me.

Hope it helps a little …

Leave a Comment