github.com/secoba/wails/v2@v2.6.4/internal/frontend/desktop/windows/winc/imageviewbox.go (about)

     1  //go:build windows
     2  
     3  /*
     4   * Copyright (C) 2019 The Winc Authors. All Rights Reserved.
     5   */
     6  
     7  package winc
     8  
     9  import (
    10  	"fmt"
    11  	"time"
    12  
    13  	"github.com/secoba/wails/v2/internal/frontend/desktop/windows/winc/w32"
    14  )
    15  
    16  type direction int
    17  
    18  const (
    19  	DirNone direction = iota
    20  	DirX
    21  	DirY
    22  	DirX2
    23  	DirY2
    24  )
    25  
    26  var ImageBoxPen = NewPen(w32.PS_GEOMETRIC, 2, NewSolidColorBrush(RGB(140, 140, 220)))
    27  var ImageBoxHiPen = NewPen(w32.PS_GEOMETRIC, 2, NewSolidColorBrush(RGB(220, 140, 140)))
    28  var ImageBoxMarkBrush = NewSolidColorBrush(RGB(40, 40, 40))
    29  var ImageBoxMarkPen = NewPen(w32.PS_GEOMETRIC, 2, ImageBoxMarkBrush)
    30  
    31  type ImageBox struct {
    32  	Name         string
    33  	Type         int
    34  	X, Y, X2, Y2 int
    35  
    36  	underMouse bool // dynamic value
    37  }
    38  
    39  func (b *ImageBox) Rect() *Rect {
    40  	return NewRect(b.X, b.Y, b.X2, b.Y2)
    41  }
    42  
    43  // ImageViewBox is image view with boxes.
    44  type ImageViewBox struct {
    45  	ControlBase
    46  
    47  	bmp       *Bitmap
    48  	mouseLeft bool
    49  	modified  bool // used by GUI to see if any image box modified
    50  
    51  	add bool
    52  
    53  	Boxes   []*ImageBox // might be persisted to file
    54  	dragBox *ImageBox
    55  	selBox  *ImageBox
    56  
    57  	dragStartX, dragStartY int
    58  	resize                 direction
    59  
    60  	onSelectedChange EventManager
    61  	onAdd            EventManager
    62  	onModify         EventManager
    63  }
    64  
    65  func NewImageViewBox(parent Controller) *ImageViewBox {
    66  	iv := new(ImageViewBox)
    67  
    68  	iv.InitWindow("winc_ImageViewBox", parent, w32.WS_EX_CONTROLPARENT, w32.WS_CHILD|w32.WS_VISIBLE)
    69  	RegMsgHandler(iv)
    70  
    71  	iv.SetFont(DefaultFont)
    72  	iv.SetText("")
    73  	iv.SetSize(200, 65)
    74  
    75  	return iv
    76  }
    77  
    78  func (iv *ImageViewBox) OnSelectedChange() *EventManager {
    79  	return &iv.onSelectedChange
    80  }
    81  
    82  func (iv *ImageViewBox) OnAdd() *EventManager {
    83  	return &iv.onAdd
    84  }
    85  
    86  func (iv *ImageViewBox) OnModify() *EventManager {
    87  	return &iv.onModify
    88  }
    89  
    90  func (iv *ImageViewBox) IsModified() bool          { return iv.modified }
    91  func (iv *ImageViewBox) SetModified(modified bool) { iv.modified = modified }
    92  func (iv *ImageViewBox) IsLoaded() bool            { return iv.bmp != nil }
    93  func (iv *ImageViewBox) AddMode() bool             { return iv.add }
    94  func (iv *ImageViewBox) SetAddMode(add bool)       { iv.add = add }
    95  func (iv *ImageViewBox) HasSelected() bool         { return iv.selBox != nil && iv.bmp != nil }
    96  
    97  func (iv *ImageViewBox) wasModified() {
    98  	iv.modified = true
    99  	iv.onModify.Fire(NewEvent(iv, nil))
   100  }
   101  
   102  func (iv *ImageViewBox) DeleteSelected() {
   103  	if iv.selBox != nil {
   104  		for i, b := range iv.Boxes {
   105  			if b == iv.selBox {
   106  				iv.Boxes = append(iv.Boxes[:i], iv.Boxes[i+1:]...)
   107  				iv.selBox = nil
   108  				iv.Invalidate(true)
   109  				iv.wasModified()
   110  				iv.onSelectedChange.Fire(NewEvent(iv, nil))
   111  				return
   112  			}
   113  		}
   114  	}
   115  }
   116  
   117  func (iv *ImageViewBox) NameSelected() string {
   118  	if iv.selBox != nil {
   119  		return iv.selBox.Name
   120  	}
   121  	return ""
   122  }
   123  
   124  func (iv *ImageViewBox) SetNameSelected(name string) {
   125  	if iv.selBox != nil {
   126  		iv.selBox.Name = name
   127  		iv.wasModified()
   128  	}
   129  }
   130  
   131  func (iv *ImageViewBox) TypeSelected() int {
   132  	if iv.selBox != nil {
   133  		return iv.selBox.Type
   134  	}
   135  	return 0
   136  }
   137  
   138  func (iv *ImageViewBox) SetTypeSelected(typ int) {
   139  	if iv.selBox != nil {
   140  		iv.selBox.Type = typ
   141  		iv.wasModified()
   142  	}
   143  }
   144  
   145  func (ib *ImageViewBox) updateHighlight(x, y int) bool {
   146  	var changed bool
   147  	for _, b := range ib.Boxes {
   148  		under := x >= b.X && y >= b.Y && x <= b.X2 && y <= b.Y2
   149  		if b.underMouse != under {
   150  			changed = true
   151  		}
   152  		b.underMouse = under
   153  		/*if sel {
   154  			break // allow only one to be underMouse
   155  		}*/
   156  	}
   157  	return changed
   158  }
   159  
   160  func (ib *ImageViewBox) isUnderMouse(x, y int) *ImageBox {
   161  	for _, b := range ib.Boxes {
   162  		if x >= b.X && y >= b.Y && x <= b.X2 && y <= b.Y2 {
   163  			return b
   164  		}
   165  	}
   166  	return nil
   167  }
   168  
   169  func (ib *ImageViewBox) getCursor(x, y int) uint16 {
   170  	for _, b := range ib.Boxes {
   171  		switch d := ib.resizingDirection(b, x, y); d {
   172  		case DirY, DirY2:
   173  			return w32.IDC_SIZENS
   174  		case DirX, DirX2:
   175  			return w32.IDC_SIZEWE
   176  		}
   177  		// w32.IDC_SIZEALL or w32.IDC_SIZE for resize
   178  	}
   179  	return w32.IDC_ARROW
   180  }
   181  
   182  func (ib *ImageViewBox) resizingDirection(b *ImageBox, x, y int) direction {
   183  	if b == nil {
   184  		return DirNone
   185  	}
   186  	switch {
   187  	case b.X == x || b.X == x-1 || b.X == x+1:
   188  		return DirX
   189  	case b.X2 == x || b.X2 == x-1 || b.X2 == x+1:
   190  		return DirX2
   191  	case b.Y == y || b.Y == y-1 || b.Y == y+1:
   192  		return DirY
   193  	case b.Y2 == y || b.Y2 == y-1 || b.Y2 == y+1:
   194  		return DirY2
   195  	}
   196  	return DirNone
   197  }
   198  
   199  func (ib *ImageViewBox) resizeToDirection(b *ImageBox, x, y int) {
   200  	switch ib.resize {
   201  	case DirX:
   202  		b.X = x
   203  	case DirY:
   204  		b.Y = y
   205  	case DirX2:
   206  		b.X2 = x
   207  	case DirY2:
   208  		b.Y2 = y
   209  	}
   210  }
   211  
   212  func (ib *ImageViewBox) drag(b *ImageBox, x, y int) {
   213  	w, h := b.X2-b.X, b.Y2-b.Y
   214  
   215  	nx := ib.dragStartX - b.X
   216  	ny := ib.dragStartY - b.Y
   217  
   218  	b.X = x - nx
   219  	b.Y = y - ny
   220  	b.X2 = b.X + w
   221  	b.Y2 = b.Y + h
   222  
   223  	ib.dragStartX, ib.dragStartY = x, y
   224  }
   225  
   226  func (iv *ImageViewBox) DrawImageFile(filepath string) (err error) {
   227  	iv.bmp, err = NewBitmapFromFile(filepath, RGB(255, 255, 0))
   228  	iv.selBox = nil
   229  	iv.modified = false
   230  	iv.onSelectedChange.Fire(NewEvent(iv, nil))
   231  	iv.onModify.Fire(NewEvent(iv, nil))
   232  	return
   233  }
   234  
   235  func (iv *ImageViewBox) DrawImage(bmp *Bitmap) {
   236  	iv.bmp = bmp
   237  	iv.selBox = nil
   238  	iv.modified = false
   239  	iv.onSelectedChange.Fire(NewEvent(iv, nil))
   240  	iv.onModify.Fire(NewEvent(iv, nil))
   241  }
   242  
   243  func (iv *ImageViewBox) WndProc(msg uint32, wparam, lparam uintptr) uintptr {
   244  	switch msg {
   245  	case w32.WM_SIZE, w32.WM_SIZING:
   246  		iv.Invalidate(true)
   247  
   248  	case w32.WM_ERASEBKGND:
   249  		return 1 // important
   250  
   251  	case w32.WM_CREATE:
   252  		internalTrackMouseEvent(iv.hwnd)
   253  
   254  	case w32.WM_PAINT:
   255  		if iv.bmp != nil {
   256  			canvas := NewCanvasFromHwnd(iv.hwnd)
   257  			defer canvas.Dispose()
   258  			iv.SetSize(iv.bmp.Size())
   259  			canvas.DrawBitmap(iv.bmp, 0, 0)
   260  
   261  			for _, b := range iv.Boxes {
   262  				// old code used NewSystemColorBrush(w32.COLOR_BTNFACE) w32.COLOR_WINDOW
   263  				pen := ImageBoxPen
   264  				if b.underMouse {
   265  					pen = ImageBoxHiPen
   266  				}
   267  				canvas.DrawRect(b.Rect(), pen)
   268  
   269  				if b == iv.selBox {
   270  					x1 := []int{b.X, b.X2, b.X2, b.X}
   271  					y1 := []int{b.Y, b.Y, b.Y2, b.Y2}
   272  
   273  					for i := 0; i < len(x1); i++ {
   274  						r := NewRect(x1[i]-2, y1[i]-2, x1[i]+2, y1[i]+2)
   275  						canvas.DrawFillRect(r, ImageBoxMarkPen, ImageBoxMarkBrush)
   276  					}
   277  
   278  				}
   279  			}
   280  		}
   281  
   282  	case w32.WM_MOUSEMOVE:
   283  		x, y := genPoint(lparam)
   284  
   285  		if iv.dragBox != nil {
   286  			if iv.resize == DirNone {
   287  				iv.drag(iv.dragBox, x, y)
   288  				iv.wasModified()
   289  			} else {
   290  				iv.resizeToDirection(iv.dragBox, x, y)
   291  				iv.wasModified()
   292  			}
   293  			iv.Invalidate(true)
   294  
   295  		} else {
   296  			if !iv.add {
   297  				w32.SetCursor(w32.LoadCursorWithResourceID(0, iv.getCursor(x, y)))
   298  			}
   299  			//  do not call repaint if underMouse item did not change.
   300  			if iv.updateHighlight(x, y) {
   301  				iv.Invalidate(true)
   302  			}
   303  		}
   304  
   305  		if iv.mouseLeft {
   306  			internalTrackMouseEvent(iv.hwnd)
   307  			iv.mouseLeft = false
   308  		}
   309  
   310  	case w32.WM_MOUSELEAVE:
   311  		iv.dragBox = nil
   312  		iv.mouseLeft = true
   313  		iv.updateHighlight(-1, -1)
   314  		iv.Invalidate(true)
   315  
   316  	case w32.WM_LBUTTONUP:
   317  		iv.dragBox = nil
   318  
   319  	case w32.WM_LBUTTONDOWN:
   320  		x, y := genPoint(lparam)
   321  		if iv.add {
   322  			now := time.Now()
   323  			s := fmt.Sprintf("field%s", now.Format("020405"))
   324  			b := &ImageBox{Name: s, underMouse: true, X: x, Y: y, X2: x + 150, Y2: y + 30}
   325  			iv.Boxes = append(iv.Boxes, b)
   326  			iv.selBox = b
   327  			iv.wasModified()
   328  			iv.onAdd.Fire(NewEvent(iv, nil))
   329  		} else {
   330  			iv.dragBox = iv.isUnderMouse(x, y)
   331  			iv.selBox = iv.dragBox
   332  			iv.dragStartX, iv.dragStartY = x, y
   333  			iv.resize = iv.resizingDirection(iv.dragBox, x, y)
   334  		}
   335  		iv.Invalidate(true)
   336  		iv.onSelectedChange.Fire(NewEvent(iv, nil))
   337  
   338  	case w32.WM_RBUTTONDOWN:
   339  
   340  	}
   341  	return w32.DefWindowProc(iv.hwnd, msg, wparam, lparam)
   342  }